Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

12
engine/gui/README.txt Executable file
View File

@ -0,0 +1,12 @@
The GUI files are organized now based on broad categories:
containers Windows, layout helper controls, controls that contain
other controls.
controls Buttons, text boxes, and so forth.
core This contains base GUI code, like GuiControl and GuiCanvas.
editor Specialized editor controls.
game Game specific controls, like progress bars or HUD elements.
utility Controls with specialized, usually invisible, purposes.
The goal is to provide developers with hints about what purpose code serves,
so that they can easily work with and extend GUI controls.

View File

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/containers/guiCtrlArrayCtrl.h"
IMPLEMENT_CONOBJECT(GuiControlArrayControl);
GuiControlArrayControl::GuiControlArrayControl()
{
mCols = 0;
mRowSize = 30;
mRowSpacing = 2;
mColSpacing = 0;
}
void GuiControlArrayControl::initPersistFields()
{
Parent::initPersistFields();
addField("colCount", TypeS32, Offset(mCols, GuiControlArrayControl));
addField("colSizes", TypeS32Vector, Offset(mColumnSizes, GuiControlArrayControl));
addField("rowSize", TypeS32, Offset(mRowSize, GuiControlArrayControl));
addField("rowSpacing", TypeS32, Offset(mRowSpacing, GuiControlArrayControl));
addField("colSpacing", TypeS32, Offset(mColSpacing, GuiControlArrayControl));
}
bool GuiControlArrayControl::onWake()
{
if ( !Parent::onWake() )
return false;
return true;
}
void GuiControlArrayControl::onSleep()
{
Parent::onSleep();
}
void GuiControlArrayControl::inspectPostApply()
{
resize(getPosition(), getExtent());
}
void GuiControlArrayControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
Parent::resize(newPosition, newExtent);
if(mCols < 1 || size() < 1)
return;
S32 *sizes = new S32[mCols];
S32 *offsets = new S32[mCols];
S32 totalSize = 0;
// Calculate the column sizes
for(S32 i=0; i<mCols; i++)
{
sizes[i] = mColumnSizes[i];
offsets[i] = totalSize;
// If it's an auto-size one, then... auto-size...
if(sizes[i] == -1)
{
sizes[i] = newExtent.x - totalSize;
break;
}
totalSize += sizes[i] + mColSpacing;
}
// Now iterate through the children and resize them to fit the grid...
for(S32 i=0; i<size(); i++)
{
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](i));
// Get the current column and row...
S32 curCol = i % mCols;
S32 curRow = i / mCols;
if(gc)
{
Point2I newPos(offsets[curCol], curRow * (mRowSize + mRowSpacing));
Point2I newExtents(sizes[curCol], mRowSize);
gc->resize(newPos, newExtents);
}
}
}

View File

@ -0,0 +1,36 @@
#ifndef _GUICTRLARRAYCTRL_H_
#define _GUICTRLARRAYCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
class GuiControlArrayControl : public GuiControl
{
private:
typedef GuiControl Parent;
S32 mCols;
Vector<S32> mColumnSizes;
S32 mRowSize;
S32 mRowSpacing;
S32 mColSpacing;
public:
GuiControlArrayControl();
void resize(const Point2I &newPosition, const Point2I &newExtent);
bool onWake();
void onSleep();
void inspectPostApply();
static void initPersistFields();
DECLARE_CONOBJECT(GuiControlArrayControl);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// A gui control allowing a window to be subdivided into panes,
// each of which displays a gui control child of the
// GuiFrameSetCtrl. Each gui control child will have an associated
// FrameDetail through which frame-specific details can be
// assigned. Frame-specific values override the values specified
// for the entire frameset.
//
// Note that it is possible to have more children than frames,
// or more frames than children. In the former case, the extra
// children will not be visible (they are moved beyond the
// visible extent of the frameset). In the latter case, frames
// will be empty.
//
// If a frameset had two columns and two rows but only three
// gui control children they would be assigned to frames as
// follows:
// 1 | 2
// -----
// 3 |
//
// The last frame would be blank.
//-----------------------------------------------------------------------------
#ifndef _GUIFRAMECTRL_H_
#define _GUIFRAMECTRL_H_
#define DISABLE_COPY_CTOR(className) \
className(const className &)
#define DISABLE_ASSIGNMENT(className) \
className& operator=(const className &)
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
// for debugging porpoises...
#define GUI_FRAME_DEBUG
// ...save the porpoises
class GuiFrameSetCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
enum
{
FRAME_STATE_ON, // ON overrides OFF
FRAME_STATE_OFF, // OFF overrides AUTO
FRAME_STATE_AUTO, // AUTO == ON, unless overridden
NO_HIT = -1,
DEFAULT_BORDER_WIDTH = 4,
DEFAULT_COLUMNS = 1,
DEFAULT_ROWS = 1,
DEFAULT_MIN_FRAME_EXTENT = 20
};
enum Region
{
VERTICAL_DIVIDER,
HORIZONTAL_DIVIDER,
DIVIDER_INTERSECTION,
NONE
};
struct FrameDetail
{
U32 mBorderWidth;
ColorI mBorderColor;
S32 mBorderEnable;
S32 mBorderMovable;
Point2I mMinExtent;
FrameDetail() { mBorderWidth = DEFAULT_BORDER_WIDTH; mBorderEnable = FRAME_STATE_AUTO; mBorderMovable = FRAME_STATE_AUTO; mMinExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT); }
};
DECLARE_CONOBJECT(GuiFrameSetCtrl);
static void initPersistFields();
GuiFrameSetCtrl();
GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[] = NULL, const U32 rowOffsets[] = NULL);
virtual ~GuiFrameSetCtrl();
void addObject(SimObject *obj);
void removeObject(SimObject *obj);
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseEnter(const GuiEvent &event);
virtual void onMouseLeave(const GuiEvent &event);
bool onAdd();
void onRender(Point2I offset, const RectI &updateRect );
protected:
/* member variables */
Vector<S32> mColumnOffsets;
Vector<S32> mRowOffsets;
GuiCursor *mMoveCursor;
GuiCursor *mUpDownCursor;
GuiCursor *mLeftRightCursor;
GuiCursor *mDefaultCursor;
FrameDetail mFramesetDetails;
VectorPtr<FrameDetail *> mFrameDetails;
bool mAutoBalance;
S32 mFudgeFactor;
/* divider activation member variables */
Region mCurHitRegion;
Point2I mLocOnDivider;
S32 mCurVerticalHit;
S32 mCurHorizontalHit;
bool init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]);
bool initCursors();
Region findHitRegion(const Point2I &point);
Region pointInAnyRegion(const Point2I &point);
S32 findResizableFrames(S32 indexes[]);
bool hitVerticalDivider(S32 x, const Point2I &point);
bool hitHorizontalDivider(S32 y, const Point2I &point);
void rebalance(const Point2I &newExtent);
void computeSizes(bool balanceFrames = false);
void computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[]);
void drawDividers(const Point2I &offset);
public:
U32 columns() const { return(mColumnOffsets.size()); }
U32 rows() const { return(mRowOffsets.size()); }
U32 borderWidth() const { return(mFramesetDetails.mBorderWidth); }
Vector<S32>* columnOffsets() { return(&mColumnOffsets); }
Vector<S32>* rowOffsets() { return(&mRowOffsets); }
FrameDetail* framesetDetails() { return(&mFramesetDetails); }
bool findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd);
void frameBorderEnable(S32 index, const char *state = NULL);
void frameBorderMovable(S32 index, const char *state = NULL);
void frameMinExtent(S32 index, const Point2I &extent);
void balanceFrames() { computeSizes(true); }
void updateSizes() { computeSizes(); }
bool onWake();
private:
DISABLE_COPY_CTOR(GuiFrameSetCtrl);
DISABLE_ASSIGNMENT(GuiFrameSetCtrl);
};
//-----------------------------------------------------------------------------
// x is the first value inside the next column, so the divider x-coords
// precede x.
inline bool GuiFrameSetCtrl::hitVerticalDivider(S32 x, const Point2I &point)
{
return((point.x >= S32(x - mFramesetDetails.mBorderWidth)) && (point.x < x) && (point.y >= 0) && (point.y < S32(mBounds.extent.y)));
}
//-----------------------------------------------------------------------------
// y is the first value inside the next row, so the divider y-coords precede y.
inline bool GuiFrameSetCtrl::hitHorizontalDivider(S32 y, const Point2I &point)
{
return((point.x >= 0) && (point.x < S32(mBounds.extent.x)) && (point.y >= S32(y - mFramesetDetails.mBorderWidth)) && (point.y < y));
}
#endif // _GUI_FRAME_CTRL_H

View File

@ -0,0 +1,310 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/containers/guiPaneCtrl.h"
IMPLEMENT_CONOBJECT(GuiPaneControl);
GuiPaneControl::GuiPaneControl()
{
mMinExtent.set(10, 10);
mActive = true;
mCollapsable = true;
mCollapsed = false;
mBarBehindText = true;
mMouseOver = false;
mDepressed = false;
mOriginalExtents.set(10,10);
mCaption = StringTable->insert("A Pane");
mCaptionID = StringTable->insert("");
}
void GuiPaneControl::initPersistFields()
{
Parent::initPersistFields();
addField("caption", TypeString, Offset(mCaption, GuiPaneControl));
addField("captionID", TypeString, Offset(mCaptionID, GuiPaneControl));
addField("collapsable", TypeBool, Offset(mCollapsable, GuiPaneControl));
addField("barBehindText", TypeBool, Offset(mBarBehindText, GuiPaneControl));
}
bool GuiPaneControl::onWake()
{
if ( !Parent::onWake() )
return false;
mFont = mProfile->mFont;
AssertFatal(mFont, "GuiPaneControl::onWake: invalid font in profile" );
if(mCaptionID && *mCaptionID != 0)
{
setCaptionID(mCaptionID);
}
mProfile->constructBitmapArray();
if(mProfile->mBitmapArrayRects.size())
{
mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
if(mFont->getHeight() > mThumbSize.y)
mThumbSize.y = mFont->getHeight();
}
else
{
mThumbSize.set(20, 20);
}
return true;
}
void GuiPaneControl::onSleep()
{
Parent::onSleep();
mFont = NULL;
}
void GuiPaneControl::setCaptionID(const char *id)
{
S32 n = Con::getIntVariable(id, -1);
if(n != -1)
{
mCaptionID = StringTable->insert(id);
setCaptionID(n);
}
}
void GuiPaneControl::setCaptionID(S32 id)
{
const UTF8 *str = getGUIString(id);
if(str)
mCaption = StringTable->insert((const char*)str);
}
void GuiPaneControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
//call set update both before and after
setUpdate();
Point2I actualNewExtent = Point2I(getMax(mMinExtent.x, newExtent.x),
getMax(mMinExtent.y, newExtent.y));
mBounds.set(newPosition, actualNewExtent);
mOriginalExtents.x = actualNewExtent.x;
GuiControl *parent = getParent();
if (parent)
parent->childResized(this);
setUpdate();
// Resize the child control if we're not collapsed
if(size() && !mCollapsed)
{
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
if(gc)
{
Point2I offset(0, mThumbSize.y);
gc->resize(offset, newExtent - offset);
}
}
}
void GuiPaneControl::onRender(Point2I offset, const RectI &updateRect)
{
// Render our awesome little doogong
if(mProfile->mBitmapArrayRects.size() >= 2 && mCollapsable)
{
S32 idx = mCollapsed ? 0 : 1;
dglClearBitmapModulation();
dglDrawBitmapStretchSR(
mProfile->mTextureHandle,
RectI(offset, mProfile->mBitmapArrayRects[idx].extent),
mProfile->mBitmapArrayRects[idx]
);
}
S32 textWidth = 0;
if(!mBarBehindText)
{
dglSetBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
textWidth = dglDrawText(
mFont,
Point2I(mThumbSize.x, 0) + offset,
mCaption,
mProfile->mFontColors
);
}
// Draw our little bar, too
if(mProfile->mBitmapArrayRects.size() >= 5)
{
dglClearBitmapModulation();
S32 barStart = mThumbSize.x + offset.x + textWidth;
S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2;
Point2I barOffset(barStart, barTop);
// Draw the start of the bar...
dglDrawBitmapStretchSR(
mProfile->mTextureHandle,
RectI(barOffset, mProfile->mBitmapArrayRects[2].extent),
mProfile->mBitmapArrayRects[2]
);
// Now draw the middle...
barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x;
if(barMiddleSize>0)
{
// We have to do this inset to prevent nasty stretching artifacts
RectI foo = mProfile->mBitmapArrayRects[3];
foo.inset(1,0);
dglDrawBitmapStretchSR(
mProfile->mTextureHandle,
RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
foo
);
}
// And the end
barOffset.x += barMiddleSize;
dglDrawBitmapStretchSR(
mProfile->mTextureHandle,
RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
mProfile->mBitmapArrayRects[4]
);
}
if(mBarBehindText)
{
dglSetBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
dglDrawText(
mFont,
Point2I(mThumbSize.x, 0) + offset,
mCaption,
mProfile->mFontColors
);
}
// Draw child controls if appropriate
if(!mCollapsed)
renderChildControls(offset, updateRect);
}
ConsoleMethod(GuiPaneControl, setCollapsed, void, 3, 3, "(bool)")
{
object->setCollapsed(dAtob(argv[2]));
}
void GuiPaneControl::setCollapsed(bool isCollapsed)
{
// Get the child
if(size() == 0 || !mCollapsable) return;
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
if(mCollapsed && !isCollapsed)
{
mCollapsed = false;
resize(getPosition(), mOriginalExtents);
if(gc)
gc->setVisible(true);
}
else if(!mCollapsed && isCollapsed)
{
mCollapsed = true;
mOriginalExtents = getExtent();
resize(getPosition(), Point2I(getExtent().x, mThumbSize.y));
if(gc)
gc->setVisible(false);
}
}
void GuiPaneControl::onMouseMove(const GuiEvent &event)
{
Point2I localMove = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
mMouseOver = (localMove.y < mThumbSize.y);
if(isMouseLocked())
mDepressed = mMouseOver;
}
void GuiPaneControl::onMouseEnter(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
{
mDepressed = true;
mMouseOver = true;
}
else
{
mMouseOver = true;
}
}
void GuiPaneControl::onMouseLeave(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
mDepressed = false;
mMouseOver = false;
}
void GuiPaneControl::onMouseDown(const GuiEvent &event)
{
if(!mCollapsable)
return;
Point2I localClick = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
if(localClick.y < mThumbSize.y)
{
mouseLock();
mDepressed = true;
//update
setUpdate();
}
}
void GuiPaneControl::onMouseUp(const GuiEvent &event)
{
// Make sure we only get events we ought to be getting...
if (! mActive)
return;
if(!mCollapsable)
return;
mouseUnlock();
setUpdate();
Point2I localClick = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
if(localClick.y < mThumbSize.y && mDepressed)
setCollapsed(!mCollapsed);
}

View File

@ -0,0 +1,65 @@
#ifndef _GUIPANECTRL_H_
#define _GUIPANECTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
/// Collapsable pane control.
///
/// This class wraps a single child control and displays a header with caption
/// above it. If you click the header it will collapse or expand. The control
/// resizes itself based on its collapsed/expanded size.
///
/// In the GUI editor, if you just want the header you can make collapsable
/// false. The caption field lets you set the caption. It expects a bitmap
/// (from the GuiControlProfile) that contains two images - the first is
/// displayed when the control is expanded and the second is displayed when
/// it is collapsed. The header is sized based off of the first image.
class GuiPaneControl : public GuiControl
{
private:
typedef GuiControl Parent;
Resource<GFont> mFont;
bool mCollapsable;
bool mCollapsed;
bool mBarBehindText;
Point2I mOriginalExtents;
StringTableEntry mCaption;
StringTableEntry mCaptionID;
Point2I mThumbSize;
bool mMouseOver;
bool mDepressed;
public:
GuiPaneControl();
void resize(const Point2I &newPosition, const Point2I &newExtent);
void onRender(Point2I offset, const RectI &updateRect);
bool getCollapsed() { return mCollapsed; };
void setCollapsed(bool isCollapsed);
bool onWake();
void onSleep();
virtual void setCaptionID(S32 id);
virtual void setCaptionID(const char *id);
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
void onMouseEnter(const GuiEvent &event);
static void initPersistFields();
DECLARE_CONOBJECT(GuiPaneControl);
};
#endif

View File

@ -0,0 +1,887 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/gBitmap.h"
#include "dgl/gTexManager.h"
#include "core/resManager.h"
#include "platform/event.h"
#include "dgl/dgl.h"
#include "gui/core/guiArrayCtrl.h"
#include "gui/containers/guiScrollCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
IMPLEMENT_CONOBJECT(GuiScrollCtrl);
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
GuiScrollCtrl::GuiScrollCtrl()
{
mBounds.extent.set(200,200);
mChildMargin.set(0,0);
mBorderThickness = 1;
mScrollBarThickness = 16;
mScrollBarArrowBtnLength = 16;
stateDepressed = false;
curHitRegion = None;
mWillFirstRespond = true;
mUseConstantHeightThumb = false;
mForceVScrollBar = ScrollBarAlwaysOn;
mForceHScrollBar = ScrollBarAlwaysOn;
}
static EnumTable::Enums scrollBarEnums[] =
{
{ GuiScrollCtrl::ScrollBarAlwaysOn, "alwaysOn" },
{ GuiScrollCtrl::ScrollBarAlwaysOff, "alwaysOff" },
{ GuiScrollCtrl::ScrollBarDynamic, "dynamic" },
};
static EnumTable gScrollBarTable(3, &scrollBarEnums[0]);
ConsoleMethod(GuiScrollCtrl, scrollToTop, void, 2, 2, "() - scrolls the scroll control to the top of the child content area.")
{
argc; argv;
object->scrollTo( 0, 0 );
}
ConsoleMethod(GuiScrollCtrl, scrollToBottom, void, 2, 2, "() - scrolls the scroll control to the bottom of the child content area.")
{
argc; argv;
object->scrollTo( 0, 0x7FFFFFFF );
}
void GuiScrollCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("willFirstRespond", TypeBool, Offset(mWillFirstRespond, GuiScrollCtrl));
addField("hScrollBar", TypeEnum, Offset(mForceHScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
addField("vScrollBar", TypeEnum, Offset(mForceVScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
addField("constantThumbHeight", TypeBool, Offset(mUseConstantHeightThumb, GuiScrollCtrl));
addField("childMargin", TypePoint2I, Offset(mChildMargin, GuiScrollCtrl));
}
void GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt)
{
Parent::resize(newPos, newExt);
computeSizes();
}
void GuiScrollCtrl::childResized(GuiControl *child)
{
Parent::childResized(child);
computeSizes();
}
bool GuiScrollCtrl::onWake()
{
if (! Parent::onWake())
return false;
mTextureHandle = mProfile->mTextureHandle;
mTextureHandle.setFilterNearest();
bool result = mProfile->constructBitmapArray() >= BmpStates * BmpCount;
AssertFatal(result, "Failed to create the bitmap array");
mBitmapBounds = mProfile->mBitmapArrayRects.address();
//init
mBaseThumbSize = mBitmapBounds[BmpStates * BmpVThumbTopCap].extent.y +
mBitmapBounds[BmpStates * BmpVThumbBottomCap].extent.y;
mScrollBarThickness = mBitmapBounds[BmpStates * BmpVPage].extent.x;
mScrollBarArrowBtnLength = mBitmapBounds[BmpStates * BmpUp].extent.y;
computeSizes();
return true;
}
void GuiScrollCtrl::onSleep()
{
Parent::onSleep();
mTextureHandle = NULL;
}
bool GuiScrollCtrl::calcChildExtents()
{
// right now the scroll control really only deals well with a single
// child control for its content
if (!size())
return false;
GuiControl *ctrl = (GuiControl *) front();
mChildExt = ctrl->mBounds.extent;
mChildPos = ctrl->mBounds.point;
return true;
}
RectI lastVisRect;
void GuiScrollCtrl::scrollRectVisible(RectI rect)
{
// rect is passed in virtual client space
if(rect.extent.x > mContentExt.x)
rect.extent.x = mContentExt.x;
if(rect.extent.y > mContentExt.y)
rect.extent.y = mContentExt.y;
// Determine the points bounding the requested rectangle
Point2I rectUpperLeft = rect.point;
Point2I rectLowerRight = rect.point + rect.extent;
lastVisRect = rect;
// Determine the points bounding the actual visible area...
Point2I visUpperLeft = mChildRelPos;
Point2I visLowerRight = mChildRelPos + mContentExt;
Point2I delta(0,0);
// We basically try to make sure that first the top left of the given
// rect is visible, and if it is, then that the bottom right is visible.
// Make sure the rectangle is visible along the X axis...
if(rectUpperLeft.x < visUpperLeft.x)
delta.x = rectUpperLeft.x - visUpperLeft.x;
else if(rectLowerRight.x > visLowerRight.x)
delta.x = rectLowerRight.x - visLowerRight.x;
// Make sure the rectangle is visible along the Y axis...
if(rectUpperLeft.y < visUpperLeft.y)
delta.y = rectUpperLeft.y - visUpperLeft.y;
else if(rectLowerRight.y > visLowerRight.y)
delta.y = rectLowerRight.y - visLowerRight.y;
// If we had any changes, scroll, otherwise don't.
if(delta.x || delta.y)
scrollDelta(delta.x, delta.y);
}
void GuiScrollCtrl::addObject(SimObject *object)
{
Parent::addObject(object);
computeSizes();
}
GuiControl* GuiScrollCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
{
if(pt.x < mProfile->mBorderThickness || pt.y < mProfile->mBorderThickness)
return this;
if(pt.x >= mBounds.extent.x - mProfile->mBorderThickness - (mHasVScrollBar ? mScrollBarThickness : 0) ||
pt.y >= mBounds.extent.y - mProfile->mBorderThickness - (mHasHScrollBar ? mScrollBarThickness : 0))
return this;
return Parent::findHitControl(pt, initialLayer);
}
void GuiScrollCtrl::computeSizes()
{
S32 thickness = (mProfile ? mProfile->mBorderThickness : 1);
Point2I borderExtent(thickness, thickness);
mContentPos = borderExtent + mChildMargin;
mContentExt = mBounds.extent - (mChildMargin * 2)
- (borderExtent * 2);
Point2I childLowerRight;
mHBarEnabled = false;
mVBarEnabled = false;
mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
setUpdate();
if (calcChildExtents())
{
childLowerRight = mChildPos + mChildExt;
if (mHasVScrollBar)
mContentExt.x -= mScrollBarThickness;
if (mHasHScrollBar)
mContentExt.y -= mScrollBarThickness;
if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
{
mHasHScrollBar = true;
mContentExt.y -= mScrollBarThickness;
}
if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
{
mHasVScrollBar = true;
mContentExt.x -= mScrollBarThickness;
// doh! ext.x changed, so check hscrollbar again
if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
{
mHasHScrollBar = true;
mContentExt.y -= mScrollBarThickness;
}
}
Point2I contentLowerRight = mContentPos + mContentExt;
// see if the child controls need to be repositioned (null space in control)
Point2I delta(0,0);
if (mChildPos.x > mContentPos.x)
delta.x = mContentPos.x - mChildPos.x;
else if (contentLowerRight.x > childLowerRight.x)
{
S32 diff = contentLowerRight.x - childLowerRight.x;
delta.x = getMin(mContentPos.x - mChildPos.x, diff);
}
//reposition the children if the child extent > the scroll content extent
if (mChildPos.y > mContentPos.y)
delta.y = mContentPos.y - mChildPos.y;
else if (contentLowerRight.y > childLowerRight.y)
{
S32 diff = contentLowerRight.y - childLowerRight.y;
delta.y = getMin(mContentPos.y - mChildPos.y, diff);
}
// apply the deltas to the children...
if (delta.x || delta.y)
{
SimGroup::iterator i;
for(i = begin(); i != end();i++)
{
GuiControl *ctrl = (GuiControl *) (*i);
ctrl->mBounds.point += delta;
}
mChildPos += delta;
childLowerRight += delta;
}
// enable needed scroll bars
if (mChildExt.x > mContentExt.x)
mHBarEnabled = true;
if (mChildExt.y > mContentExt.y)
mVBarEnabled = true;
mChildRelPos = mContentPos - mChildPos;
}
// build all the rectangles and such...
calcScrollRects();
calcThumbs();
}
void GuiScrollCtrl::calcScrollRects(void)
{
S32 thickness = ( mProfile ? mProfile->mBorderThickness : 1 );
if (mHasHScrollBar)
{
mLeftArrowRect.set(thickness,
mBounds.extent.y - thickness - mScrollBarThickness - 1,
mScrollBarArrowBtnLength,
mScrollBarThickness);
mRightArrowRect.set(mBounds.extent.x - thickness - (mHasVScrollBar ? mScrollBarThickness : 0) - mScrollBarArrowBtnLength,
mBounds.extent.y - thickness - mScrollBarThickness - 1,
mScrollBarArrowBtnLength,
mScrollBarThickness);
mHTrackRect.set(mLeftArrowRect.point.x + mLeftArrowRect.extent.x,
mLeftArrowRect.point.y,
mRightArrowRect.point.x - (mLeftArrowRect.point.x + mLeftArrowRect.extent.x),
mScrollBarThickness);
}
if (mHasVScrollBar)
{
mUpArrowRect.set(mBounds.extent.x - thickness - mScrollBarThickness,
thickness,
mScrollBarThickness,
mScrollBarArrowBtnLength);
mDownArrowRect.set(mBounds.extent.x - thickness - mScrollBarThickness,
mBounds.extent.y - thickness - mScrollBarArrowBtnLength - (mHasHScrollBar ? ( mScrollBarThickness + 1 ) : 0),
mScrollBarThickness,
mScrollBarArrowBtnLength);
mVTrackRect.set(mUpArrowRect.point.x,
mUpArrowRect.point.y + mUpArrowRect.extent.y,
mScrollBarThickness,
mDownArrowRect.point.y - (mUpArrowRect.point.y + mUpArrowRect.extent.y) );
}
}
void GuiScrollCtrl::calcThumbs()
{
if (mHBarEnabled)
{
U32 trackSize = mHTrackRect.len_x();
if (mUseConstantHeightThumb)
mHThumbSize = mBaseThumbSize;
else
mHThumbSize = getMax(mBaseThumbSize, S32((mContentExt.x * trackSize) / mChildExt.x));
mHThumbPos = mHTrackRect.point.x + (mChildRelPos.x * (trackSize - mHThumbSize)) / (mChildExt.x - mContentExt.x);
}
if (mVBarEnabled)
{
U32 trackSize = mVTrackRect.len_y();
if (mUseConstantHeightThumb)
mVThumbSize = mBaseThumbSize;
else
mVThumbSize = getMax(mBaseThumbSize, S32((mContentExt.y * trackSize) / mChildExt.y));
mVThumbPos = mVTrackRect.point.y + (mChildRelPos.y * (trackSize - mVThumbSize)) / (mChildExt.y - mContentExt.y);
}
}
void GuiScrollCtrl::scrollDelta(S32 deltaX, S32 deltaY)
{
scrollTo(mChildRelPos.x + deltaX, mChildRelPos.y + deltaY);
}
void GuiScrollCtrl::scrollTo(S32 x, S32 y)
{
if(!size())
return;
setUpdate();
if (x > mChildExt.x - mContentExt.x)
x = mChildExt.x - mContentExt.x;
if (x < 0)
x = 0;
if (y > mChildExt.y - mContentExt.y)
y = mChildExt.y - mContentExt.y;
if (y < 0)
y = 0;
Point2I delta(x - mChildRelPos.x, y - mChildRelPos.y);
mChildRelPos += delta;
mChildPos -= delta;
for(SimSet::iterator i = begin(); i != end();i++)
{
GuiControl *ctrl = (GuiControl *) (*i);
ctrl->mBounds.point -= delta;
}
calcThumbs();
}
GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I &pt)
{
if (mVBarEnabled && mHasVScrollBar)
{
if (mUpArrowRect.pointInRect(pt))
return UpArrow;
else if (mDownArrowRect.pointInRect(pt))
return DownArrow;
else if (mVTrackRect.pointInRect(pt))
{
if (pt.y < mVThumbPos)
return UpPage;
else if (pt.y < mVThumbPos + mVThumbSize)
return VertThumb;
else
return DownPage;
}
}
if (mHBarEnabled && mHasHScrollBar)
{
if (mLeftArrowRect.pointInRect(pt))
return LeftArrow;
else if (mRightArrowRect.pointInRect(pt))
return RightArrow;
else if (mHTrackRect.pointInRect(pt))
{
if (pt.x < mHThumbPos)
return LeftPage;
else if (pt.x < mHThumbPos + mHThumbSize)
return HorizThumb;
else
return RightPage;
}
}
return None;
}
bool GuiScrollCtrl::wantsTabListMembership()
{
return true;
}
bool GuiScrollCtrl::loseFirstResponder()
{
setUpdate();
return true;
}
bool GuiScrollCtrl::becomeFirstResponder()
{
setUpdate();
return mWillFirstRespond;
}
bool GuiScrollCtrl::onKeyDown(const GuiEvent &event)
{
if (mWillFirstRespond)
{
switch (event.keyCode)
{
case KEY_RIGHT:
scrollByRegion(RightArrow);
return true;
case KEY_LEFT:
scrollByRegion(LeftArrow);
return true;
case KEY_DOWN:
scrollByRegion(DownArrow);
return true;
case KEY_UP:
scrollByRegion(UpArrow);
return true;
case KEY_PAGE_UP:
scrollByRegion(UpPage);
return true;
case KEY_PAGE_DOWN:
scrollByRegion(DownPage);
return true;
}
}
return Parent::onKeyDown(event);
}
void GuiScrollCtrl::onMouseDown(const GuiEvent &event)
{
mouseLock();
setFirstResponder();
setUpdate();
Point2I curMousePos = globalToLocalCoord(event.mousePoint);
curHitRegion = findHitRegion(curMousePos);
stateDepressed = true;
// Set a 0.5 second delay before we start scrolling
mLastUpdated = Platform::getVirtualMilliseconds() + 500;
scrollByRegion(curHitRegion);
if (curHitRegion == VertThumb)
{
mChildRelPosAnchor = mChildRelPos;
mThumbMouseDelta = curMousePos.y - mVThumbPos;
}
else if (curHitRegion == HorizThumb)
{
mChildRelPosAnchor = mChildRelPos;
mThumbMouseDelta = curMousePos.x - mHThumbPos;
}
}
void GuiScrollCtrl::onMouseUp(const GuiEvent &)
{
mouseUnlock();
setUpdate();
curHitRegion = None;
stateDepressed = false;
}
void GuiScrollCtrl::onMouseDragged(const GuiEvent &event)
{
Point2I curMousePos = globalToLocalCoord(event.mousePoint);
setUpdate();
if ( (curHitRegion != VertThumb) && (curHitRegion != HorizThumb) )
{
Region hit = findHitRegion(curMousePos);
if (hit != curHitRegion)
stateDepressed = false;
else
stateDepressed = true;
return;
}
// ok... if the mouse is 'near' the scroll bar, scroll with it
// otherwise, snap back to the previous position.
if (curHitRegion == VertThumb)
{
if (curMousePos.x >= mVTrackRect.point.x - mScrollBarThickness &&
curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarThickness &&
curMousePos.y >= mVTrackRect.point.y - mScrollBarThickness &&
curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarThickness)
{
S32 newVThumbPos = curMousePos.y - mThumbMouseDelta;
if(newVThumbPos != mVThumbPos)
{
S32 newVPos = (newVThumbPos - mVTrackRect.point.y) *
(mChildExt.y - mContentExt.y) /
(mVTrackRect.extent.y - mVThumbSize);
scrollTo(mChildRelPosAnchor.x, newVPos);
}
}
else
scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
}
else if (curHitRegion == HorizThumb)
{
if (curMousePos.x >= mHTrackRect.point.x - mScrollBarThickness &&
curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarThickness &&
curMousePos.y >= mHTrackRect.point.y - mScrollBarThickness &&
curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarThickness)
{
S32 newHThumbPos = curMousePos.x - mThumbMouseDelta;
if(newHThumbPos != mHThumbPos)
{
S32 newHPos = (newHThumbPos - mHTrackRect.point.x) *
(mChildExt.x - mContentExt.x) /
(mHTrackRect.extent.x - mHThumbSize);
scrollTo(newHPos, mChildRelPosAnchor.y);
}
}
else
scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
}
}
bool GuiScrollCtrl::onMouseWheelUp(const GuiEvent &event)
{
if ( !mAwake || !mVisible )
return( false );
scrollByRegion((event.modifier & SI_CTRL) ? UpPage : UpArrow);
// Tell the kids that the mouse moved (relatively):
iterator itr;
for ( itr = begin(); itr != end(); itr++ )
{
GuiControl* grandKid = static_cast<GuiControl*>( *itr );
grandKid->onMouseMove( event );
}
return( true );
}
bool GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event)
{
if ( !mAwake || !mVisible )
return( false );
scrollByRegion((event.modifier & SI_CTRL) ? DownPage : DownArrow);
// Tell the kids that the mouse moved (relatively):
iterator itr;
for ( itr = begin(); itr != end(); itr++ )
{
GuiControl* grandKid = static_cast<GuiControl *>( *itr );
grandKid->onMouseMove( event );
}
return( true );
}
void GuiScrollCtrl::onPreRender()
{
Parent::onPreRender();
// Short circuit if not depressed to save cycles
if( stateDepressed != true )
return;
//default to one second, though it shouldn't be necessary
U32 timeThreshold = 1000;
// We don't want to scroll by pages at an interval the same as when we're scrolling
// using the arrow buttons, so adjust accordingly.
switch( curHitRegion )
{
case UpPage:
case DownPage:
case LeftPage:
case RightPage:
timeThreshold = 200;
break;
case UpArrow:
case DownArrow:
case LeftArrow:
case RightArrow:
timeThreshold = 20;
break;
default:
// Neither a button or a page, don't scroll (shouldn't get here)
return;
break;
};
S32 timeElapsed = Platform::getVirtualMilliseconds() - mLastUpdated;
if ( ( timeElapsed > 0 ) && ( timeElapsed > timeThreshold ) )
{
mLastUpdated = Platform::getVirtualMilliseconds();
scrollByRegion(curHitRegion);
}
}
void GuiScrollCtrl::scrollByRegion(Region reg)
{
setUpdate();
if(!size())
return;
GuiControl *content = (GuiControl *) front();
U32 rowHeight, columnWidth;
U32 pageHeight, pageWidth;
content->getScrollLineSizes(&rowHeight, &columnWidth);
if(rowHeight >= mContentExt.y)
pageHeight = 1;
else
pageHeight = mContentExt.y - rowHeight;
if(columnWidth >= mContentExt.x)
pageWidth = 1;
else
pageWidth = mContentExt.x - columnWidth;
if (mVBarEnabled)
{
switch(reg)
{
case UpPage:
scrollDelta(0, -pageHeight);
break;
case DownPage:
scrollDelta(0, pageHeight);
break;
case UpArrow:
scrollDelta(0, -rowHeight);
break;
case DownArrow:
scrollDelta(0, rowHeight);
break;
}
}
if (mHBarEnabled)
{
switch(reg)
{
case LeftPage:
scrollDelta(-pageWidth, 0);
break;
case RightPage:
scrollDelta(pageWidth, 0);
break;
case LeftArrow:
scrollDelta(-columnWidth, 0);
break;
case RightArrow:
scrollDelta(columnWidth, 0);
break;
}
}
}
void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI r(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
if (mProfile->mOpaque)
dglDrawRectFill(r, mProfile->mFillColor);
if (mProfile->mBorder)
renderFilledBorder(r, mProfile);
// draw scroll bars
if (mHasVScrollBar)
drawVScrollBar(offset);
if (mHasHScrollBar)
drawHScrollBar(offset);
//draw the scroll corner
if (mHasVScrollBar && mHasHScrollBar)
drawScrollCorner(offset);
// draw content controls
// create a rect to intersect with the updateRect
RectI contentRect(mContentPos.x + offset.x, mContentPos.y + offset.y, mContentExt.x, mContentExt.y);
if(contentRect.intersect(updateRect))
renderChildControls(offset, contentRect);
// Finally draw the last vis rect (debug aid, BJG)
//RectI renderRect = lastVisRect;
//renderRect.point += mContentPos + offset;
//dglSetClipRect(r);
//dglDrawRect(renderRect, ColorI(0, 255, 0));
}
void GuiScrollCtrl::drawBorder( const Point2I &offset, bool /*isFirstResponder*/ )
{
}
void GuiScrollCtrl::drawVScrollBar(const Point2I &offset)
{
Point2I pos = offset + mUpArrowRect.point;
S32 bitmap = (mVBarEnabled ? ((curHitRegion == UpArrow && stateDepressed) ?
BmpStates * BmpUp + BmpHilite : BmpStates * BmpUp) : BmpStates * BmpUp + BmpDisabled);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
pos.y += mScrollBarArrowBtnLength;
S32 end;
if (mVBarEnabled)
end = mVThumbPos + offset.y;
else
end = mDownArrowRect.point.y + offset.y;
bitmap = (mVBarEnabled ? ((curHitRegion == DownPage && stateDepressed) ?
BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage) : BmpStates * BmpVPage + BmpDisabled);
if (end > pos.y)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
}
pos.y = end;
if (mVBarEnabled)
{
bool thumbSelected = (curHitRegion == VertThumb && stateDepressed);
S32 ttop = (thumbSelected ? BmpStates * BmpVThumbTopCap + BmpHilite : BmpStates * BmpVThumbTopCap);
S32 tmid = (thumbSelected ? BmpStates * BmpVThumb + BmpHilite : BmpStates * BmpVThumb);
S32 tbot = (thumbSelected ? BmpStates * BmpVThumbBottomCap + BmpHilite : BmpStates * BmpVThumbBottomCap);
// draw the thumb
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
pos.y += mBitmapBounds[ttop].extent.y;
end = mVThumbPos + mVThumbSize - mBitmapBounds[tbot].extent.y + offset.y;
if (end > pos.y)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[tmid].extent.x, end - pos.y)), mBitmapBounds[tmid]);
}
pos.y = end;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
pos.y += mBitmapBounds[tbot].extent.y;
end = mVTrackRect.point.y + mVTrackRect.extent.y - 1 + offset.y;
bitmap = (curHitRegion == DownPage && stateDepressed) ? BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage;
if (end > pos.y)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
}
pos.y = end;
}
bitmap = (mVBarEnabled ? ((curHitRegion == DownArrow && stateDepressed ) ?
BmpStates * BmpDown + BmpHilite : BmpStates * BmpDown) : BmpStates * BmpDown + BmpDisabled);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
}
void GuiScrollCtrl::drawHScrollBar(const Point2I &offset)
{
S32 bitmap;
//draw the left arrow
bitmap = (mHBarEnabled ? ((curHitRegion == LeftArrow && stateDepressed) ?
BmpStates * BmpLeft + BmpHilite : BmpStates * BmpLeft) : BmpStates * BmpLeft + BmpDisabled);
Point2I pos = offset;
pos += mLeftArrowRect.point;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
pos.x += mLeftArrowRect.extent.x;
//draw the left page
S32 end;
if (mHBarEnabled)
end = mHThumbPos + offset.x;
else
end = mRightArrowRect.point.x + offset.x;
bitmap = (mHBarEnabled ? ((curHitRegion == LeftPage && stateDepressed) ?
BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage) : BmpStates * BmpHPage + BmpDisabled);
if (end > pos.x)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
}
pos.x = end;
//draw the thumb and the rightPage
if (mHBarEnabled)
{
bool thumbSelected = (curHitRegion == HorizThumb && stateDepressed);
S32 ttop = (thumbSelected ? BmpStates * BmpHThumbLeftCap + BmpHilite : BmpStates * BmpHThumbLeftCap );
S32 tmid = (thumbSelected ? BmpStates * BmpHThumb + BmpHilite : BmpStates * BmpHThumb);
S32 tbot = (thumbSelected ? BmpStates * BmpHThumbRightCap + BmpHilite : BmpStates * BmpHThumbRightCap);
// draw the thumb
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
pos.x += mBitmapBounds[ttop].extent.x;
end = mHThumbPos + mHThumbSize - mBitmapBounds[tbot].extent.x + offset.x;
if (end > pos.x)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[tmid].extent.y)), mBitmapBounds[tmid]);
}
pos.x = end;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
pos.x += mBitmapBounds[tbot].extent.x;
end = mHTrackRect.point.x + mHTrackRect.extent.x - 1 + offset.x;
bitmap = ((curHitRegion == RightPage && stateDepressed) ? BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage);
if (end > pos.x)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
}
pos.x = end;
}
bitmap = (mHBarEnabled ? ((curHitRegion == RightArrow && stateDepressed) ?
BmpStates * BmpRight + BmpHilite : BmpStates * BmpRight) : BmpStates * BmpRight + BmpDisabled);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
}
void GuiScrollCtrl::drawScrollCorner(const Point2I &offset)
{
Point2I pos = offset;
pos.x += mRightArrowRect.point.x + mRightArrowRect.extent.x;
pos.y += mRightArrowRect.point.y;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[BmpStates * BmpResize]);
}
void GuiScrollCtrl::autoScroll(Region reg)
{
scrollByRegion(reg);
}

View File

@ -0,0 +1,201 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUISCROLLCTRL_H_
#define _GUISCROLLCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiScrollCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
// the scroll control uses a bitmap array to draw all its
// graphics... these are the bitmaps it needs:
enum BitmapIndices
{
BmpUp,
BmpDown,
BmpVThumbTopCap,
BmpVThumb,
BmpVThumbBottomCap,
BmpVPage,
BmpLeft,
BmpRight,
BmpHThumbLeftCap,
BmpHThumb,
BmpHThumbRightCap,
BmpHPage,
BmpResize,
BmpCount
};
enum BitmapStates
{
BmpDefault = 0,
BmpHilite,
BmpDisabled,
BmpStates
};
RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
TextureHandle mTextureHandle;
S32 mBorderThickness; // this gets set per class in the constructor
Point2I mChildMargin; // the thickeness of the margin around the child controls
// note - it is implicit in the scroll view that the buttons all have the same
// arrow length and that horizontal and vertical scroll bars have the
// same thickness
S32 mScrollBarThickness; // determined by the width of the vertical page bmp
S32 mScrollBarArrowBtnLength; // determined by the height of the up arrow
bool mHBarEnabled;
bool mVBarEnabled;
bool mHasHScrollBar;
bool mHasVScrollBar;
Point2I mContentPos; // the position of the content region in the control's coord system
Point2I mContentExt; // the extent of the content region
Point2I mChildPos; // the position of the upper left corner of the child control(s)
Point2I mChildExt;
Point2I mChildRelPos; // the relative position of the upper left content corner in
// the child's coordinate system - 0,0 if scrolled all the way to upper left.
//--------------------------------------
// for mouse dragging the thumb
Point2I mChildRelPosAnchor; // the original childRelPos when scrolling started
S32 mThumbMouseDelta;
S32 mLastUpdated;
S32 mHThumbSize;
S32 mHThumbPos;
S32 mVThumbSize;
S32 mVThumbPos;
S32 mBaseThumbSize;
RectI mUpArrowRect;
RectI mDownArrowRect;
RectI mLeftArrowRect;
RectI mRightArrowRect;
RectI mHTrackRect;
RectI mVTrackRect;
//--------------------------------------
// for determing hit area
public: //called by the ComboPopUp class
enum Region
{
UpArrow,
DownArrow,
LeftArrow,
RightArrow,
UpPage,
DownPage,
LeftPage,
RightPage,
VertThumb,
HorizThumb,
None
};
enum {
ScrollBarAlwaysOn = 0,
ScrollBarAlwaysOff = 1,
ScrollBarDynamic = 2
};
bool stateDepressed;
Region curHitRegion;
bool disabled;
S32 mForceHScrollBar;
S32 mForceVScrollBar;
bool mUseConstantHeightThumb;
bool mWillFirstRespond; // for automatically handling arrow keys
Region findHitRegion(const Point2I &);
protected:
virtual bool calcChildExtents();
virtual void calcScrollRects(void);
void calcThumbs();
void scrollByRegion(Region reg);
//--------------------------------------
//--------------------------------------
public:
GuiScrollCtrl();
DECLARE_CONOBJECT(GuiScrollCtrl);
static void initPersistFields();
void autoScroll(Region reg);
void scrollTo(S32 x, S32 y);
void scrollDelta(S32 x, S32 y);
void scrollRectVisible(RectI rect);
void computeSizes();
// you can change the bitmap array dynamically.
void loadBitmapArray();
void addObject(SimObject *obj);
void resize(const Point2I &newPosition, const Point2I &newExtent);
void childResized(GuiControl *child);
Point2I getChildPos() { return mChildPos; }
Point2I getChildExtent() { return mChildExt; }
Point2I getContentExtent() { return mContentExt; }
S32 getBorderThickness(void) { return mBorderThickness; }
S32 scrollBarThickness() const { return(mScrollBarThickness); }
S32 scrollBarArrowBtnLength() const { return(mScrollBarArrowBtnLength); }
bool hasHScrollBar() const { return(mHasHScrollBar); }
bool hasVScrollBar() const { return(mHasVScrollBar); }
bool enabledHScrollBar() const { return(mHBarEnabled); }
bool enabledVScrollBar() const { return(mVBarEnabled); }
bool isScrolledToBottom() { return mChildPos.y + mChildExt.y <= mContentPos.y + mContentExt.y; }
bool wantsTabListMembership();
bool becomeFirstResponder();
bool loseFirstResponder();
Region getCurHitRegion(void) { return curHitRegion; }
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseRepeat(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
bool onMouseWheelUp(const GuiEvent &event);
bool onMouseWheelDown(const GuiEvent &event);
bool onWake();
void onSleep();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
virtual void drawBorder(const Point2I &offset, bool isFirstResponder);
virtual void drawVScrollBar(const Point2I &offset);
virtual void drawHScrollBar(const Point2I &offset);
virtual void drawScrollCorner(const Point2I &offset);
virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
};
#endif //_GUI_SCROLL_CTRL_H

View File

@ -0,0 +1,165 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/containers/guiStackCtrl.h"
IMPLEMENT_CONOBJECT(GuiStackControl);
GuiStackControl::GuiStackControl()
{
mMinExtent.set(60, 60);
mResizing = false;
mStackFromBottom = false;
mPadding = 0;
}
void GuiStackControl::initPersistFields()
{
Parent::initPersistFields();
addField("stackFromBottom", TypeBool, Offset(mStackFromBottom, GuiStackControl));
addField("padding", TypeF32, Offset(mPadding, GuiStackControl));
}
bool GuiStackControl::onWake()
{
if ( !Parent::onWake() )
return false;
updatePanes();
return true;
}
void GuiStackControl::onSleep()
{
Parent::onSleep();
}
void GuiStackControl::updatePanes()
{
// Prevent recursion
if(mResizing) return;
mResizing = true;
// Store the sum of the heights of our controls.
S32 totalHeight=0;
Point2I curPos;
if(mStackFromBottom)
{
// If we go from the bottom, things are basically the same...
// except we have to assign positions in an arse backwards way
// (literally :P)
// Figure out how high everything is going to be...
// Place each child...
for(S32 i=0; i<size(); i++)
{
// Place control
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](i));
if(gc)
{
Point2I childExtent = gc->getExtent();
totalHeight += childExtent.y;
}
}
// Figure out where we're starting...
curPos = getPosition();
curPos.y += totalHeight;
// Offset so the bottom control goes in the right place...
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](size()-1));
if(gc)
curPos.y -= gc->getExtent().y;
// Now work up from there!
for(S32 i=size()-1; i>=0; i--)
{
// Place control
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](i));
if(gc)
{
// We must place the child...
// Make it have our width but keep its height
Point2I childExtent = gc->getExtent();
// Update our state...
curPos.y -= childExtent.y - mPadding;
// And resize...
gc->resize(curPos - getPosition(), Point2I(getExtent().x, childExtent.y));
}
}
}
else
{
// Position and resize everything...
curPos = getPosition();
// Place each child...
for(S32 i=0; i<size(); i++)
{
// Place control
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](i));
if(gc)
{
// We must place the child...
// Make it have our width but keep its height
Point2I childExtent = gc->getExtent();
gc->resize(curPos - getPosition(), Point2I(getExtent().x, childExtent.y));
// Update our state...
curPos.y += childExtent.y + mPadding;
totalHeight += childExtent.y + mPadding;
}
}
// Conform our size to the sum of the child sizes.
curPos.x = getExtent().x;
curPos.y = totalHeight;
resize(getPosition(), curPos);
}
mResizing = false;
}
void GuiStackControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
//call set update both before and after
setUpdate();
Point2I actualNewExtent = Point2I( getMax(mMinExtent.x, newExtent.x),
getMax(mMinExtent.y, newExtent.y));
mBounds.set(newPosition, actualNewExtent);
GuiControl *parent = getParent();
if (parent)
parent->childResized(this);
setUpdate();
updatePanes();
}
void GuiStackControl::addObject(SimObject *obj)
{
Parent::addObject(obj);
updatePanes();
}
void GuiStackControl::childResized(GuiControl *child)
{
updatePanes();
}

View File

@ -0,0 +1,49 @@
#ifndef _GUISTACKCTRL_H_
#define _GUISTACKCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
/// A vertical stack of GUI controls.
///
/// This maintains a vertical stack of GUI controls. If one is deleted, or
/// resized, then the stack is resized to fit. The order of the stack is
/// determined by the internal order of the children (ie, order of addition).
///
/// Children set their own height but are constrained to the width of the
/// stack control.
///
/// @todo Make this support horizontal stacks.
class GuiStackControl : public GuiControl
{
private:
typedef GuiControl Parent;
bool mResizing;
bool mStackFromBottom;
F32 mPadding;
public:
GuiStackControl();
void resize(const Point2I &newPosition, const Point2I &newExtent);
void childResized(GuiControl *child);
bool onWake();
void onSleep();
void updatePanes();
S32 getCount() { return size(); }; /// Returns the number of children in the stack
void addObject(SimObject *obj);
static void initPersistFields();
DECLARE_CONOBJECT(GuiStackControl);
};
#endif

View File

@ -0,0 +1,638 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "console/simBase.h"
#include "gui/core/guiCanvas.h"
#include "gui/containers/guiTabBookCtrl.h"
#include "platform/event.h"
#include "core/fileStream.h"
#include "gui/containers/guiScrollCtrl.h"
#include "gui/editor/guiEditCtrl.h"
#include "gui/controls/guiPopUpCtrl.h"
// So we can set tab alignment via gui editor
static EnumTable::Enums tabAlignEnums[] =
{
{ GuiTabBookCtrl::AlignTop, "Top" },
// { GuiTabBookCtrl::AlignLeft, "Left" },
{ GuiTabBookCtrl::AlignBottom,"Bottom" },
// { GuiTabBookCtrl::AlignRight, "Right" }
};
static EnumTable gTabAlignEnums(/*4*/2,&tabAlignEnums[0]);
IMPLEMENT_CONOBJECT(GuiTabBookCtrl);
GuiTabBookCtrl::GuiTabBookCtrl()
{
VECTOR_SET_ASSOCIATION(mPages);
mTabHeight = 24;
mLastTabHeight = mTabHeight;
mTabWidth = 64;
mLastTabWidth = mTabWidth;
mTabPosition = GuiTabBookCtrl::AlignTop;
mLastTabPosition = mTabPosition;
mActivePage = NULL;
mHoverTab = NULL;
mHasTexture = false;
mBitmapBounds = NULL;
mBounds.extent.set( 400, 300 );
mPageRect = RectI(0,0,0,0);
mTabRect = RectI(0,0,0,0);
}
void GuiTabBookCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("TabPosition", TypeEnum, Offset(mTabPosition,GuiTabBookCtrl), 1, &gTabAlignEnums );
addField("TabHeight", TypeS32, Offset(mTabHeight,GuiTabBookCtrl) );
addField("TabWidth", TypeS32, Offset(mTabWidth,GuiTabBookCtrl));
}
// Empty for now, will implement for handling design time context menu for manipulating pages
ConsoleMethod( GuiTabBookCtrl, addPage, void, 2, 2, "(no arguments expected)")
{
object->addNewPage();
}
//ConsoleMethod( GuiTabBookCtrl, removePage, void, 2, 2, "()")
//{
//}
bool GuiTabBookCtrl::onAdd()
{
Parent::onAdd();
mTabRect = getTabBaseRect( Point2I(0,0) );
mPageRect = getPageRect( mTabRect, Point2I(0,0) );
return true;
}
void GuiTabBookCtrl::onRemove()
{
Parent::onRemove();
}
void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
{
Vector<GuiTabPageCtrl*>::iterator i = mPages.begin();
for( ; i != mPages.end(); i++)
{
GuiTabPageCtrl* tab = (*i);
if( tab == child )
{
if( tab == mActivePage )
mActivePage = NULL;
mPages.erase( i );
break;
}
}
if( mPages.empty() )
mActivePage = NULL;
else if (mActivePage == NULL )
mActivePage = (*mPages.begin());
}
void GuiTabBookCtrl::onChildAdded( GuiControl *child )
{
GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child);
if( !page )
{
Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page");
SimObject *simObj = reinterpret_cast<SimObject*>(child);
removeObject( simObj );
if( mActivePage )
{
mActivePage->addObject( simObj );
}
else
{
Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent");
GuiControl *rent = getParent();
if( rent )
rent->addObject( simObj );
}
return;
}
child->resize( mPageRect.point, mPageRect.extent );
mPages.push_back( page );
selectPage( page );
}
bool GuiTabBookCtrl::onWake()
{
if (! Parent::onWake())
return false;
mHasTexture = mProfile->constructBitmapArray();
if( mHasTexture )
mBitmapBounds = mProfile->mBitmapArrayRects.address();
return true;
}
void GuiTabBookCtrl::onSleep()
{
Parent::onSleep();
}
void GuiTabBookCtrl::addNewPage()
{
char textbuf[1024];
GuiTabPageCtrl * page = new GuiTabPageCtrl();
page->setField("profile", "GuiTabPageProfile");
dSprintf(textbuf, sizeof(textbuf), "TabBookPage%d_%d", getId(), page->getId());
page->registerObject(textbuf);
this->addObject( page );
}
void GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
Parent::resize( newPosition, newExtent );
mTabRect = getTabBaseRect( Point2I(0,0) );
mPageRect = getPageRect( mTabRect, Point2I(0,0) );
RectI pageRect = getPageRectChild();
// Resize Children
SimSet::iterator i;
for(i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
ctrl->mBounds = pageRect;
}
}
void GuiTabBookCtrl::childResized(GuiControl *child)
{
Parent::childResized( child );
child->mBounds = getPageRectChild();
}
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
{
return findHitTab( event.mousePoint );
}
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
{
S32 hitTab = 0; // Which tab did we hit?
hitPoint = globalToLocalCoord( hitPoint );
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
hitTab = (S32)mFloor( (F32)hitPoint.x / (F32)mTabWidth );
break;
case AlignLeft:
case AlignRight:
hitTab = (S32)mFloor( (F32)hitPoint.y / (F32)mTabWidth );
break;
};
if( mPages.size() > hitTab && hitTab >= 0 )
return mPages[hitTab];
return NULL;
}
RectI GuiTabBookCtrl::getHitRect( Point2I offset )
{
// We can get our rect without a specified offset, but if we don't specify one
// when rendering with GUI editor open, our offset is wrong because the editor
// offsets the canvas to accomodate creation of it's own controls
RectI Result = RectI( localToGlobalCoord( Point2I(0,0) ), mBounds.extent );
if( ! offset.isZero() )
Result.point -= offset;
switch( mTabPosition )
{
case AlignTop:
Result.extent.y = mTabHeight;
break;
case AlignBottom:
Result.point.y = (Result.point.y + Result.extent.y) - mTabHeight;
Result.extent.y = mTabHeight;
break;
case AlignLeft:
Result.extent.x = mTabHeight;
break;
case AlignRight:
Result.point.x = (Result.point.x + Result.extent.x) - mTabHeight;
Result.extent.x = mTabHeight;
break;
}
return Result;
}
RectI GuiTabBookCtrl::getTabBaseRect( Point2I offset = Point2I(0,0) )
{
// We can get our rect without a specified offset, but if we don't specify one
// when rendering with GUI editor open, our offset is wrong because the editor
// offsets the canvas to accomodate creation of it's own controls
RectI Result = RectI( offset.isZero() ? mBounds.point : offset, mBounds.extent );
switch( mTabPosition )
{
case AlignTop:
Result.extent.y = mTabHeight;
break;
case AlignBottom:
Result.point.y = (Result.point.y + Result.extent.y) - mTabHeight;
Result.extent.y = mTabHeight;
break;
case AlignLeft:
Result.extent.x = mTabHeight;
break;
case AlignRight:
Result.point.x = (Result.point.x + Result.extent.x) - mTabHeight;
Result.extent.x = mTabHeight;
break;
}
return Result;
}
RectI GuiTabBookCtrl::getPageRect( const RectI &tabBaseRect, Point2I offset )
{
RectI Result;
switch( mTabPosition )
{
case AlignTop:
Result.point.x = tabBaseRect.point.x;
Result.extent.x = tabBaseRect.extent.x;
Result.point.y = tabBaseRect.point.y + tabBaseRect.extent.y;
Result.extent.y = mBounds.extent.y - mTabHeight;
break;
case AlignBottom:
Result.point.x = tabBaseRect.point.x;
Result.extent.x = tabBaseRect.extent.x;
Result.point.y = offset.y;
Result.extent.y = mBounds.extent.y - mTabHeight;
break;
case AlignLeft:
Result.point.x = offset.x + mTabHeight;
Result.extent.x = mBounds.extent.x - mTabHeight;
Result.point.y = offset.y;
Result.extent.y = mBounds.extent.y;
break;
case AlignRight:
Result.point.x = offset.x;
Result.extent.x = mBounds.extent.x - mTabHeight;
Result.point.y = offset.y;
Result.extent.y = mBounds.extent.y;
break;
}
return Result;
}
RectI GuiTabBookCtrl::getPageRectChild()
{
RectI Result;
switch( mTabPosition )
{
case AlignTop:
Result.point.x = 0;
Result.extent.x = mBounds.extent.x;
Result.point.y = mTabHeight;
Result.extent.y = mBounds.extent.y - mTabHeight;
break;
case AlignBottom:
Result.point.x = 0;
Result.extent.x = mBounds.extent.x;
Result.point.y = 0;
Result.extent.y = mBounds.extent.y - mTabHeight;
break;
case AlignLeft:
Result.point.x = mTabHeight;
Result.extent.x = mBounds.extent.x - mTabHeight;
Result.point.y = 0;
Result.extent.y = mBounds.extent.y;
break;
case AlignRight:
Result.point.x = 0;
Result.extent.x = mBounds.extent.x - mTabHeight;
Result.point.y = 0;
Result.extent.y = mBounds.extent.y;
break;
}
return Result;
}
void GuiTabBookCtrl::onPreRender()
{
// sometimes we need to resize because of a changed persistent field
// that's what this does
solveDirty();
}
void GuiTabBookCtrl::solveDirty()
{
bool dirty = false;
if( mTabPosition != mLastTabPosition )
{
mLastTabPosition = mTabPosition;
dirty = true;
}
if( mTabHeight != mLastTabHeight )
{
mLastTabHeight = mTabHeight;
dirty = true;
}
if( mTabWidth != mLastTabWidth )
{
mLastTabWidth = mTabWidth;
dirty = true;
}
if( dirty )
resize( mBounds.point, mBounds.extent );
}
void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI tabRect = getTabBaseRect( offset );
RectI pageRect = getPageRect( tabRect, offset );
// We're so nice we'll store the old modulation before we clear it for our rendering! :)
ColorI oldModulation;
dglGetBitmapModulation( &oldModulation );
// Wipe it out
dglClearBitmapModulation();
// Render our tabs
renderTabs( tabRect );
// This is ideal, we know what our active tab is, so we only render it
if( mActivePage != NULL )
{
mActivePage->onRender( pageRect.point, pageRect );
}
else
{
// Render only the active page
if( ! mPages.empty() )
{
GuiTabPageCtrl* tab = reinterpret_cast<GuiTabPageCtrl*>(*mPages.begin());
tab->onRender( pageRect.point, pageRect );
mActivePage = tab;
}
else
{
// if we have no active page, render a filled rect only
dglDrawRectFill( pageRect, mProfile->mFillColor );
}
}
// Restore old modulation
dglSetBitmapModulation( oldModulation );
}
void GuiTabBookCtrl::renderTabs( const RectI &bounds )
{
RectI oldClip = dglGetClipRect();
dglSetClipRect( bounds );
S32 fitTabs = 0; // How many tabs can we fit in the book?
S32 extentY,extentX;
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
fitTabs = (S32)mCeil( (F32)bounds.extent.x / (F32)mTabWidth );
extentX = mTabWidth;
extentY = mTabHeight;
break;
case AlignLeft:
case AlignRight:
fitTabs = (S32)mCeil( (F32)bounds.extent.y / (F32)mTabWidth );
extentY = mTabWidth;
extentX = mTabHeight;
break;
};
Vector<GuiTabPageCtrl*>::iterator i = mPages.begin();
for( S32 j=0 ; ( i != mPages.end() && j < fitTabs ) ; i++, j++)
{
RectI tabBounds = RectI( bounds.point.x, bounds.point.y, extentX, extentY );
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
tabBounds.point.x = bounds.point.x + ( j * mTabWidth );
break;
case AlignLeft:
case AlignRight:
tabBounds.point.y = bounds.point.y + ( j * mTabWidth );
break;
};
GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>(*i);
renderTab( tabBounds, tab );
}
dglSetClipRect( oldClip );
}
void GuiTabBookCtrl::renderTab( RectI tabRect, GuiTabPageCtrl *tab )
{
StringTableEntry text = tab->getText();
ColorI oldColor;
dglGetBitmapModulation( &oldColor );
// Is this a skinned control?
if( mHasTexture && mProfile->mBitmapArrayRects.size() == NumBitmaps)
{
S32 tabTextureIndex = 0;
RectI stretchRect;
if ( mActivePage == tab )
stretchRect = mBitmapBounds[ TabSelected ];
else if( mHoverTab == tab )
stretchRect = mBitmapBounds[ TabHover ];
else
stretchRect = mBitmapBounds[ TabNormal ];
dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, (mTabPosition == AlignBottom) ? /* Render texture upside down */ GFlip_Y : 0 );
}
else
{
// If this isn't a skinned control or the bitmap is simply missing, handle it WELL
if ( mActivePage == tab )
dglDrawRectFill(tabRect, mProfile->mFillColor);
else if( mHoverTab == tab )
dglDrawRectFill(tabRect, mProfile->mFillColorHL);
else
dglDrawRectFill(tabRect, mProfile->mFillColorNA);
}
dglSetBitmapModulation(mProfile->mFontColor);
renderJustifiedText( tabRect.point, tabRect.extent, text );
dglSetBitmapModulation( oldColor);
}
void GuiTabBookCtrl::selectPage( S32 index )
{
if( mPages.size() < index )
return;
// Select the page
selectPage( mPages[ index ] );
}
void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
{
Vector<GuiTabPageCtrl*>::iterator i = mPages.begin();
for( ; i != mPages.end() ; i++ )
{
GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>(*i);
if( page == tab )
{
mActivePage = tab;
tab->setVisible( true );
// Notify User
char *retBuffer = Con::getReturnBuffer( 512 );
dStrcpy( retBuffer, tab->getText() );
Con::executef( this, 2, "onTabSelected", retBuffer );
}
else
tab->setVisible( false );
}
}
void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
{
RectI TabBase = getHitRect();
if( TabBase.pointInRect( event.mousePoint ) )
{
GuiTabPageCtrl *tab = findHitTab( event.mousePoint );
if( tab != NULL )
selectPage( tab );
}
}
void GuiTabBookCtrl::onRightMouseDown(const GuiEvent &event)
{
if (! mActive)
{
Parent::onRightMouseDown(event);
return;
}
setFirstResponder();
//search for the control hit in any layer below the edit layer
GuiControl *hitCtrl = findHitControl(globalToLocalCoord(event.mousePoint), mLayer - 1);
if (hitCtrl != this)
{
Con::executef(this, 1, "onClearSelected");
}
}
void GuiTabBookCtrl::onMouseMove(const GuiEvent &event)
{
RectI TabBase = getHitRect();
if( TabBase.pointInRect( event.mousePoint ) )
{
GuiTabPageCtrl *tab = findHitTab( event.mousePoint );
if( tab != NULL && mHoverTab != tab )
mHoverTab = tab;
else if ( !tab )
mHoverTab = NULL;
}
Parent::onMouseMove( event );
}
void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event )
{
mHoverTab = NULL;
}
void GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
{
RectI TabBase = getHitRect( offset );
if( TabBase.pointInRect( event.mousePoint ) )
{
GuiTabPageCtrl *tab = findHitTab( event.mousePoint + offset );
if( tab != NULL )
selectPage( tab );
}
// This shouldn't be called if it's not design time, but check just incase
if ( GuiControl::smDesignTime )
{
// If we clicked in the editor and our addset is the tab book
// ctrl, select the child ctrl so we can edit it's properties
GuiEditCtrl* edit = GuiControl::smEditorHandle;
if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL )
edit->select( mActivePage );
}
}
void GuiTabBookCtrl::onRightMouseDownEditor(const GuiEvent &event, Point2I offset)
{
//////GuiPopUpTextListCtrl
//GuiPopUpMenuCtrl * menu = new GuiPopUpMenuCtrl();
//menu->setField("profile", "GuiPopUpMenuProfile");
//menu->setField("text", "Duh");
////now add the entries
//menu->addEntry("Test",1);
//menu->onAction();
//menu->registerObject();
}

View File

@ -0,0 +1,198 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUI_TABBOOKCTRL_H_
#define _GUI_TABBOOKCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUITABPAGECTRL_H_
#include "gui/controls/guiTabPageCtrl.h"
#endif
/// Tab Book Control for creation of tabbed dialogs
///
/// @see GUI for an overview of the Torque GUI system.
///
/// @section GuiTabBookCtrl_Intro Introduction
///
/// GuiTabBookCtrl is a container class that holds children of type GuiTabPageCtrl
///
/// GuiTabBookCtrl creates an easy to work with system for creating tabbed dialogs
/// allowing for creation of dialogs that store alot of information in a small area
/// by seperating the information into pages which are changeable by clicking their
/// page title on top or bottom of the control
///
/// tabs may be aligned to be on top or bottom of the book and are changeable while
/// the GUI editor is open for quick switching between pages allowing multipage dialogs
/// to be edited quickly and easily.
///
/// The control may only contain children of type GuiTabPageCtrl.
/// If a control is added to the Book that is not of type GuiTabPageCtrl, it will be
/// removed and relocated to the currently active page of the control.
/// If there is no active page in the book, the child control will be relocated to the
/// parent of the book.
///
/// @ref GUI has an overview of the GUI system.
///
/// @nosubgrouping
class GuiTabBookCtrl : public GuiControl
{
public:
enum TabPosition
{
AlignTop, ///< Align the tabs on the top of the tab book control
AlignBottom,///< Align the tabs on the bottom of the tab book control
AlignLeft, ///< Unsupported currently
AlignRight ///< Unsupported currently
};
private:
typedef GuiControl Parent; ///< typedef for parent class access
RectI mPageRect; ///< Rectangle of the tab page portion of the control
RectI mTabRect; ///< Rectangle of the tab portion of the control
Vector<GuiTabPageCtrl*> mPages; ///< Vector of pages contained by the control
GuiTabPageCtrl* mActivePage; ///< Pointer to the active (selected) tab page child control
GuiTabPageCtrl* mHoverTab; ///< Pointer to the tab page that currently has the mouse positioned ontop of its tab
TabPosition mTabPosition; ///< Current tab position (see alignment)
TabPosition mLastTabPosition; ///< Last known tab position, stored to compare to tabPosition to know when to resize children
S32 mTabHeight; ///< Current tab height
S32 mLastTabHeight; ///< Last known tab height, stored to compare to current tabHeight to know when to resize children
S32 mTabWidth; ///< Current tab width
S32 mLastTabWidth; ///< Last know tab width, stored to compare to current tabWidth to know when to resize children
enum
{
TabSelected = 0, ///< Index of selected tab texture
TabNormal, ///< Index of normal tab texture
TabHover, ///< Index of hover tab texture
NumBitmaps ///< Number of bitmaps in this array
};
bool mHasTexture; ///< Indicates whether we have a texture to render the tabs with
RectI *mBitmapBounds;///< Array of rectangles identifying textures for tab book
public:
GuiTabBookCtrl();
DECLARE_CONOBJECT(GuiTabBookCtrl);
static void initPersistFields();
/// @name Control Events
/// @{
bool onAdd();
void onRemove();
bool onWake();
void onSleep();
void onPreRender();
void onRender( Point2I offset, const RectI &updateRect );
/// @}
/// @name Child events
/// @{
void onChildRemoved( GuiControl* child );
void onChildAdded( GuiControl *child );
/// @}
/// @name Rendering methods
/// @{
/// Tab rendering routine, iterates through all tabs rendering one at a time
/// @param bounds The rectanlge to render the tabs in
void renderTabs( const RectI &bounds );
/// Tab rendering subroutine, renders one tab with specified options
/// @param tabRect the rectangle to render the tab into
/// @param tab pointer to the tab page control for which to render the tab
void renderTab( RectI tabRect, GuiTabPageCtrl* tab );
/// @}
/// @name Page Management
/// @{
/// Create a new tab page child in the book
///
/// Pages created are not titled and appear with no text on their tab when created.
/// This may change in the future.
void addNewPage();
/// Select a tab page based on an index
/// @param index The index in the list that specifies which page to select
void selectPage( S32 index );
/// Select a tab page by a pointer to that page
/// @param page A pointer to the GuiTabPageCtrl to select. This page must be a child of the tab book.
void selectPage( GuiTabPageCtrl *page );
/// @}
/// @name Internal Utility Functions
/// @{
/// Checks to see if a tab option has changed and we need to reize children, resizes if necesary
void solveDirty();
RectI getHitRect( Point2I offset = Point2I(0,0) );
/// Get the rectangle of the tab base
RectI getTabBaseRect( Point2I offset );
/// Get the rectangle of the page portion of the control
RectI getPageRect( const RectI &tabBaseRect, Point2I offset );
/// Get a page rect for a child control, with the offset point starting at (0,0)
RectI getPageRectChild();
/// Find the tab that was hit by the current event, if any
/// @param event The GuiEvent that caused this function call
GuiTabPageCtrl *findHitTab( const GuiEvent &event );
/// Find the tab that was hit, based on a point
/// @param hitPoint A Point2I that specifies where to search for a tab hit
GuiTabPageCtrl *findHitTab( Point2I hitPoint );
/// @}
/// @name Sizing
/// @{
/// Rezize our control
/// This method is overridden so that we may handle resizing of our child tab
/// pages when we are resized.
/// This ensures we keep our sizing in sync when we are moved or sized.
///
/// @param newPosition The new position of the control
/// @param newExtent The new extent of the control
void resize(const Point2I &newPosition, const Point2I &newExtent);
/// Called when a child page is resized
/// This method is overridden so that we may handle resizing of our child tab
/// pages when one of them is resized.
/// This ensures we keep our sizing in sync when we our children are sized or moved.
///
/// @param child A pointer to the child control that has been resized
void childResized(GuiControl *child);
/// @}
/// @name Mouse Events
/// @{
void onMouseDown(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
void onRightMouseDown(const GuiEvent &event);
void onMouseDownEditor(const GuiEvent &event, Point2I offset);
void onRightMouseDownEditor(const GuiEvent &event, Point2I offset);
/// @}
};
#endif //_GUI_TABBOOKCTRL_H_

View File

@ -0,0 +1,743 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/containers/guiWindowCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
IMPLEMENT_CONOBJECT(GuiWindowCtrl);
GuiWindowCtrl::GuiWindowCtrl(void)
{
mResizeWidth = true;
mResizeHeight = true;
mCanMove = true;
mCanClose = true;
mCanMinimize = true;
mCanMaximize = true;
mTitleHeight = 20;
mResizeRightWidth = 10;
mResizeBottomHeight = 10;
mCloseCommand = StringTable->insert("");
mMinimized = false;
mMaximized = false;
mMouseMovingWin = false;
mMouseResizeWidth = false;
mMouseResizeHeight = false;
mBounds.extent.set(100, 200);
mMinSize.set(50, 50);
mMinimizeIndex = -1;
mTabIndex = -1;
RectI closeRect(80, 2, 16, 16);
mCloseButton = closeRect;
closeRect.point.x -= 18;
mMaximizeButton = closeRect;
closeRect.point.x -= 18;
mMinimizeButton = closeRect;
//other defaults
mActive = true;
mPressClose = false;
mPressMaximize = false;
mPressMinimize = false;
mDefaultCursor = NULL;
mNWSECursor = NULL;
mNESWCursor = NULL;
mUpDownCursor = NULL;
mLeftRightCursor = NULL;
}
void GuiWindowCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("resizeWidth", TypeBool, Offset(mResizeWidth, GuiWindowCtrl));
addField("resizeHeight", TypeBool, Offset(mResizeHeight, GuiWindowCtrl));
addField("canMove", TypeBool, Offset(mCanMove, GuiWindowCtrl));
addField("canClose", TypeBool, Offset(mCanClose, GuiWindowCtrl));
addField("canMinimize", TypeBool, Offset(mCanMinimize, GuiWindowCtrl));
addField("canMaximize", TypeBool, Offset(mCanMaximize, GuiWindowCtrl));
addField("minSize", TypePoint2I, Offset(mMinSize, GuiWindowCtrl));
addField("closeCommand", TypeString, Offset(mCloseCommand, GuiWindowCtrl));
}
bool GuiWindowCtrl::isMinimized(S32 &index)
{
index = mMinimizeIndex;
return mMinimized && mVisible;
}
// helper fn so button positioning shares code...
void GuiWindowCtrl::PositionButtons(void)
{
S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x;
S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y;
Point2I mainOff = mProfile->mTextOffset;
// until a pref, if alignment is LEFT, put buttons RIGHT justified.
// ELSE, put buttons LEFT justified.
int closeLeft = mainOff.x, closeTop = mainOff.y, closeOff = buttonWidth + 2;
if ( mProfile->mAlignment == GuiControlProfile::LeftJustify )
{
closeOff = -closeOff;
closeLeft = mBounds.extent.x - buttonWidth - mainOff.x;
}
RectI closeRect(closeLeft, closeTop, buttonHeight, buttonWidth);
mCloseButton = closeRect;
// Always put Minimize on left side of Maximize.
closeRect.point.x += closeOff;
if (closeOff>0)
{
mMinimizeButton = closeRect;
closeRect.point.x += closeOff;
mMaximizeButton = closeRect;
}
else
{
mMaximizeButton = closeRect;
closeRect.point.x += closeOff;
mMinimizeButton = closeRect;
}
}
bool GuiWindowCtrl::onWake()
{
if (! Parent::onWake())
return false;
//get the texture for the close, minimize, and maximize buttons
mTextureHandle = mProfile->mTextureHandle;
bool result = mProfile->constructBitmapArray() >= NumBitmaps;
AssertFatal(result, "Failed to create the bitmap array");
if(!result)
return false;
mBitmapBounds = mProfile->mBitmapArrayRects.address();
S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x;
S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y;
mTitleHeight = buttonHeight + 4;
mResizeRightWidth = mTitleHeight / 2;
mResizeBottomHeight = mTitleHeight / 2;
//set the button coords
PositionButtons();
//set the tab index
mTabIndex = -1;
GuiControl *parent = getParent();
if (parent && mFirstResponder)
{
mTabIndex = 0;
//count the number of windows preceeding this one
iterator i;
for (i = parent->begin(); i != parent->end(); i++)
{
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
if (ctrl)
{
if (ctrl == this) break;
else if (ctrl->mFirstResponder) mTabIndex++;
}
}
}
return true;
}
void GuiWindowCtrl::onSleep()
{
mTextureHandle = NULL;
Parent::onSleep();
}
GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
{
if (! mMinimized)
return Parent::findHitControl(pt, initialLayer);
else
return this;
}
void GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
Parent::resize(newPosition, newExtent);
//set the button coords
PositionButtons();
}
void GuiWindowCtrl::onMouseDown(const GuiEvent &event)
{
setUpdate();
mOrigBounds = mBounds;
mMouseDownPosition = event.mousePoint;
Point2I localPoint = globalToLocalCoord(event.mousePoint);
//select this window - move it to the front, and set the first responder
selectWindow();
//if we clicked within the title bar
if (localPoint.y < mTitleHeight)
{
//if we clicked on the close button
if (mCanClose && mCloseButton.pointInRect(localPoint))
{
mPressClose = mCanClose;
}
else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
{
mPressMaximize = mCanMaximize;
}
else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
{
mPressMinimize = mCanMinimize;
}
//else we clicked within the title
else
{
mMouseMovingWin = mCanMove;
mMouseResizeWidth = false;
mMouseResizeHeight = false;
}
}
else
{
mMouseMovingWin = false;
//see if we clicked on the right edge
if (mResizeWidth && (localPoint.x > mBounds.extent.x - mResizeRightWidth))
{
mMouseResizeWidth = true;
}
//see if we clicked on the bottom edge (as well)
if (mResizeHeight && (localPoint.y > mBounds.extent.y - mResizeBottomHeight))
{
mMouseResizeHeight = true;
}
}
if (mMouseMovingWin || mMouseResizeWidth || mMouseResizeHeight ||
mPressClose || mPressMaximize || mPressMinimize)
{
mouseLock();
}
else
{
GuiControl *ctrl = findHitControl(localPoint);
if (ctrl && ctrl != this)
ctrl->onMouseDown(event);
}
}
void GuiWindowCtrl::onMouseDragged(const GuiEvent &event)
{
GuiControl *parent = getParent();
GuiCanvas *root = getRoot();
if (! root) return;
Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
Point2I newPosition = mBounds.point;
Point2I newExtent = mBounds.extent;
bool update = false;
if (mMouseMovingWin && parent)
{
newPosition.x = getMax(0, getMin(parent->mBounds.extent.x - mBounds.extent.x, mOrigBounds.point.x + deltaMousePosition.x));
newPosition.y = getMax(0, getMin(parent->mBounds.extent.y - mBounds.extent.y, mOrigBounds.point.y + deltaMousePosition.y));
update = true;
}
else if(mPressClose || mPressMaximize || mPressMinimize)
{
setUpdate();
}
else
{
if (mMouseResizeWidth && parent)
{
newExtent.x = getMax(0, getMax(mMinSize.x, getMin(parent->mBounds.extent.x, mOrigBounds.extent.x + deltaMousePosition.x)));
update = true;
}
if (mMouseResizeHeight && parent)
{
newExtent.y = getMax(0, getMax(mMinSize.y, getMin(parent->mBounds.extent.y, mOrigBounds.extent.y + deltaMousePosition.y)));
update = true;
}
}
if (update)
{
Point2I pos = parent->localToGlobalCoord(mBounds.point);
root->addUpdateRegion(pos, mBounds.extent);
resize(newPosition, newExtent);
}
}
void GuiWindowCtrl::onMouseUp(const GuiEvent &event)
{
bool closing = mPressClose;
bool maximizing = mPressMaximize;
bool minimizing = mPressMinimize;
mPressClose = false;
mPressMaximize = false;
mPressMinimize = false;
event;
mouseUnlock();
mMouseMovingWin = false;
mMouseResizeWidth = false;
mMouseResizeHeight = false;
GuiControl *parent = getParent();
if (! parent)
return;
//see if we take an action
Point2I localPoint = globalToLocalCoord(event.mousePoint);
if (closing && mCloseButton.pointInRect(localPoint))
{
Con::evaluate(mCloseCommand);
}
else if (maximizing && mMaximizeButton.pointInRect(localPoint))
{
if (mMaximized)
{
//resize to the previous position and extent, bounded by the parent
resize(Point2I(getMax(0, getMin(parent->mBounds.extent.x - mStandardBounds.extent.x, mStandardBounds.point.x)),
getMax(0, getMin(parent->mBounds.extent.y - mStandardBounds.extent.y, mStandardBounds.point.y))),
mStandardBounds.extent);
//set the flag
mMaximized = false;
}
else
{
//only save the position if we're not minimized
if (! mMinimized)
{
mStandardBounds = mBounds;
}
else
{
mMinimized = false;
}
//resize to fit the parent
resize(Point2I(0, 0), parent->mBounds.extent);
//set the flag
mMaximized = true;
}
}
else if (minimizing && mMinimizeButton.pointInRect(localPoint))
{
if (mMinimized)
{
//resize to the previous position and extent, bounded by the parent
resize(Point2I(getMax(0, getMin(parent->mBounds.extent.x - mStandardBounds.extent.x, mStandardBounds.point.x)),
getMax(0, getMin(parent->mBounds.extent.y - mStandardBounds.extent.y, mStandardBounds.point.y))),
mStandardBounds.extent);
//set the flag
mMinimized = false;
}
else
{
if (parent->mBounds.extent.x < 100 || parent->mBounds.extent.y < mTitleHeight + 3)
return;
//only save the position if we're not maximized
if (! mMaximized)
{
mStandardBounds = mBounds;
}
else
{
mMaximized = false;
}
//first find the lowest unused minimized index up to 32 minimized windows
U32 indexMask = 0;
iterator i;
S32 count = 0;
for (i = parent->begin(); i != parent->end() && count < 32; i++)
{
count++;
S32 index;
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
if (ctrl && ctrl->isMinimized(index))
{
indexMask |= (1 << index);
}
}
//now find the first unused bit
for (count = 0; count < 32; count++)
{
if (! (indexMask & (1 << count))) break;
}
//if we have more than 32 minimized windows, use the first position
count = getMax(0, count);
//this algorithm assumes all window have the same title height, and will minimize to 98 pix
Point2I newExtent(98, mTitleHeight);
//first, how many can fit across
S32 numAcross = getMax(1, (parent->mBounds.extent.x / newExtent.x + 2));
//find the new "mini position"
Point2I newPosition;
newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2;
newPosition.y = parent->mBounds.extent.y - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2;
//find the minimized position and extent
resize(newPosition, newExtent);
//set the index so other windows will not try to minimize to the same location
mMinimizeIndex = count;
//set the flag
mMinimized = true;
}
}
}
GuiControl *GuiWindowCtrl::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 *GuiWindowCtrl::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;
}
bool GuiWindowCtrl::onKeyDown(const GuiEvent &event)
{
//if this control is a dead end, kill the event
if ((! mVisible) || (! mActive) || (! mAwake)) return true;
if ((event.keyCode == KEY_TAB) && (event.modifier & SI_CTRL))
{
//find the next sibling window, and select it
GuiControl *parent = getParent();
if (parent)
{
GuiWindowCtrl *firstWindow = NULL;
iterator i;
for (i = parent->begin(); i != parent->end(); i++)
{
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
if (ctrl && ctrl->getTabIndex() == mTabIndex + 1)
{
ctrl->selectWindow();
return true;
}
else if (ctrl && ctrl->getTabIndex() == 0)
{
firstWindow = ctrl;
}
}
//recycle from the beginning
if (firstWindow != this)
{
firstWindow->selectWindow();
return true;
}
}
}
return Parent::onKeyDown(event);
}
void GuiWindowCtrl::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 GuiWindowCtrl::drawWinRect(const RectI &myRect)
{
Point2I bl = myRect.point;
Point2I tr;
tr.x = myRect.point.x + myRect.extent.x - 1;
tr.y = myRect.point.y + myRect.extent.y - 1;
dglDrawRectFill(myRect, mProfile->mFillColor);
dglDrawLine(Point2I(bl.x + 1, tr.y), Point2I(bl.x + 1, bl.y), ColorI(255, 255, 255));
dglDrawLine(Point2I(bl.x, tr.y + 1), Point2I(tr.x, tr.y + 1), ColorI(255, 255, 255));
//dglDrawRect(myRect, ColorI(0, 0, 0)); // Taken out, this is controled via mProfile->mBorder
}
void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect)
{
//draw the outline
RectI winRect;
winRect.point = offset;
winRect.extent = mBounds.extent;
GuiCanvas *root = getRoot();
GuiControl *firstResponder = root ? root->getFirstResponder() : NULL;
bool isKey = (!firstResponder || ControlIsChild(firstResponder));
U32 topBase = isKey ? BorderTopLeftKey : BorderTopLeftNoKey;
winRect.point.x += mBitmapBounds[BorderLeft].extent.x;
winRect.point.y += mBitmapBounds[topBase + 2].extent.y;
winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x;
winRect.extent.y -= mBitmapBounds[topBase + 2].extent.y + mBitmapBounds[BorderBottom].extent.y;
dglDrawRectFill(winRect, mProfile->mFillColor);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, offset, mBitmapBounds[topBase]);
dglDrawBitmapSR(mTextureHandle, Point2I(offset.x + mBounds.extent.x - mBitmapBounds[topBase+1].extent.x, offset.y),
mBitmapBounds[topBase + 1]);
RectI destRect;
destRect.point.x = offset.x + mBitmapBounds[topBase].extent.x;
destRect.point.y = offset.y;
destRect.extent.x = mBounds.extent.x - mBitmapBounds[topBase].extent.x - mBitmapBounds[topBase + 1].extent.x;
destRect.extent.y = mBitmapBounds[topBase + 2].extent.y;
RectI stretchRect = mBitmapBounds[topBase + 2];
stretchRect.inset(1,0);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
destRect.point.x = offset.x;
destRect.point.y = offset.y + mBitmapBounds[topBase].extent.y;
destRect.extent.x = mBitmapBounds[BorderLeft].extent.x;
destRect.extent.y = mBounds.extent.y - mBitmapBounds[topBase].extent.y - mBitmapBounds[BorderBottomLeft].extent.y;
stretchRect = mBitmapBounds[BorderLeft];
stretchRect.inset(0,1);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
destRect.point.x = offset.x + mBounds.extent.x - mBitmapBounds[BorderRight].extent.x;
destRect.extent.x = mBitmapBounds[BorderRight].extent.x;
destRect.point.y = offset.y + mBitmapBounds[topBase + 1].extent.y;
destRect.extent.y = mBounds.extent.y - mBitmapBounds[topBase + 1].extent.y - mBitmapBounds[BorderBottomRight].extent.y;
stretchRect = mBitmapBounds[BorderRight];
stretchRect.inset(0,1);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
dglDrawBitmapSR(mTextureHandle, offset + Point2I(0, mBounds.extent.y - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]);
dglDrawBitmapSR(mTextureHandle, offset + mBounds.extent - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]);
destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x;
destRect.extent.x = mBounds.extent.x - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x;
destRect.point.y = offset.y + mBounds.extent.y - mBitmapBounds[BorderBottom].extent.y;
destRect.extent.y = mBitmapBounds[BorderBottom].extent.y;
stretchRect = mBitmapBounds[BorderBottom];
stretchRect.inset(1,0);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
//draw the title
// dhc addition: copied/modded from renderJustifiedText, since we enforce a
// different color usage here. NOTE: it currently CAN overdraw the controls
// if mis-positioned or 'scrunched' in a small width.
dglSetBitmapModulation(mProfile->mFontColor);
S32 fontHeight = mFont->getHeight();
S32 textWidth = mProfile->mFont->getStrWidth((const UTF8 *)mText);
Point2I start(0,0);
// align the horizontal
if ( mProfile->mAlignment == GuiControlProfile::RightJustify )
start.set( winRect.extent.x - textWidth, 0 );
else if ( mProfile->mAlignment == GuiControlProfile::CenterJustify )
start.set( ( winRect.extent.x - textWidth) / 2, 0 );
else // GuiControlProfile::LeftJustify or garbage... ;)
start.set( 0, 0 );
// If the text is longer then the box size, (it'll get clipped) so force Left Justify
if( textWidth > winRect.extent.x ) start.set( 0, 0 );
// center the vertical
// start.y = ( winRect.extent.y - ( font->getHeight() - 2 ) ) / 2;
dglDrawText(mFont, start + offset + mProfile->mTextOffset, mText);
// deal with rendering the titlebar controls
AssertFatal(root, "Unable to get the root Canvas.");
Point2I localPoint = globalToLocalCoord(root->getCursorPos());
//draw the close button
Point2I tempUL;
Point2I tempLR;
S32 bmp = BmpStates * BmpClose;
if( mCanClose ) {
if( mCloseButton.pointInRect( localPoint ) && mPressClose )
bmp += BmpHilite;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, offset + mCloseButton.point, mBitmapBounds[bmp]);
}
//draw the maximize button
if( mMaximized )
bmp = BmpStates * BmpNormal;
else
bmp = BmpStates * BmpMaximize;
if( mCanMaximize ) {
if( mMaximizeButton.pointInRect( localPoint ) && mPressMaximize )
bmp += BmpHilite;
dglClearBitmapModulation();
dglDrawBitmapSR( mTextureHandle, offset + mMaximizeButton.point, mBitmapBounds[bmp] );
}
//draw the minimize button
if( mMinimized )
bmp = BmpStates * BmpNormal;
else
bmp = BmpStates * BmpMinimize;
if( mCanMinimize ) {
if( mMinimizeButton.pointInRect( localPoint ) && mPressMinimize )
bmp += BmpHilite;
dglClearBitmapModulation();
dglDrawBitmapSR( mTextureHandle, offset + mMinimizeButton.point, mBitmapBounds[bmp] );
}
if( !mMinimized )
{
//render the children
renderChildControls( offset, updateRect );
}
}
bool GuiWindowCtrl::initCursors()
{
if ( mUpDownCursor == NULL || mLeftRightCursor == NULL || mDefaultCursor == NULL || mNWSECursor == NULL || mNESWCursor == NULL )
{
SimObject *obj;
obj = Sim::findObject("UpDownCursor");
mUpDownCursor = dynamic_cast<GuiCursor*>(obj);
obj = Sim::findObject("LeftRightCursor");
mLeftRightCursor = dynamic_cast<GuiCursor*>(obj);
obj = Sim::findObject("DefaultCursor");
mDefaultCursor = dynamic_cast<GuiCursor*>(obj);
obj = Sim::findObject("NESWCursor");
mNESWCursor = dynamic_cast<GuiCursor*>(obj);
obj = Sim::findObject("NWSECursor");
mNWSECursor = dynamic_cast<GuiCursor*>(obj);
return( mUpDownCursor != NULL && mLeftRightCursor != NULL && mDefaultCursor != NULL && mNWSECursor != NULL && mNESWCursor != NULL );
}
else
return(true);
}
void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
{
showCursor = true;
if( initCursors() )
{
Point2I mousePos = lastGuiEvent.mousePoint;
RectI winRect = mBounds;
RectI rightRect = RectI( ( ( winRect.extent.x + winRect.point.x ) - mResizeRightWidth), winRect.point.y, mResizeRightWidth, winRect.extent.y );
RectI bottomRect = RectI( winRect.point.x, ( ( winRect.point.y + winRect.extent.y ) - mResizeBottomHeight), winRect.extent.x , mResizeBottomHeight );
bool resizeRight = rightRect.pointInRect( mousePos );
bool resizeBottom = bottomRect.pointInRect( mousePos );
if ( resizeRight && resizeBottom && mResizeHeight && mResizeWidth )
cursor = mNWSECursor;
else if ( resizeBottom && mResizeHeight )
cursor = mUpDownCursor;
else if ( resizeRight && mResizeWidth )
cursor = mLeftRightCursor;
else
cursor = NULL;
}
}

View File

@ -0,0 +1,132 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIWINDOWCTRL_H_
#define _GUIWINDOWCTRL_H_
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
class GuiWindowCtrl : public GuiTextCtrl
{
private:
typedef GuiTextCtrl Parent;
bool mResizeWidth;
bool mResizeHeight;
bool mCanMove;
bool mCanClose;
bool mCanMinimize;
bool mCanMaximize;
bool mPressClose;
bool mPressMinimize;
bool mPressMaximize;
Point2I mMinSize;
GuiCursor* mDefaultCursor;
GuiCursor* mLeftRightCursor;
GuiCursor* mUpDownCursor;
GuiCursor* mNWSECursor;
GuiCursor* mNESWCursor;
StringTableEntry mCloseCommand;
S32 mTitleHeight;
S32 mResizeRightWidth;
S32 mResizeBottomHeight;
bool mMouseMovingWin;
bool mMouseResizeWidth;
bool mMouseResizeHeight;
bool mMinimized;
bool mMaximized;
Point2I mMouseDownPosition;
RectI mOrigBounds;
RectI mStandardBounds;
RectI mCloseButton;
RectI mMinimizeButton;
RectI mMaximizeButton;
S32 mMinimizeIndex;
S32 mTabIndex;
void PositionButtons(void);
protected:
enum BitmapIndices
{
BmpClose,
BmpMaximize,
BmpNormal,
BmpMinimize,
BmpCount
};
enum {
BorderTopLeftKey = 12,
BorderTopRightKey,
BorderTopKey,
BorderTopLeftNoKey,
BorderTopRightNoKey,
BorderTopNoKey,
BorderLeft,
BorderRight,
BorderBottomLeft,
BorderBottom,
BorderBottomRight,
NumBitmaps
};
enum BitmapStates
{
BmpDefault = 0,
BmpHilite,
BmpDisabled,
BmpStates
};
RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
TextureHandle mTextureHandle;
void drawWinRect(const RectI &myRect);
public:
GuiWindowCtrl();
DECLARE_CONOBJECT(GuiWindowCtrl);
static void initPersistFields();
bool onWake();
void onSleep();
bool isMinimized(S32 &index);
bool initCursors();
void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
void setFont(S32 fntTag);
GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
void resize(const Point2I &newPosition, const Point2I &newExtent);
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
//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);
bool onKeyDown(const GuiEvent &event);
S32 getTabIndex(void) { return mTabIndex; }
void selectWindow(void);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif //_GUI_WINDOW_CTRL_H

View File

@ -0,0 +1,26 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "gui/controls/guiBackgroundCtrl.h"
IMPLEMENT_CONOBJECT(GuiBackgroundCtrl);
//--------------------------------------------------------------------------
GuiBackgroundCtrl::GuiBackgroundCtrl() : GuiControl()
{
mDraw = false;
}
//--------------------------------------------------------------------------
void GuiBackgroundCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if ( mDraw )
Parent::onRender( offset, updateRect );
renderChildControls(offset, updateRect);
}

View File

@ -0,0 +1,29 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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);
GuiBackgroundCtrl();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif

View File

@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (c) 2002 GarageGames.Com
//-----------------------------------------------------------------------------
#include "gui/core/guiControl.h"
#include "dgl/dgl.h"
/// 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]
TextureHandle mTextureHandle;
public:
bool onWake();
void onSleep();
void onRender(Point2I offset, const RectI &updateRect);
DECLARE_CONOBJECT(GuiBitmapBorderCtrl);
};
IMPLEMENT_CONOBJECT(GuiBitmapBorderCtrl);
bool GuiBitmapBorderCtrl::onWake()
{
if (! Parent::onWake())
return false;
//get the texture for the close, minimize, and maximize buttons
mTextureHandle = mProfile->mTextureHandle;
bool result = mProfile->constructBitmapArray() >= NumBitmaps;
AssertFatal(result, "Failed to create the bitmap array");
if(!result)
return false;
mBitmapBounds = mProfile->mBitmapArrayRects.address();
return true;
}
void GuiBitmapBorderCtrl::onSleep()
{
mTextureHandle = NULL;
Parent::onSleep();
}
void GuiBitmapBorderCtrl::onRender(Point2I offset, const RectI &updateRect)
{
renderChildControls( offset, updateRect );
dglSetClipRect(updateRect);
//draw the outline
RectI winRect;
winRect.point = offset;
winRect.extent = mBounds.extent;
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)
dglDrawRectFill(winRect, mProfile->mFillColor);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, offset, mBitmapBounds[BorderTopLeft]);
dglDrawBitmapSR(mTextureHandle, Point2I(offset.x + mBounds.extent.x - 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 = mBounds.extent.x - mBitmapBounds[BorderTopLeft].extent.x - mBitmapBounds[BorderTopRight].extent.x;
destRect.extent.y = mBitmapBounds[BorderTop].extent.y;
RectI stretchRect = mBitmapBounds[BorderTop];
stretchRect.inset(1,0);
dglDrawBitmapStretchSR(mTextureHandle, 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 = mBounds.extent.y - mBitmapBounds[BorderTopLeft].extent.y - mBitmapBounds[BorderBottomLeft].extent.y;
stretchRect = mBitmapBounds[BorderLeft];
stretchRect.inset(0,1);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
destRect.point.x = offset.x + mBounds.extent.x - mBitmapBounds[BorderRight].extent.x;
destRect.extent.x = mBitmapBounds[BorderRight].extent.x;
destRect.point.y = offset.y + mBitmapBounds[BorderTopRight].extent.y;
destRect.extent.y = mBounds.extent.y - mBitmapBounds[BorderTopRight].extent.y - mBitmapBounds[BorderBottomRight].extent.y;
stretchRect = mBitmapBounds[BorderRight];
stretchRect.inset(0,1);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
dglDrawBitmapSR(mTextureHandle, offset + Point2I(0, mBounds.extent.y - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]);
dglDrawBitmapSR(mTextureHandle, offset + mBounds.extent - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]);
destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x;
destRect.extent.x = mBounds.extent.x - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x;
destRect.point.y = offset.y + mBounds.extent.y - mBitmapBounds[BorderBottom].extent.y;
destRect.extent.y = mBitmapBounds[BorderBottom].extent.y;
stretchRect = mBitmapBounds[BorderBottom];
stretchRect.inset(1,0);
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
}

View File

@ -0,0 +1,234 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
//-------------------------------------
//
// Bitmap Button Contrl
// Set 'bitmap' comsole field to base name of bitmaps to use. This control will
// append '_n' for normal
// append '_h' for hilighted
// append '_d' for depressed
//
// if bitmap cannot be found it will use the default bitmap to render.
//
// 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 normal bitmap (if present)
//
#include "console/console.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "platform/platformAudio.h"
#include "gui/core/guiCanvas.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/controls/guiBitmapButtonCtrl.h"
IMPLEMENT_CONOBJECT(GuiBitmapButtonCtrl);
//-------------------------------------
GuiBitmapButtonCtrl::GuiBitmapButtonCtrl()
{
mBitmapName = StringTable->insert("");
mBounds.extent.set(140, 30);
}
//-------------------------------------
void GuiBitmapButtonCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("bitmap", TypeFilename, Offset(mBitmapName, GuiBitmapButtonCtrl));
}
//-------------------------------------
bool GuiBitmapButtonCtrl::onWake()
{
if (! Parent::onWake())
return false;
setActive(true);
setBitmap(mBitmapName);
return true;
}
//-------------------------------------
void GuiBitmapButtonCtrl::onSleep()
{
mTextureNormal = NULL;
mTextureHilight = NULL;
mTextureDepressed = NULL;
Parent::onSleep();
}
//-------------------------------------
ConsoleMethod( GuiBitmapButtonCtrl, setBitmap, void, 3, 3, "(filepath name)")
{
object->setBitmap(argv[2]);
}
//-------------------------------------
void GuiBitmapButtonCtrl::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 normal bitmap (if present)
Parent::inspectPostApply();
if ((mBounds.extent.x == 0) && (mBounds.extent.y == 0) && mTextureNormal)
{
TextureObject *texture = (TextureObject *) mTextureNormal;
mBounds.extent.x = texture->bitmapWidth;
mBounds.extent.y = texture->bitmapHeight;
}
}
//-------------------------------------
void GuiBitmapButtonCtrl::setBitmap(const char *name)
{
mBitmapName = StringTable->insert(name);
if(!isAwake())
return;
if (*mBitmapName)
{
char buffer[1024];
char *p;
dStrcpy(buffer, name);
p = buffer + dStrlen(buffer);
dStrcpy(p, "_n");
mTextureNormal = TextureHandle(buffer, BitmapTexture, true);
dStrcpy(p, "_h");
mTextureHilight = TextureHandle(buffer, BitmapTexture, true);
if (!mTextureHilight)
mTextureHilight = mTextureNormal;
dStrcpy(p, "_d");
mTextureDepressed = TextureHandle(buffer, BitmapTexture, true);
if (!mTextureDepressed)
mTextureDepressed = mTextureHilight;
dStrcpy(p, "_i");
mTextureInactive = TextureHandle(buffer, BitmapTexture, true);
if (!mTextureInactive)
mTextureInactive = mTextureNormal;
}
else
{
mTextureNormal = NULL;
mTextureHilight = NULL;
mTextureDepressed = NULL;
mTextureInactive = NULL;
}
setUpdate();
}
//-------------------------------------
void GuiBitmapButtonCtrl::onRender(Point2I offset, const RectI& updateRect)
{
enum {
NORMAL,
HILIGHT,
DEPRESSED,
INACTIVE
} state = NORMAL;
if (mActive)
{
if (mMouseOver) state = HILIGHT;
if (mDepressed || mStateOn) state = DEPRESSED;
}
else
state = INACTIVE;
switch (state)
{
case NORMAL: renderButton(mTextureNormal, offset, updateRect); break;
case HILIGHT: renderButton(mTextureHilight ? mTextureHilight : mTextureNormal, offset, updateRect); break;
case DEPRESSED: renderButton(mTextureDepressed, offset, updateRect); break;
case INACTIVE: renderButton(mTextureInactive ? mTextureInactive : mTextureNormal, offset, updateRect); break;
}
}
//------------------------------------------------------------------------------
void GuiBitmapButtonCtrl::renderButton(TextureHandle &texture, Point2I &offset, const RectI& updateRect)
{
if (texture)
{
RectI rect(offset, mBounds.extent);
dglClearBitmapModulation();
dglDrawBitmapStretch(texture, rect);
renderChildControls( offset, updateRect);
}
else
Parent::onRender(offset, updateRect);
}
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiBitmapButtonTextCtrl);
void GuiBitmapButtonTextCtrl::onRender(Point2I offset, const RectI& updateRect)
{
enum {
NORMAL,
HILIGHT,
DEPRESSED,
INACTIVE
} state = NORMAL;
if (mActive)
{
if (mMouseOver) state = HILIGHT;
if (mDepressed || mStateOn) state = DEPRESSED;
}
else
state = INACTIVE;
TextureHandle texture;
switch (state)
{
case NORMAL:
texture = mTextureNormal;
break;
case HILIGHT:
texture = mTextureHilight;
break;
case DEPRESSED:
texture = mTextureDepressed;
break;
case INACTIVE:
texture = mTextureInactive;
if(!texture)
texture = mTextureNormal;
break;
}
if (texture)
{
RectI rect(offset, mBounds.extent);
dglClearBitmapModulation();
dglDrawBitmapStretch(texture, rect);
Point2I textPos = offset;
if(mDepressed)
textPos += Point2I(1,1);
// Make sure we take the profile's textOffset into account.
textPos += mProfile->mTextOffset;
dglSetBitmapModulation( mProfile->mFontColor );
renderJustifiedText(textPos, mBounds.extent, mButtonText);
renderChildControls( offset, updateRect);
}
else
Parent::onRender(offset, updateRect);
}

View File

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIBITMAPBUTTON_H_
#define _GUIBITMAPBUTTON_H_
#ifndef _GUIBUTTONCTRL_H_
#include "gui/controls/guiButtonCtrl.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
///-------------------------------------
/// Bitmap Button Contrl
/// Set 'bitmap' comsole field to base name of bitmaps to use. This control will
/// append '_n' for normal
/// append '_h' for hilighted
/// append '_d' for depressed
///
/// if bitmap cannot be found it will use the default bitmap to render.
///
/// if the extent is set to (0,0) in the gui editor and apply hit, this control will
/// set it's extent to be exactly the size of the normal bitmap (if present)
///
class GuiBitmapButtonCtrl : public GuiButtonCtrl
{
private:
typedef GuiButtonCtrl Parent;
protected:
StringTableEntry mBitmapName;
TextureHandle mTextureNormal;
TextureHandle mTextureHilight;
TextureHandle mTextureDepressed;
TextureHandle mTextureInactive;
void renderButton(TextureHandle &texture, Point2I &offset, const RectI& updateRect);
public:
DECLARE_CONOBJECT(GuiBitmapButtonCtrl);
GuiBitmapButtonCtrl();
static void initPersistFields();
//Parent methods
bool onWake();
void onSleep();
void inspectPostApply();
void setBitmap(const char *name);
void onRender(Point2I offset, const RectI &updateRect);
};
class GuiBitmapButtonTextCtrl : public GuiBitmapButtonCtrl
{
typedef GuiBitmapButtonCtrl Parent;
public:
DECLARE_CONOBJECT(GuiBitmapButtonTextCtrl);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif //_GUI_BITMAP_BUTTON_CTRL_H

View File

@ -0,0 +1,166 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "gui/controls/guiBitmapCtrl.h"
IMPLEMENT_CONOBJECT(GuiBitmapCtrl);
GuiBitmapCtrl::GuiBitmapCtrl(void)
{
mBitmapName = StringTable->insert("");
startPoint.set(0, 0);
mWrap = false;
}
void GuiBitmapCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField("bitmap", TypeFilename, Offset(mBitmapName, GuiBitmapCtrl));
addField("wrap", TypeBool, Offset(mWrap, GuiBitmapCtrl));
endGroup("Misc");
}
ConsoleMethod( GuiBitmapCtrl, setValue, void, 4, 4, "(int xAxis, int yAxis)"
"Set the offset of the bitmap.")
{
object->setValue(dAtoi(argv[2]), dAtoi(argv[3]));
}
ConsoleMethod( GuiBitmapCtrl, setBitmap, void, 3, 3, "(string filename)"
"Set the bitmap displayed in the control. Note that it is limited in size, to 256x256.")
{
object->setBitmap(argv[2]);
}
bool GuiBitmapCtrl::onWake()
{
if (! Parent::onWake())
return false;
setActive(true);
setBitmap(mBitmapName);
return true;
}
void GuiBitmapCtrl::onSleep()
{
mTextureHandle = 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 && (mBounds.extent.x == 0) && (mBounds.extent.y == 0) && mTextureHandle)
{
TextureObject *texture = (TextureObject *) mTextureHandle;
mBounds.extent.x = texture->bitmapWidth;
mBounds.extent.y = texture->bitmapHeight;
}
}
void GuiBitmapCtrl::setBitmap(const char *name, bool resize)
{
mBitmapName = StringTable->insert(name);
if (*mBitmapName) {
mTextureHandle = TextureHandle(mBitmapName, BitmapTexture, true);
// Resize the control to fit the bitmap
if (resize) {
TextureObject* texture = (TextureObject *) mTextureHandle;
mBounds.extent.x = texture->bitmapWidth;
mBounds.extent.y = texture->bitmapHeight;
Point2I extent = getParent()->getExtent();
parentResized(extent,extent);
}
}
else
mTextureHandle = NULL;
setUpdate();
}
void GuiBitmapCtrl::setBitmap(const TextureHandle &handle, bool resize)
{
mTextureHandle = handle;
// Resize the control to fit the bitmap
if (resize) {
TextureObject* texture = (TextureObject *) mTextureHandle;
mBounds.extent.x = texture->bitmapWidth;
mBounds.extent.y = texture->bitmapHeight;
Point2I extent = getParent()->getExtent();
parentResized(extent,extent);
}
}
void GuiBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if (mTextureHandle)
{
dglClearBitmapModulation();
if(mWrap)
{
TextureObject* texture = (TextureObject *) mTextureHandle;
RectI srcRegion;
RectI dstRegion;
float xdone = ((float)mBounds.extent.x/(float)texture->bitmapWidth)+1;
float ydone = ((float)mBounds.extent.y/(float)texture->bitmapHeight)+1;
int xshift = startPoint.x%texture->bitmapWidth;
int yshift = startPoint.y%texture->bitmapHeight;
for(int y = 0; y < ydone; ++y)
for(int x = 0; x < xdone; ++x)
{
srcRegion.set(0,0,texture->bitmapWidth,texture->bitmapHeight);
dstRegion.set( ((texture->bitmapWidth*x)+offset.x)-xshift,
((texture->bitmapHeight*y)+offset.y)-yshift,
texture->bitmapWidth,
texture->bitmapHeight);
dglDrawBitmapStretchSR(texture,dstRegion, srcRegion, false);
}
}
else
{
RectI rect(offset, mBounds.extent);
dglDrawBitmapStretch(mTextureHandle, rect);
}
}
if (mProfile->mBorder || !mTextureHandle)
{
RectI rect(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
dglDrawRect(rect, mProfile->mBorderColor);
}
renderChildControls(offset, updateRect);
}
void GuiBitmapCtrl::setValue(S32 x, S32 y)
{
if (mTextureHandle)
{
TextureObject* texture = (TextureObject *) mTextureHandle;
x+=texture->bitmapWidth/2;
y+=texture->bitmapHeight/2;
}
while (x < 0)
x += 256;
startPoint.x = x % 256;
while (y < 0)
y += 256;
startPoint.y = y % 256;
}

View File

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIBITMAPCTRL_H_
#define _GUIBITMAPCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
/// Renders a bitmap.
class GuiBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
StringTableEntry mBitmapName;
TextureHandle mTextureHandle;
Point2I startPoint;
bool mWrap;
public:
//creation methods
DECLARE_CONOBJECT(GuiBitmapCtrl);
GuiBitmapCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
void inspectPostApply();
void setBitmap(const char *name,bool resize = false);
void setBitmap(const TextureHandle &handle,bool resize = false);
S32 getWidth() const { return(mTextureHandle.getWidth()); }
S32 getHeight() const { return(mTextureHandle.getHeight()); }
void onRender(Point2I offset, const RectI &updateRect);
void setValue(S32 x, S32 y);
};
#endif

View File

@ -0,0 +1,41 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiButtonBaseCtrl.h"
class GuiBorderButtonCtrl : public GuiButtonBaseCtrl
{
typedef GuiButtonBaseCtrl Parent;
protected:
public:
DECLARE_CONOBJECT(GuiBorderButtonCtrl);
void onRender(Point2I offset, const RectI &updateRect);
};
IMPLEMENT_CONOBJECT(GuiBorderButtonCtrl);
void GuiBorderButtonCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI bounds(offset, mBounds.extent);
if(mActive && mMouseOver)
{
bounds.inset(2,2);
dglDrawRect(bounds, mProfile->mFontColorHL);
bounds.inset(-2,-2);
}
if(mActive && (mStateOn || mDepressed))
{
dglDrawRect(bounds, mProfile->mFontColorHL);
bounds.inset(1,1);
dglDrawRect(bounds, mProfile->mFontColorHL);
}
renderChildControls(offset, updateRect);
}

View File

@ -0,0 +1,311 @@
//-----------------------------------------------------------------------------
// Torque Engine
//
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "platform/platformAudio.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiButtonBaseCtrl.h"
#include "i18n/lang.h"
IMPLEMENT_CONOBJECT(GuiButtonBaseCtrl);
GuiButtonBaseCtrl::GuiButtonBaseCtrl()
{
mDepressed = false;
mMouseOver = false;
mActive = true;
mButtonText = StringTable->insert("Button");
mButtonTextID = StringTable->insert("");
mStateOn = false;
mRadioGroup = -1;
mButtonType = ButtonTypePush;
}
bool GuiButtonBaseCtrl::onWake()
{
if(!Parent::onWake())
return false;
// is we have a script variable, make sure we're in sync
if ( mConsoleVariable[0] )
mStateOn = Con::getBoolVariable( mConsoleVariable );
if(mButtonTextID && *mButtonTextID != 0)
setTextID(mButtonTextID);
return true;
}
ConsoleMethod( GuiButtonBaseCtrl, performClick, void, 2, 2, "() - simulates a button click from script." )
{
argc; argv;
object->onAction();
}
ConsoleMethod( GuiButtonBaseCtrl, setText, void, 3, 3, "(string text) - sets the text of the button to the string." )
{
argc;
object->setText( argv[2] );
}
ConsoleMethod( GuiButtonBaseCtrl, setTextID, void, 3, 3, "(string id) - sets the text of the button to the localized string." )
{
argc;
object->setTextID(argv[2]);
}
ConsoleMethod( GuiButtonBaseCtrl, getText, const char *, 2, 2, "() - returns the text of the button." )
{
argc; argv;
return object->getText( );
}
static EnumTable::Enums buttonTypeEnums[] =
{
{ GuiButtonBaseCtrl::ButtonTypePush, "PushButton" },
{ GuiButtonBaseCtrl::ButtonTypeCheck, "ToggleButton" },
{ GuiButtonBaseCtrl::ButtonTypeRadio, "RadioButton" },
};
static EnumTable gButtonTypeTable(3, &buttonTypeEnums[0]);
void GuiButtonBaseCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField("text", TypeCaseString, Offset(mButtonText, GuiButtonBaseCtrl));
addField("textID", TypeString, Offset(mButtonTextID, GuiButtonBaseCtrl));
addField("groupNum", TypeS32, Offset(mRadioGroup, GuiButtonBaseCtrl));
addField("buttonType", TypeEnum, Offset(mButtonType, GuiButtonBaseCtrl), 1, &gButtonTypeTable);
endGroup("Misc");
}
void GuiButtonBaseCtrl::setText(const char *text)
{
mButtonText = StringTable->insert(text);
}
void GuiButtonBaseCtrl::setTextID(const char *id)
{
S32 n = Con::getIntVariable(id, -1);
if(n != -1)
{
mButtonTextID = StringTable->insert(id);
setTextID(n);
}
}
void GuiButtonBaseCtrl::setTextID(S32 id)
{
const UTF8 *str = getGUIString(id);
if(str)
setText((const char*)str);
//mButtonTextID = id;
}
const char *GuiButtonBaseCtrl::getText()
{
return mButtonText;
}
//---------------------------------------------------------------------------
void GuiButtonBaseCtrl::acceleratorKeyPress(U32)
{
if (! mActive)
return;
//set the bool
mDepressed = true;
if (mProfile->mTabable)
setFirstResponder();
}
//---------------------------------------------------------------------------
void GuiButtonBaseCtrl::acceleratorKeyRelease(U32)
{
if (! mActive)
return;
if (mDepressed)
{
//set the bool
mDepressed = false;
//perform the action
onAction();
}
//update
setUpdate();
}
void GuiButtonBaseCtrl::onMouseDown(const GuiEvent &event)
{
if (! mActive)
return;
if (mProfile->mCanKeyFocus)
setFirstResponder();
if (mProfile->mSoundButtonDown)
{
F32 pan = (F32(event.mousePoint.x)/F32(Canvas->mBounds.extent.x)*2.0f-1.0f)*0.8f;
AUDIOHANDLE handle = alxCreateSource(mProfile->mSoundButtonDown);
alxPlay(handle);
}
//lock the mouse
mouseLock();
mDepressed = true;
//update
setUpdate();
}
void GuiButtonBaseCtrl::onMouseEnter(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
{
mDepressed = true;
mMouseOver = true;
}
else
{
if ( mActive && mProfile->mSoundButtonOver )
{
F32 pan = (F32(event.mousePoint.x)/F32(Canvas->mBounds.extent.x)*2.0f-1.0f)*0.8f;
AUDIOHANDLE handle = alxCreateSource(mProfile->mSoundButtonOver);
alxPlay(handle);
}
mMouseOver = true;
}
}
void GuiButtonBaseCtrl::onMouseLeave(const GuiEvent &)
{
setUpdate();
if(isMouseLocked())
mDepressed = false;
mMouseOver = false;
}
void GuiButtonBaseCtrl::onMouseUp(const GuiEvent &)
{
if (! mActive)
return;
mouseUnlock();
setUpdate();
//if we released the mouse within this control, perform the action
if (mDepressed)
onAction();
mDepressed = false;
}
//--------------------------------------------------------------------------
bool GuiButtonBaseCtrl::onKeyDown(const GuiEvent &event)
{
//if the control is a dead end, kill the event
if (!mActive)
return true;
//see if the key down is a return or space or not
if ((event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE)
&& event.modifier == 0)
{
if ( mProfile->mSoundButtonDown )
{
F32 pan = ( F32( event.mousePoint.x ) / F32( Canvas->mBounds.extent.x ) * 2.0f - 1.0f ) * 0.8f;
AUDIOHANDLE handle = alxCreateSource( mProfile->mSoundButtonDown );
alxPlay( handle );
}
return true;
}
//otherwise, pass the event to it's parent
return Parent::onKeyDown(event);
}
//--------------------------------------------------------------------------
bool GuiButtonBaseCtrl::onKeyUp(const GuiEvent &event)
{
//if the control is a dead end, kill the event
if (!mActive)
return true;
//see if the key down is a return or space or not
if (mDepressed &&
(event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE) &&
event.modifier == 0)
{
onAction();
return true;
}
//otherwise, pass the event to it's parent
return Parent::onKeyUp(event);
}
//---------------------------------------------------------------------------
void GuiButtonBaseCtrl::setScriptValue(const char *value)
{
mStateOn = dAtob(value);
// Update the console variable:
if ( mConsoleVariable[0] )
Con::setBoolVariable( mConsoleVariable, mStateOn );
setUpdate();
}
//---------------------------------------------------------------------------
const char *GuiButtonBaseCtrl::getScriptValue()
{
return mStateOn ? "1" : "0";
}
//---------------------------------------------------------------------------
void GuiButtonBaseCtrl::onAction()
{
if(!mActive)
return;
if(mButtonType == ButtonTypeCheck)
{
mStateOn = mStateOn ? false : true;
// Update the console variable:
if ( mConsoleVariable[0] )
Con::setBoolVariable( mConsoleVariable, mStateOn );
// Execute the console command (if any)
if( mConsoleCommand[0] )
Con::evaluate( mConsoleCommand, false );
}
else if(mButtonType == ButtonTypeRadio)
{
mStateOn = true;
messageSiblings(mRadioGroup);
}
setUpdate();
Parent::onAction();
}
//---------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMessage( GuiControl *sender, S32 msg )
{
Parent::onMessage(sender, msg);
if( mRadioGroup == msg && mButtonType == ButtonTypeRadio )
{
setUpdate();
mStateOn = ( sender == this );
}
}

View File

@ -0,0 +1,69 @@
//-----------------------------------------------------------------------------
// Torque Engine
//
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIBUTTONBASECTRL_H_
#define _GUIBUTTONBASECTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
// all the button functionality is moving into buttonBase
// thus, all subclasses will just be rendering classes,
// and radios and check boxes can be done using pushbuttons
// or bitmap buttons.
class GuiButtonBaseCtrl : public GuiControl
{
typedef GuiControl Parent;
protected:
StringTableEntry mButtonText;
StringTableEntry mButtonTextID;
bool mDepressed;
bool mMouseOver;
bool mStateOn;
S32 mButtonType;
S32 mRadioGroup;
public:
enum {
ButtonTypePush,
ButtonTypeCheck,
ButtonTypeRadio,
};
GuiButtonBaseCtrl();
bool onWake();
DECLARE_CONOBJECT(GuiButtonBaseCtrl);
static void initPersistFields();
void setText(const char *text);
void setTextID(S32 id);
void setTextID(const char *id);
const char *getText();
void acceleratorKeyPress(U32 index);
void acceleratorKeyRelease(U32 index);
void onMouseDown(const GuiEvent &);
void onMouseUp(const GuiEvent &);
void onMouseEnter(const GuiEvent &);
void onMouseLeave(const GuiEvent &);
bool onKeyDown(const GuiEvent &event);
bool onKeyUp(const GuiEvent &event);
void setScriptValue(const char *value);
const char *getScriptValue();
void onMessage(GuiControl *,S32 msg);
void onAction();
};
#endif

View File

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "platform/platformAudio.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiButtonCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
IMPLEMENT_CONOBJECT(GuiButtonCtrl);
GuiButtonCtrl::GuiButtonCtrl()
{
mBounds.extent.set(140, 30);
mButtonText = StringTable->insert("Button");
}
//--------------------------------------------------------------------------
void GuiButtonCtrl::onRender(Point2I offset,
const RectI& updateRect)
{
bool highlight = mMouseOver;
bool depressed = mDepressed;
ColorI fontColor = mActive ? (highlight ? mProfile->mFontColorHL : mProfile->mFontColor) : mProfile->mFontColorNA;
ColorI backColor = mActive ? mProfile->mFillColor : mProfile->mFillColorNA;
ColorI borderColor = mActive ? mProfile->mBorderColor : mProfile->mBorderColorNA;
RectI boundsRect(offset, mBounds.extent);
if( mProfile->mBorder != 0 )
{
if (mDepressed || mStateOn)
renderFilledBorder( boundsRect, mProfile->mBorderColorHL, mProfile->mFillColorHL );
else
renderFilledBorder( boundsRect, mProfile->mBorderColor, mProfile->mFillColor );
}
Point2I textPos = offset;
if(depressed)
textPos += Point2I(1,1);
dglSetBitmapModulation( fontColor );
renderJustifiedText(textPos, mBounds.extent, mButtonText);
//render the children
renderChildControls( offset, updateRect);
}

View File

@ -0,0 +1,24 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIBUTTONCTRL_H_
#define _GUIBUTTONCTRL_H_
#ifndef _GUIBUTTONBASECTRL_H_
#include "gui/controls/guiButtonBaseCtrl.h"
#endif
class GuiButtonCtrl : public GuiButtonBaseCtrl
{
typedef GuiButtonBaseCtrl Parent;
public:
DECLARE_CONOBJECT(GuiButtonCtrl);
GuiButtonCtrl();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif //_GUI_BUTTON_CTRL_H

View File

@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiCheckBoxCtrl.h"
#include "console/consoleTypes.h"
IMPLEMENT_CONOBJECT(GuiCheckBoxCtrl);
//---------------------------------------------------------------------------
GuiCheckBoxCtrl::GuiCheckBoxCtrl()
{
mBounds.extent.set(140, 30);
mStateOn = false;
mIndent = 0;
mButtonType = ButtonTypeCheck;
}
//---------------------------------------------------------------------------
bool GuiCheckBoxCtrl::onWake()
{
if(!Parent::onWake())
return false;
// make sure there is a bitmap array for this control type
// if it is declared as such in the control
mProfile->constructBitmapArray();
return true;
}
//---------------------------------------------------------------------------
void GuiCheckBoxCtrl::onRender(Point2I offset, const RectI &updateRect)
{
ColorI backColor = mActive ? mProfile->mFillColor : mProfile->mFillColorNA;
ColorI fontColor = mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor;
ColorI insideBorderColor = isFirstResponder() ? mProfile->mBorderColorHL : mProfile->mBorderColor;
// just draw the check box and the text:
S32 xOffset = 0;
dglClearBitmapModulation();
if(mProfile->mBitmapArrayRects.size() >= 4)
{
S32 index = mStateOn;
if(!mActive)
index = 2;
else if(mDepressed)
index += 2;
xOffset = mProfile->mBitmapArrayRects[0].extent.x + 2 + mIndent;
S32 y = (mBounds.extent.y - mProfile->mBitmapArrayRects[0].extent.y) / 2;
dglDrawBitmapSR(mProfile->mTextureHandle, offset + Point2I(mIndent, y), mProfile->mBitmapArrayRects[index]);
}
if(mButtonText[0] != NULL)
{
dglSetBitmapModulation( fontColor );
renderJustifiedText(Point2I(offset.x + xOffset, offset.y),
Point2I(mBounds.extent.x - mBounds.extent.y, mBounds.extent.y),
mButtonText);
}
//render the children
renderChildControls(offset, updateRect);
}
//---------------------------------------------------------------------------
// EOF //

View File

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUICHECKBOXCTRL_H_
#define _GUICHECKBOXCTRL_H_
#ifndef _GUIBUTTONBASECTRL_H_
#include "gui/controls/guiButtonBaseCtrl.h"
#endif
class GuiCheckBoxCtrl : public GuiButtonBaseCtrl
{
typedef GuiButtonBaseCtrl Parent;
protected:
public:
S32 mIndent;
DECLARE_CONOBJECT(GuiCheckBoxCtrl);
GuiCheckBoxCtrl();
void onRender(Point2I offset, const RectI &updateRect);
bool onWake();
};
#endif //_GUI_CHECKBOX_CTRL_H

View File

@ -0,0 +1,390 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "platform/platformAudio.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiButtonCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/controls/guiColorPicker.h"
/// @name Common colors we use
/// @{
ColorF colorWhite(1.,1.,1.);
ColorF colorWhiteBlend(1.,1.,1.,.75);
ColorF colorBlack(.0,.0,.0);
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);
//--------------------------------------------------------------------------
GuiColorPickerCtrl::GuiColorPickerCtrl()
{
mBounds.extent.set(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;
}
//--------------------------------------------------------------------------
static EnumTable::Enums gColorPickerModeEnums[] =
{
{ GuiColorPickerCtrl::pPallet, "Pallet" },
{ GuiColorPickerCtrl::pHorizColorRange, "HorizColor"},
{ GuiColorPickerCtrl::pVertColorRange, "VertColor" },
{ GuiColorPickerCtrl::pBlendColorRange, "BlendColor"},
{ GuiColorPickerCtrl::pHorizAlphaRange, "HorizAlpha"},
{ GuiColorPickerCtrl::pVertAlphaRange, "VertAlpha" },
{ GuiColorPickerCtrl::pDropperBackground, "Dropper" },
};
static EnumTable gColorPickerModeTable( 7, gColorPickerModeEnums );
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("ColorPicker");
addField("BaseColor", TypeColorF, Offset(mBaseColor, GuiColorPickerCtrl));
addField("PickColor", TypeColorF, Offset(mPickColor, GuiColorPickerCtrl));
addField("SelectorGap", TypeS32, Offset(mSelectorGap, GuiColorPickerCtrl));
addField("DisplayMode", TypeEnum, Offset(mDisplayMode, GuiColorPickerCtrl), 1, &gColorPickerModeTable );
addField("ActionOnMove", TypeBool,Offset(mActionOnMove, GuiColorPickerCtrl));
endGroup("ColorPicker");
}
//--------------------------------------------------------------------------
// Function to draw a box which can have 4 different colors in each corner blended together
void dglDrawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glColor4f(c1.red, c1.green, c1.blue, c1.alpha);
glVertex2f(l, t);
glColor4f(c2.red, c2.green, c2.blue, c2.alpha);
glVertex2f(r, t);
glColor4f(c3.red, c3.green, c3.blue, c3.alpha);
glVertex2f(r, b);
glColor4f(c4.red, c4.green, c4.blue, c4.alpha);
glVertex2f(l, b);
glEnd();
}
//--------------------------------------------------------------------------
/// Function to draw a set of boxes blending throughout an array of colors
void dglDrawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
// Calculate increment value
S32 x_inc = int(mFloor((r - l) / (numColors-1)));
S32 y_inc = int(mFloor((b - t) / (numColors-1)));
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
for (U16 i=0;i<numColors-1;i++)
{
// If we are at the end, x_inc and y_inc need to go to the end (otherwise there is a rendering bug)
if (i == numColors-2)
{
x_inc += r-l-1;
y_inc += b-t-1;
}
if (!vertical) // Horizontal (+x)
{
// First color
glColor4ub(colors[i].red, colors[i].green, colors[i].blue, colors[i].alpha);
glVertex2f(l, t);
glVertex2f(l, b);
// Second color
glColor4ub(colors[i+1].red, colors[i+1].green, colors[i+1].blue, colors[i+1].alpha);
glVertex2f(l+x_inc, b);
glVertex2f(l+x_inc, t);
l += x_inc;
}
else // Vertical (+y)
{
// First color
glColor4ub(colors[i].red, colors[i].green, colors[i].blue, colors[i].alpha);
glVertex2f(l, t);
glVertex2f(r, t);
// Second color
glColor4ub(colors[i+1].red, colors[i+1].green, colors[i+1].blue, colors[i+1].alpha);
glVertex2f(r, t+y_inc);
glVertex2f(l, t+y_inc);
t += y_inc;
}
}
glEnd();
}
void GuiColorPickerCtrl::drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode)
{
U16 sMax = mSelectorGap*2;
switch (mode)
{
case sVertical:
// Now draw the vertical selector
// Up -> Pos
if (selectorPos.y != bounds.point.y+1)
dglDrawLine(selectorPos.x, bounds.point.y, selectorPos.x, selectorPos.y-sMax-1, colorWhiteBlend);
// Down -> Pos
if (selectorPos.y != bounds.point.y+bounds.extent.y)
dglDrawLine(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)
dglDrawLine(bounds.point.x, selectorPos.y-1, selectorPos.x-sMax, selectorPos.y-1, colorWhiteBlend);
// Right -> Pos
if (selectorPos.x != bounds.point.x)
dglDrawLine(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)
dglDrawRect(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
switch (mDisplayMode)
{
case pHorizColorRange:
dglDrawBlendRangeBox( pickerBounds, false, 7, mColorRange);
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertColorRange:
dglDrawBlendRangeBox( pickerBounds, true, 7, mColorRange);
drawSelector( pickerBounds, selectorPos, sHorizontal );
break;
case pHorizAlphaRange:
dglDrawBlendBox( pickerBounds, colorBlack, colorWhite, colorWhite, colorBlack );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertAlphaRange:
dglDrawBlendBox( pickerBounds, colorBlack, colorBlack, colorWhite, colorWhite );
drawSelector( pickerBounds, selectorPos, sHorizontal );
break;
case pBlendColorRange:
dglDrawBlendBox( pickerBounds, colorWhite, mBaseColor, colorBlack, colorBlack );
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pDropperBackground:
break;
case pPallet:
default:
dglDrawRectFill( pickerBounds, mBaseColor );
break;
}
}
void GuiColorPickerCtrl::onRender(Point2I offset, const RectI& updateRect)
{
RectI boundsRect(offset, mBounds.extent);
renderColorBox(boundsRect);
if (mPositionChanged)
{
mPositionChanged = false;
Point2I extent = Canvas->getExtent();
// If we are anything but a pallete, change the pick color
if (mDisplayMode != pPallet)
{
// To simplify things a bit, just read the color via glReadPixels()
GLfloat rBuffer[4];
glReadBuffer(GL_BACK);
U32 buf_x = offset.x+mSelectorPos.x+1;
U32 buf_y = extent.y-(offset.y+mSelectorPos.y+1);
glReadPixels(buf_x, buf_y, 1, 1, GL_RGBA, GL_FLOAT, rBuffer);
mPickColor.red = rBuffer[0];
mPickColor.green = rBuffer[1];
mPickColor.blue = rBuffer[2];
mPickColor.alpha = rBuffer[3];
// Now do onAction() if we are allowed
if (mActionOnMove)
onAction();
}
}
//render the children
renderChildControls( offset, updateRect);
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::setSelectorPos(const Point2I &pos)
{
Point2I extent = mBounds.extent;
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();
}
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseDown(const GuiEvent &event)
{
if (!mActive)
return;
if (mDisplayMode == pDropperBackground)
return;
if (mProfile->mCanKeyFocus)
setFirstResponder();
// 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));
}
}
//--------------------------------------------------------------------------
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;
mMouseDown = false;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseUp(const GuiEvent &)
{
//if we released the mouse within this control, perform the action
if (mActive && mMouseDown && (mDisplayMode != pDropperBackground))
{
onAction();
mMouseDown = false;
}
else if (mActive && (mDisplayMode == pDropperBackground))
{
// In a dropper, the alt command executes the mouse up action (to signal stopping)
if ( mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand, false );
}
}
//--------------------------------------------------------------------------
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,121 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUICOLORPICKER_H_
#define _GUICOLORPICKER_H_
#ifndef _GUICONTROL_H_
#include "gui/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
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
/// @}
/// @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.
static ColorI mColorRange[7]; ///< Color range for pHorizColorRange and pVertColorRange
/// @}
public:
DECLARE_CONOBJECT(GuiColorPickerCtrl);
GuiColorPickerCtrl();
static void initPersistFields();
void onRender(Point2I offset, const RectI &updateRect);
/// @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 &);
/// @}
};
#endif

106
engine/gui/controls/guiConsole.cc Executable file
View File

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiTypes.h"
#include "gui/core/guiControl.h"
#include "gui/controls/guiConsole.h"
#include "gui/containers/guiScrollCtrl.h"
IMPLEMENT_CONOBJECT(GuiConsole);
GuiConsole::GuiConsole()
{
mBounds.extent.set(1, 1);
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 = mBounds.extent.y / 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
resize(mBounds.point, 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: dglSetBitmapModulation(mProfile->mFontColor); break;
case ConsoleLogEntry::Warning: dglSetBitmapModulation(mProfile->mFontColorHL); break;
case ConsoleLogEntry::Error: dglSetBitmapModulation(mProfile->mFontColorNA); break;
}
dglDrawText(mFont, Point2I(offset.x + 3, offset.y), entry.mString, mProfile->mFontColors);
Con::unlockLog();
}

View File

@ -0,0 +1,32 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUICONSOLE_H_
#define _GUICONSOLE_H_
#ifndef _GUIARRAYCTRL_H_
#include "gui/core/guiArrayCtrl.h"
#endif
class GuiConsole : public GuiArrayCtrl
{
private:
typedef GuiArrayCtrl Parent;
Resource<GFont> mFont;
S32 getMaxWidth(S32 startIndex, S32 endIndex);
public:
GuiConsole();
DECLARE_CONOBJECT(GuiConsole);
bool onWake();
void onPreRender();
void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
};
#endif

View File

@ -0,0 +1,89 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiConsoleEditCtrl.h"
#include "core/frameAllocator.h"
IMPLEMENT_CONOBJECT(GuiConsoleEditCtrl);
GuiConsoleEditCtrl::GuiConsoleEditCtrl()
{
mSinkAllKeyEvents = true;
mSiblingScroller = NULL;
mUseSiblingScroller = true;
}
void GuiConsoleEditCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField("useSiblingScroller", TypeBool, Offset(mUseSiblingScroller, GuiConsoleEditCtrl));
endGroup("Misc");
}
bool GuiConsoleEditCtrl::onKeyDown(const GuiEvent &event)
{
S32 stringLen = dStrlen(mText);
setUpdate();
if (event.keyCode == KEY_TAB)
{
if (event.modifier & SI_SHIFT)
{
// Do some skullduggery so that we're working against the mTextBuffer.
FrameTemp<UTF8> tmpBuff(mTextBuffer.length() * 3 + 1);
mTextBuffer.get(tmpBuff, mTextBuffer.length() * 3 + 1);
mCursorPos = Con::tabComplete(tmpBuff, mCursorPos, GuiTextCtrl::MAX_STRING_LENGTH, false);
mTextBuffer.set(tmpBuff);
return true;
}
else
{
FrameTemp<UTF8> tmpBuff(GuiTextCtrl::MAX_STRING_LENGTH + 1);
mTextBuffer.get(tmpBuff, GuiTextCtrl::MAX_STRING_LENGTH + 1);
mCursorPos = Con::tabComplete(tmpBuff, mCursorPos, GuiTextCtrl::MAX_STRING_LENGTH, true);
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)
{
if (mSiblingScroller = dynamic_cast<GuiScrollCtrl*>(*itr))
{
return mSiblingScroller->onKeyDown(event);
}
}
}
// No luck... so don't try, next time.
mUseSiblingScroller = false;
}
}
}
return Parent::onKeyDown(event);
}

View File

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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);
static void initPersistFields();
bool onKeyDown(const GuiEvent &event);
};
#endif //_GUI_TEXTEDIT_CTRL_H

View File

@ -0,0 +1,158 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "core/color.h"
#include "gui/controls/guiConsoleTextCtrl.h"
#include "dgl/dgl.h"
IMPLEMENT_CONOBJECT(GuiConsoleTextCtrl);
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
GuiConsoleTextCtrl::GuiConsoleTextCtrl()
{
//default fonts
mConsoleExpression = NULL;
mResult = NULL;
}
void GuiConsoleTextCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField("expression", TypeCaseString, Offset(mConsoleExpression, GuiConsoleTextCtrl));
endGroup("Misc");
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
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 = StringTable->insert(txt);
else
mConsoleExpression = NULL;
//Make sure we have a font
mProfile->incRefCount();
mFont = mProfile->mFont;
setUpdate();
//decrement the profile referrence
mProfile->decRefCount();
}
void GuiConsoleTextCtrl::calcResize()
{
if (!mResult)
return;
//resize
if (mProfile->mAutoSizeWidth)
{
if (mProfile->mAutoSizeHeight)
resize(mBounds.point, Point2I(mFont->getStrWidth((const UTF8 *)mResult) + 4, mFont->getHeight() + 4));
else
resize(mBounds.point, Point2I(mFont->getStrWidth((const UTF8 *)mResult) + 4, mBounds.extent.y));
}
else if (mProfile->mAutoSizeHeight)
{
resize(mBounds.point, Point2I(mBounds.extent.x, mFont->getHeight() + 4));
}
}
void GuiConsoleTextCtrl::onPreRender()
{
if (mConsoleExpression)
mResult = Con::evaluatef("$temp = %s;", mConsoleExpression);
calcResize();
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
void GuiConsoleTextCtrl::onRender(Point2I offset, const RectI &updateRect)
{
// draw the background rectangle
RectI r(offset, mBounds.extent);
dglDrawRectFill(r, ColorI(255,255,255));
// draw the border
r.extent += r.point;
glColor4ub(0, 0, 0, 0);
glBegin(GL_LINE_LOOP);
glVertex2i(r.point.x, r.point.y);
glVertex2i(r.extent.x-1, r.point.y);
glVertex2i(r.extent.x-1, r.extent.y-1);
glVertex2i(r.point.x, r.extent.y-1);
glEnd();
if (mResult)
{
S32 txt_w = mFont->getStrWidth((const UTF8 *)mResult);
Point2I localStart;
switch (mProfile->mAlignment)
{
case GuiControlProfile::RightJustify:
localStart.set(mBounds.extent.x - txt_w-2, 0);
break;
case GuiControlProfile::CenterJustify:
localStart.set((mBounds.extent.x - txt_w) / 2, 0);
break;
default:
// GuiControlProfile::LeftJustify
localStart.set(2,0);
break;
}
Point2I globalStart = localToGlobalCoord(localStart);
//draw the text
dglSetBitmapModulation(mProfile->mFontColor);
dglDrawText(mFont, globalStart, mResult, mProfile->mFontColors);
}
//render the child controls
renderChildControls(offset, updateRect);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
const char *GuiConsoleTextCtrl::getScriptValue()
{
return getText();
}
void GuiConsoleTextCtrl::setScriptValue(const char *val)
{
setText(val);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
// EOF //

View File

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUICONSOLETEXTCTRL_H_
#define _GUICONSOLETEXTCTRL_H_
#ifndef _GFONT_H_
#include "dgl/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:
const char *mConsoleExpression;
const char *mResult;
Resource<GFont> mFont;
public:
//creation methods
DECLARE_CONOBJECT(GuiConsoleTextCtrl);
GuiConsoleTextCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
//text methods
virtual void setText(const char *txt = NULL);
const char *getText() { return mConsoleExpression; }
//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,108 @@
#include "core/findMatch.h"
#include "gui/controls/guiDirectoryFileListCtrl.h"
IMPLEMENT_CONOBJECT( GuiDirectoryFileListCtrl );
GuiDirectoryFileListCtrl::GuiDirectoryFileListCtrl()
{
}
bool GuiDirectoryFileListCtrl::onWake()
{
if( !Parent::onWake() )
return false;
setCurrentPath( "/", "*.*" );
return true;
}
void GuiDirectoryFileListCtrl::onMouseDown(const GuiEvent &event)
{
Parent::onMouseDown( event );
if( event.mouseClickCount == 2 && getNamespace() )
Con::executef(this, 1, "onDoubleClick");
}
void GuiDirectoryFileListCtrl::openDirectory()
{
Vector<Platform::FileInfo> fileVector;
Platform::dumpPath( mFilePath, fileVector, 0 );
// Clear the current file listing
clear();
// 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( FindMatch::isMatchMultipleExprs( mFilter, (*i).pFileName,false ) )
addEntry( j++, (*i).pFileName );
}
//Done!
}
bool GuiDirectoryFileListCtrl::setCurrentPath( StringTableEntry path, StringTableEntry filter )
{
// Oops, gotta give us a path to work with
if( !path )
return false;
char ExpandedPath[512];
char FullPath[512];
dMemset( ExpandedPath, 0, 512 );
dMemset( FullPath, 0, 512 );
Con::expandScriptFilename( ExpandedPath, 512, path );
if( ExpandedPath[0] != '/' )
dSprintf( FullPath, 512, "%s/%s", Platform::getWorkingDirectory(), ExpandedPath );
else
dSprintf( FullPath, 512, "%s%s", Platform::getWorkingDirectory(), ExpandedPath );
// Platform::isDirectory expects no trailing / so make sure we conform
if( FullPath[ dStrlen( FullPath ) - 1 ] == '/' )
FullPath[ dStrlen( FullPath ) - 1 ] = 0x00;
if( ! filter )
filter = StringTable->insert("*.*");
// A bad path!? For shame...
if( !Platform::isDirectory( FullPath ) && !Platform::hasSubDirectory( FullPath ) )
return false;
// Store our new info
mFilePath = StringTable->insert( FullPath );
mFilter = StringTable->insert( filter );
// Update our view
openDirectory();
// Peace out!
return true;
}
StringTableEntry GuiDirectoryFileListCtrl::getSelectedFileName()
{
return StringTable->insert( getSelectedText() );
}
ConsoleMethod( GuiDirectoryFileListCtrl, setPath, bool, 3, 4, "setPath(path,filter) - directory to enumerate files from (without trailing slash)" )
{
return object->setCurrentPath( argv[2], argv[3] );
}
ConsoleMethod( GuiDirectoryFileListCtrl, getSelectedFile, const char*, 2, 2, "getSelectedFile () - returns the currently selected file name" )
{
return object->getSelectedFileName();
}

View File

@ -0,0 +1,40 @@
#ifndef _GUI_DIRECTORYFILELISTCTRL_H_
#define _GUI_DIRECTORYFILELISTCTRL_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _GUITEXTLISTCTRL_H_
#include "gui/controls/guiTextListCtrl.h"
#endif
class GuiDirectoryFileListCtrl : public GuiTextListCtrl
{
private:
typedef GuiTextListCtrl Parent;
protected:
StringTableEntry mFilePath;
StringTableEntry mFilter;
void openDirectory();
public:
GuiDirectoryFileListCtrl();
DECLARE_CONOBJECT(GuiDirectoryFileListCtrl);
/// Set the current path to grab files from
bool setCurrentPath( StringTableEntry path, StringTableEntry filter );
bool setCurrentFilter( StringTableEntry filter );
/// Get the currently selected file's name
StringTableEntry getSelectedFileName();
virtual void onMouseDown(const GuiEvent &event);
bool onWake();
};
#endif

View File

@ -0,0 +1,382 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/controls/guiDirectoryTreeCtrl.h"
IMPLEMENT_CONOBJECT(GuiDirectoryTreeCtrl);
GuiDirectoryTreeCtrl::GuiDirectoryTreeCtrl(): GuiTreeViewCtrl()
{
// Parent configuration
mBounds.set( 0,0,200,100 );
mDestroyOnSleep = false;
mSupportMouseDragging = false;
mMultipleSelections = false;
mSelPath = StringTable->insert("");
}
bool GuiDirectoryTreeCtrl::onAdd()
{
if( !Parent::onAdd() )
return false;
// Kill off any existing items
destroyTree();
// Specify our icons
buildIconTable( NULL );
// 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.
ResourceManager->initExcludedDirectories();
StringTableEntry RootPath = ResourceManager->getModPaths();
//getUnit(argv[1], dAtoi(argv[2]), " \t\n");
S32 modCount = getUnitCount( RootPath, ";" );
for( S32 i = 0; i < modCount; i++ )
{
// Compose full mod path location, and dump the path to our vector
StringTableEntry currentMod = getUnit( RootPath, i, ";" );
char fullModPath [512];
dMemset( fullModPath, 0, 512 );
dSprintf( fullModPath, 512, "%s/%s/", Platform::getWorkingDirectory(), currentMod );
Vector<StringTableEntry> pathVec;
Platform::dumpDirectories( fullModPath, pathVec, 1, true);
if( ! pathVec.empty() )
{
// 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", currentMod, (*j) );
addPathToTree( fullModPathSub );
}
}
else
addPathToTree( fullModPath );
}
return true;
}
bool GuiDirectoryTreeCtrl::onWake()
{
if( !Parent::onWake() )
return false;
// Success!
return true;
}
bool GuiDirectoryTreeCtrl::onVirtualParentExpand(Item *item)
{
if( !item || !item->isExpanded() )
return true;
StringTableEntry pathToExpand = item->getValue();
if( !pathToExpand )
{
Con::errorf("GuiDirectoryTreeCtrl::onVirtualParentExpand - Unable to retrieve item value!");
return false;
}
Vector<StringTableEntry> pathVec;
Platform::dumpDirectories( pathToExpand, pathVec, 1, 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;
}
bool GuiDirectoryTreeCtrl::buildIconTable(const char * icons)
{
// Icons should be designated by the bitmap/png file names (minus the file extensions)
// and separated by colons (:).
if (!icons)
icons = StringTable->insert("common/ui/bs:common/ui/folder:common/ui/folder_closed");
return Parent::buildIconTable( icons );
}
void GuiDirectoryTreeCtrl::addPathToTree( StringTableEntry path )
{
if( !path )
{
Con::errorf("GuiDirectoryTreeCtrl::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 = getUnit( path, 0, "//" );
// There are no current roots, we can safely create one
if( root == 0 )
{
recurseInsert( NULL, path );
}
else
{
while( root != 0 )
{
if( dStrcmp( 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 GuiDirectoryTreeCtrl::onItemSelected( Item *item )
{
Con::executef( this, 2, "onSelectPath", avar("%s",item->getValue()) );
mSelPath = StringTable->insert( item->getValue() );
if( Platform::hasSubDirectory( item->getValue() ) )
item->setVirtualParent( true );
}
void GuiDirectoryTreeCtrl::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 *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 );
getItem( itemIndex )->setNormalImage( Icon_FolderClosed );
getItem( itemIndex )->setExpandedImage( Icon_Folder );
}
else
{
Item *item = parent;
char *szValue = new char[ 1024 ];
dMemset( szValue, 0, 1024 );
dSprintf( szValue, 1024, "%s/%s", parent->getValue(), curPos );
Item *exists = item->findChildByValue( szValue );
if( !exists && dStrcmp( curPos, "" ) != 0 )
{
// Since we're adding a child this parent can't be a virtual parent, so clear that flag
item->setVirtualParent( false );
itemIndex = insertItem( item->getID(), curPos);
getItem( itemIndex )->setValue( szValue );
getItem( itemIndex )->setNormalImage( Icon_FolderClosed );
getItem( itemIndex )->setExpandedImage( Icon_Folder );
}
else
{
delete []szValue;
itemIndex = ( item != NULL ) ? ( ( exists != NULL ) ? exists->getID() : -1 ) : -1;
}
}
// 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( delim )
{
if( ( dStrcmp( delim, "" ) == 0 ) && item )
{
item->setExpanded( false );
if( parent && Platform::hasSubDirectory( item->getValue() ) )
item->setVirtualParent( true );
}
}
else
{
if( item )
{
item->setExpanded( false );
if( parent && Platform::hasSubDirectory( item->getValue() ) )
item->setVirtualParent( true );
}
}
// Down the rabbit hole we go
recurseInsert( getItem( itemIndex ), delim );
}
StringTableEntry GuiDirectoryTreeCtrl::getUnit(const char *string, U32 index, const char *set)
{
U32 sz;
while(index--)
{
if(!*string)
return "";
sz = dStrcspn(string, set);
if (string[sz] == 0)
return "";
string += (sz + 1);
}
sz = dStrcspn(string, set);
if (sz == 0)
return "";
char *ret = Con::getReturnBuffer(sz+1);
dStrncpy(ret, string, sz);
ret[sz] = '\0';
return ret;
}
StringTableEntry GuiDirectoryTreeCtrl::getUnits(const char *string, S32 startIndex, S32 endIndex, const char *set)
{
S32 sz;
S32 index = startIndex;
while(index--)
{
if(!*string)
return "";
sz = dStrcspn(string, set);
if (string[sz] == 0)
return "";
string += (sz + 1);
}
const char *startString = string;
while(startIndex <= endIndex--)
{
sz = dStrcspn(string, set);
string += sz;
if (*string == 0)
break;
string++;
}
if(!*string)
string++;
U32 totalSize = (U32(string - startString));
char *ret = Con::getReturnBuffer(totalSize);
dStrncpy(ret, startString, totalSize - 1);
ret[totalSize-1] = '\0';
return ret;
}
U32 GuiDirectoryTreeCtrl::getUnitCount(const char *string, const char *set)
{
U32 count = 0;
U8 last = 0;
while(*string)
{
last = *string++;
for(U32 i =0; set[i]; i++)
{
if(last == set[i])
{
count++;
last = 0;
break;
}
}
}
if(last)
count++;
return count;
}
ConsoleMethod( GuiDirectoryTreeCtrl, getSelectedPath, const char*, 2,2, "getSelectedPath() - returns the currently selected path in the tree")
{
return object->getSelectedPath();
}
StringTableEntry GuiDirectoryTreeCtrl::getSelectedPath()
{
return mSelPath;
}
ConsoleMethod( GuiDirectoryTreeCtrl, setSelectedPath, bool, 3, 3, "setSelectedPath(path) - expands the tree to the specified path")
{
return object->setSelectedPath( argv[2] );
}
bool GuiDirectoryTreeCtrl::setSelectedPath( StringTableEntry 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( Platform::isDirectory( 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,"GuiDirectoryTreeCtrl::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,58 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUI_DIRECTORYTREECTRL_H_
#define _GUI_DIRECTORYTREECTRL_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
#ifndef _GUI_TREEVIEWCTRL_H
#include "gui/controls/guiTreeViewCtrl.h"
#endif
class GuiDirectoryTreeCtrl : public GuiTreeViewCtrl
{
private:
typedef GuiTreeViewCtrl Parent;
// Utility functions
void recurseInsert( Item* parent, StringTableEntry path );
void addPathToTree( StringTableEntry path );
protected:
StringTableEntry mSelPath;
public:
enum
{
Icon_Folder = 1,
Icon_FolderClosed
};
GuiDirectoryTreeCtrl();
bool onWake();
bool onAdd();
bool onVirtualParentExpand(Item *item);
void onItemSelected( Item *item );
StringTableEntry getSelectedPath();
bool setSelectedPath( StringTableEntry path );
bool buildIconTable(const char * icons);
// Mod Path Parsing
StringTableEntry getUnit(const char *string, U32 index, const char *set);
StringTableEntry getUnits(const char *string, S32 startIndex, S32 endIndex, const char *set);
U32 getUnitCount(const char *string, const char *set);
DECLARE_CONOBJECT(GuiDirectoryTreeCtrl);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,275 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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 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 {
const char *bitmapName;
U32 bitmapNameLen;
TextureHandle bitmapHandle;
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();
// 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);
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(char *bitmapName, U32 bitmapNameLen);
Font *allocFont(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;
U32 mMinSensibleWidth;
// Console settable parameters
U32 mLineSpacingPixels;
bool mAllowColorChars;
// Too many chars sound:
AudioProfile* 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 maintainance
bool isSelectionActive() const;
void clearSelection();
// Pixel -> textposition mappings
S32 getTextPosition(const Point2I& localPosition);
// Gui control overrides
bool onAdd();
bool onWake();
void onSleep();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void getCursorPositionAndColor(Point2I &cursorTop, Point2I &cursorBottom, ColorI &color);
void inspectPostApply();
void resize(const Point2I &newPosition, const Point2I &newExtent);
void parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent);
bool onKeyDown(const GuiEvent& event);
void onMouseDown(const GuiEvent&);
void onMouseDragged(const GuiEvent&);
void onMouseUp(const GuiEvent&);
public:
void setSelectionStart( U32 start ) { clearSelection(); mSelectionStart = start; };
void setSelectionEnd( U32 end ) { mSelectionEnd = end;};
void setSelectionActive(bool active) { mSelectionActive = active; };
S32 getCursorPosition() { return( mCursorPosition ); }
};
#endif // _H_GUIMLTEXTCTRL_

View File

@ -0,0 +1,376 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/controls/guiMLTextEditCtrl.h"
#include "gui/containers/guiScrollCtrl.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "platform/event.h"
#include "core/frameAllocator.h"
#include "core/stringBuffer.h"
IMPLEMENT_CONOBJECT(GuiMLTextEditCtrl);
//--------------------------------------------------------------------------
GuiMLTextEditCtrl::GuiMLTextEditCtrl()
{
mEscapeCommand = StringTable->insert( "" );
mIsEditCtrl = true;
mActive = true;
mVertMoveAnchorValid = false;
}
//--------------------------------------------------------------------------
GuiMLTextEditCtrl::~GuiMLTextEditCtrl()
{
}
//--------------------------------------------------------------------------
void 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->mBounds.extent.y, newExt.y );
Parent::resize( newPosition, newExt );
}
//--------------------------------------------------------------------------
void GuiMLTextEditCtrl::initPersistFields()
{
Parent::initPersistFields();
addField( "escapeCommand", TypeString, Offset( mEscapeCommand, GuiMLTextEditCtrl ) );
}
//--------------------------------------------------------------------------
// Key events...
bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event)
{
setUpdate();
//handle modifiers first...
if (event.modifier & SI_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;
}
}
}
else if ( event.modifier & SI_SHIFT )
{
switch ( event.keyCode )
{
case KEY_TAB:
return( Parent::onKeyDown( event ) );
}
}
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;
}
}
if (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.get(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);
mCursorPosition = mSelectionStart;
}
else
{
switch ( event.keyCode )
{
case KEY_BACKSPACE:
if (mCursorPosition != 0)
{
// delete one character left
deleteChars(mCursorPosition-1, mCursorPosition-1);
setUpdate();
}
break;
case KEY_DELETE:
if (mCursorPosition != mTextBuffer.length())
{
// delete one character right
deleteChars(mCursorPosition, mCursorPosition);
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);
dglDrawLine(top + offset, bottom + offset, mProfile->mCursorColor);
}
}

View File

@ -0,0 +1,41 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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();
void resize(const Point2I &newPosition, const Point2I &newExtent);
DECLARE_CONOBJECT(GuiMLTextEditCtrl);
static void initPersistFields();
};
#endif // _H_GUIMLTEXTEDITCTRL_

View File

@ -0,0 +1,811 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiPopUpCtrl.h"
#include "console/consoleTypes.h"
#include "gui/core/guiDefaultControlRender.h"
IMPLEMENT_CONOBJECT(GuiPopUpMenuCtrl);
GuiPopUpBackgroundCtrl::GuiPopUpBackgroundCtrl(GuiPopUpMenuCtrl *ctrl, GuiPopUpTextListCtrl *textList)
{
mPopUpCtrl = ctrl;
mTextList = textList;
}
void GuiPopUpBackgroundCtrl::onMouseDown(const GuiEvent &event)
{
mTextList->setSelectedCell(Point2I(-1,-1));
mPopUpCtrl->closePopUp();
}
//------------------------------------------------------------------------------
GuiPopUpTextListCtrl::GuiPopUpTextListCtrl()
{
mPopUpCtrl = NULL;
}
//------------------------------------------------------------------------------
GuiPopUpTextListCtrl::GuiPopUpTextListCtrl(GuiPopUpMenuCtrl *ctrl)
{
mPopUpCtrl = ctrl;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void GuiPopUpTextListCtrl::onCellSelected( Point2I /*cell*/ )
{
// Do nothing, the parent control will take care of everything...
}
//------------------------------------------------------------------------------
bool GuiPopUpTextListCtrl::onKeyDown(const GuiEvent &event)
{
//if the control is a dead end, don't process the input:
if ( !mVisible || !mActive || !mAwake )
return false;
//see if the key down is a <return> or not
if ( event.modifier == 0 )
{
if ( event.keyCode == KEY_RETURN )
{
mPopUpCtrl->closePopUp();
return true;
}
else if ( event.keyCode == KEY_ESCAPE )
{
mSelectedCell.set( -1, -1 );
mPopUpCtrl->closePopUp();
return true;
}
}
//otherwise, pass the event to it's parent
return Parent::onKeyDown(event);
}
void GuiPopUpTextListCtrl::onMouseDown(const GuiEvent &event)
{
Parent::onMouseDown(event);
mPopUpCtrl->closePopUp();
}
//------------------------------------------------------------------------------
void GuiPopUpTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
{
ColorI fontColor;
mPopUpCtrl->getFontColor( fontColor, mList[cell.y].id, selected, mouseOver );
dglSetBitmapModulation( fontColor );
dglDrawText( mFont, Point2I( offset.x + 4, offset.y ), mList[cell.y].text );
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
GuiPopUpMenuCtrl::GuiPopUpMenuCtrl(void)
{
VECTOR_SET_ASSOCIATION(mEntries);
VECTOR_SET_ASSOCIATION(mSchemes);
mSelIndex = -1;
mActive = true;
mMaxPopupHeight = 200;
mScrollDir = GuiScrollCtrl::None;
mScrollCount = 0;
mLastYvalue = 0;
mIncValue = 0;
mRevNum = 0;
mInAction = false;
}
//------------------------------------------------------------------------------
GuiPopUpMenuCtrl::~GuiPopUpMenuCtrl()
{
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::initPersistFields(void)
{
Parent::initPersistFields();
addField("maxPopupHeight", TypeS32, Offset(mMaxPopupHeight, GuiPopUpMenuCtrl));
}
//------------------------------------------------------------------------------
ConsoleMethod( GuiPopUpMenuCtrl, add, void, 4, 5, "(string name, int idNum, int scheme=0)")
{
if ( argc > 4 )
object->addEntry(argv[2],dAtoi(argv[3]),dAtoi(argv[4]));
else
object->addEntry(argv[2],dAtoi(argv[3]),0);
}
ConsoleMethod( GuiPopUpMenuCtrl, addScheme, void, 6, 6, "(int id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL)")
{
ColorI fontColor, fontColorHL, fontColorSEL;
U32 r, g, b;
char buf[64];
dStrcpy( buf, argv[3] );
char* temp = dStrtok( buf, " \0" );
r = temp ? dAtoi( temp ) : 0;
temp = dStrtok( NULL, " \0" );
g = temp ? dAtoi( temp ) : 0;
temp = dStrtok( NULL, " \0" );
b = temp ? dAtoi( temp ) : 0;
fontColor.set( r, g, b );
dStrcpy( buf, argv[4] );
temp = dStrtok( buf, " \0" );
r = temp ? dAtoi( temp ) : 0;
temp = dStrtok( NULL, " \0" );
g = temp ? dAtoi( temp ) : 0;
temp = dStrtok( NULL, " \0" );
b = temp ? dAtoi( temp ) : 0;
fontColorHL.set( r, g, b );
dStrcpy( buf, argv[5] );
temp = dStrtok( buf, " \0" );
r = temp ? dAtoi( temp ) : 0;
temp = dStrtok( NULL, " \0" );
g = temp ? dAtoi( temp ) : 0;
temp = dStrtok( NULL, " \0" );
b = temp ? dAtoi( temp ) : 0;
fontColorSEL.set( r, g, b );
object->addScheme( dAtoi( argv[2] ), fontColor, fontColorHL, fontColorSEL );
}
ConsoleMethod( GuiPopUpMenuCtrl, setText, void, 3, 3, "(string text)")
{
object->setText(argv[2]);
}
ConsoleMethod( GuiPopUpMenuCtrl, getText, const char*, 2, 2, "")
{
return object->getText();
}
ConsoleMethod( GuiPopUpMenuCtrl, clear, void, 2, 2, "Clear the popup list.")
{
object->clear();
}
ConsoleMethod(GuiPopUpMenuCtrl, sort, void, 2, 2, "Sort the list alphabetically.")
{
object->sort();
}
ConsoleMethod( GuiPopUpMenuCtrl, forceOnAction, void, 2, 2, "")
{
object->onAction();
}
ConsoleMethod( GuiPopUpMenuCtrl, forceClose, void, 2, 2, "")
{
object->closePopUp();
}
ConsoleMethod( GuiPopUpMenuCtrl, getSelected, S32, 2, 2, "")
{
return object->getSelected();
}
ConsoleMethod( GuiPopUpMenuCtrl, setSelected, void, 3, 3, "(int id)")
{
object->setSelected(dAtoi(argv[2]));
}
ConsoleMethod( GuiPopUpMenuCtrl, getTextById, const char*, 3, 3, "(int id)")
{
return(object->getTextById(dAtoi(argv[2])));
}
ConsoleMethod( GuiPopUpMenuCtrl, setEnumContent, void, 4, 4, "(string class, string enum)"
"This fills the popup with a classrep's field enumeration type info.\n\n"
"More of a helper function than anything. If console access to the field list is added, "
"at least for the enumerated types, then this should go away..")
{
AbstractClassRep * classRep = AbstractClassRep::getClassList();
// walk the class list to get our class
while(classRep)
{
if(!dStricmp(classRep->getClassName(), argv[2]))
break;
classRep = classRep->getNextClass();
}
// get it?
if(!classRep)
{
Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", argv[2]);
return;
}
// walk the fields to check for this one (findField checks StringTableEntry ptrs...)
U32 i;
for(i = 0; i < classRep->mFieldList.size(); i++)
if(!dStricmp(classRep->mFieldList[i].pFieldname, argv[3]))
break;
// found it?
if(i == classRep->mFieldList.size())
{
Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", argv[3], argv[2]);
return;
}
const AbstractClassRep::Field & field = classRep->mFieldList[i];
// check the type
if(field.type != TypeEnum)
{
Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", argv[3], argv[2]);
return;
}
AssertFatal(field.table, avar("enumeration '%s' for class '%s' with NULL ", argv[3], argv[2]));
// fill it
for(i = 0; i < field.table->size; i++)
object->addEntry(field.table->table[i].label, field.table->table[i].index);
}
//------------------------------------------------------------------------------
ConsoleMethod( GuiPopUpMenuCtrl, findText, S32, 3, 3, "(string text)"
"Returns the position of the first entry containing the specified text.")
{
return( object->findText( argv[2] ) );
}
//------------------------------------------------------------------------------
ConsoleMethod( GuiPopUpMenuCtrl, size, S32, 2, 2, "Get the size of the menu - the number of entries in it.")
{
return( object->getNumEntries() );
}
//------------------------------------------------------------------------------
ConsoleMethod( GuiPopUpMenuCtrl, replaceText, void, 3, 3, "(bool doReplaceText)")
{
object->replaceText(dAtoi(argv[2]));
}
//------------------------------------------------------------------------------
bool GuiPopUpMenuCtrl::onAdd()
{
if (!Parent::onAdd())
return false;
mSelIndex = -1;
mReplaceText = true;
return true;
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::onSleep()
{
Parent::onSleep();
closePopUp(); // Tests in function.
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::clear()
{
mEntries.setSize(0);
setText("");
mSelIndex = -1;
mRevNum = 0;
}
//------------------------------------------------------------------------------
static S32 QSORT_CALLBACK textCompare(const void *a,const void *b)
{
GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a);
GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b);
return (dStricmp(ea->buf, eb->buf));
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::sort()
{
dQsort((void *)&(mEntries[0]), mEntries.size(), sizeof(Entry), textCompare);
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::addEntry(const char *buf, S32 id, U32 scheme)
{
Entry e;
dStrcpy(e.buf, buf);
e.id = id;
e.scheme = scheme;
// see if there is a shortcut key
char * cp = dStrchr(e.buf, '~');
e.ascii = cp ? cp[1] : 0;
mEntries.push_back(e);
if ( mInAction && mTl )
{
// Add the new entry:
mTl->addEntry( e.id, e.buf );
repositionPopup();
}
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::addScheme( U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL )
{
if ( !id )
return;
Scheme newScheme;
newScheme.id = id;
newScheme.fontColor = fontColor;
newScheme.fontColorHL = fontColorHL;
newScheme.fontColorSEL = fontColorSEL;
mSchemes.push_back( newScheme );
}
//------------------------------------------------------------------------------
S32 GuiPopUpMenuCtrl::getSelected()
{
if (mSelIndex == -1)
return 0;
return mEntries[mSelIndex].id;
}
//------------------------------------------------------------------------------
const char* GuiPopUpMenuCtrl::getTextById(S32 id)
{
for ( U32 i = 0; i < mEntries.size(); i++ )
{
if ( mEntries[i].id == id )
return( mEntries[i].buf );
}
return( "" );
}
//------------------------------------------------------------------------------
S32 GuiPopUpMenuCtrl::findText( const char* text )
{
for ( U32 i = 0; i < mEntries.size(); i++ )
{
if ( dStrcmp( text, mEntries[i].buf ) == 0 )
return( mEntries[i].id );
}
return( -1 );
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::setSelected(S32 id)
{
S32 i;
for (i = 0; U32(i) < mEntries.size(); i++)
if (id == mEntries[i].id)
{
i = (mRevNum > i) ? mRevNum - i : i;
mSelIndex = i;
setText(mEntries[i].buf);
// Now perform the popup action:
char idval[24];
dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
Con::executef( this, 3, "onSelect", idval, mEntries[mSelIndex].buf );
return;
}
setText("");
mSelIndex = -1;
Con::executef( this, 1, "onCancel" );
// Execute the popup console command:
if ( mConsoleCommand[0] )
Con::evaluate( mConsoleCommand, false );
}
//------------------------------------------------------------------------------
const char *GuiPopUpMenuCtrl::getScriptValue()
{
return getText();
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
{
updateRect;
Point2I localStart;
if(mScrollDir != GuiScrollCtrl::None)
autoScroll();
RectI r(offset, mBounds.extent);
RectI buttonRect( ( r.point.x + r.extent.x ) - 18, r.point.y + 2, 16, r.extent.y - 4);
if( mProfile->mBorder && mProfile->mOpaque)
{
if(mInAction)
{
renderFilledBorder(r, mProfile->mBorderColorHL, mProfile->mFillColor);
renderFilledBorder( buttonRect, mProfile->mBorderColorHL, mProfile->mFillColorNA);
}
else
{
renderFilledBorder(r, mProfile->mBorderColorHL, mProfile->mFillColor);
renderFilledBorder( buttonRect, mProfile->mBorderColorHL, mProfile->mFillColorNA);
}
}
S32 txt_w = mFont->getStrWidth((const UTF8 *)mText);
localStart.x = 0;
localStart.y = (mBounds.extent.y - (mFont->getHeight())) / 2;
// align the horizontal
switch (mProfile->mAlignment)
{
case GuiControlProfile::RightJustify:
localStart.x = mBounds.extent.x - txt_w;
break;
case GuiControlProfile::CenterJustify:
localStart.x = (mBounds.extent.x - txt_w) / 2;
break;
default: // GuiControlProfile::LeftJustify
localStart.x = 4;
break;
}
Point2I globalStart = localToGlobalCoord(localStart);
dglSetBitmapModulation(mProfile->mFontColor);
dglDrawText(mFont, globalStart, mText, mProfile->mFontColors);
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::closePopUp()
{
if ( !mInAction )
return;
// Get the selection from the text list:
mSelIndex = mTl->getSelectedCell().y;
mSelIndex = (mRevNum >= mSelIndex && mSelIndex != -1) ? mRevNum - mSelIndex : mSelIndex;
if ( mSelIndex != -1 )
{
if(mReplaceText)
setText( mEntries[mSelIndex].buf );
setIntVariable( mEntries[mSelIndex].id );
}
// Release the mouse:
mInAction = false;
mTl->mouseUnlock();
// Pop the background:
getRoot()->popDialogControl(mBackground);
// Kill the popup:
mBackground->removeObject( mSc );
mTl->deleteObject();
mSc->deleteObject();
mBackground->deleteObject();
// Set this as the first responder:
setFirstResponder();
// Now perform the popup action:
if ( mSelIndex != -1 )
{
char idval[24];
dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id );
Con::executef( this, 3, "onSelect", idval, mEntries[mSelIndex].buf );
}
else
Con::executef( this, 1, "onCancel" );
// Execute the popup console command:
if ( mConsoleCommand[0] )
Con::evaluate( mConsoleCommand, false );
}
//------------------------------------------------------------------------------
bool GuiPopUpMenuCtrl::onKeyDown(const GuiEvent &event)
{
//if the control is a dead end, don't process the input:
if ( !mVisible || !mActive || !mAwake )
return false;
//see if the key down is a <return> or not
if ( event.keyCode == KEY_RETURN && event.modifier == 0 )
{
onAction();
return true;
}
S32 selected = mSelIndex;
switch( event.keyCode )
{
case KEY_RIGHT:
case KEY_DOWN:
if( ( selected + 1 ) < mEntries.size() )
setSelected( mEntries[selected + 1].id );
break;
case KEY_UP:
case KEY_LEFT:
if( ( selected - 1 ) > 0 )
setSelected( mEntries[selected - 1].id );
break;
}
//otherwise, pass the event to its parent
return Parent::onKeyDown( event );
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::onAction()
{
GuiControl *canCtrl = getParent();
addChildren();
GuiCanvas *root = getRoot();
Point2I windowExt = root->mBounds.extent;
mBackground->mBounds.point.set(0,0);
mBackground->mBounds.extent = root->mBounds.extent;
S32 textWidth = 0, width = mBounds.extent.x;
const S32 menuSpace = 5;
const S32 textSpace = 2;
bool setScroll = false;
for(U32 i=0; i<mEntries.size(); ++i)
if(S32(mFont->getStrWidth((const UTF8 *)mEntries[i].buf)) > textWidth)
textWidth = mFont->getStrWidth((const UTF8 *)mEntries[i].buf);
if(textWidth > mBounds.extent.x)
{
textWidth +=10;
width = textWidth;
}
mTl->setCellSize(Point2I(width, mFont->getHeight()+3));
for(U32 j=0; j<mEntries.size(); ++j)
mTl->addEntry(mEntries[j].id, mEntries[j].buf);
Point2I pointInGC = canCtrl->localToGlobalCoord(mBounds.point);
Point2I scrollPoint(pointInGC.x, pointInGC.y + mBounds.extent.y);
//Calc max Y distance, so Scroll Ctrl will fit on window
S32 maxYdis = windowExt.y - pointInGC.y - mBounds.extent.y - menuSpace;
//If scroll bars need to be added
if(maxYdis < mTl->mBounds.extent.y + textSpace)
{
//Should we pop menu list above the button
if(maxYdis < pointInGC.y - menuSpace)
{
reverseTextList();
maxYdis = pointInGC.y - menuSpace;
//Does the menu need a scroll bar
if(maxYdis < mTl->mBounds.extent.y + textSpace)
{
//Calc for the width of the scroll bar
if(textWidth >= width)
width += 20;
mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace));
//Pop menu list above the button
scrollPoint.set(pointInGC.x, menuSpace - 1);
setScroll = true;
}
//No scroll bar needed
else
{
maxYdis = mTl->mBounds.extent.y + textSpace;
scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis -1);
}
}
//Scroll bar needed but Don't pop above button
else
{
mRevNum = 0;
//Calc for the width of the scroll bar
if(textWidth >= width)
width += 20;
mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace));
setScroll = true;
}
}
//No scroll bar needed
else
maxYdis = mTl->mBounds.extent.y + textSpace;
//offset it from the background so it lines up properly
mSc->mBounds.point = mBackground->globalToLocalCoord(scrollPoint);
if(mSc->mBounds.point.x + width > mBackground->mBounds.extent.x)
if(width - mBounds.extent.x > 0)
mSc->mBounds.point.x -= width - mBounds.extent.x;
mSc->mBounds.extent.set(width-1, maxYdis);
mSc->registerObject();
mTl->registerObject();
mBackground->registerObject();
mSc->addObject( mTl );
mBackground->addObject( mSc );
// JDD - push the popup dialog to the topmost layer, so it's never under anything
root->pushDialogControl(mBackground,99);
if ( setScroll )
{
if ( mSelIndex )
mTl->scrollCellVisible( Point2I(0, mSelIndex));
else
mTl->scrollCellVisible( Point2I( 0, 0 ) );
}
mTl->setFirstResponder();
mInAction = true;
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::addChildren()
{
mTl = new GuiPopUpTextListCtrl(this);
AssertFatal(mTl, "Failed to create the GuiPopUpTextListCtrl for the PopUpMenu");
mTl->mProfile = mProfile;
mTl->setField("noDuplicates", "false");
mSc = new GuiScrollCtrl;
AssertFatal(mSc, "Failed to create the GuiScrollCtrl for the PopUpMenu");
mSc->mProfile = mProfile;
mSc->setField("hScrollBar","AlwaysOff");
mSc->setField("vScrollBar","dynamic");
mBackground = new GuiPopUpBackgroundCtrl(this, mTl);
AssertFatal(mBackground, "Failed to create the GuiBackgroundCtrl for the PopUpMenu");
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::repositionPopup()
{
if ( !mInAction || !mSc || !mTl )
return;
// I'm not concerned with this right now...
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::reverseTextList()
{
mTl->clear();
for(S32 i=mEntries.size()-1; i >= 0; --i)
mTl->addEntry(mEntries[i].id, mEntries[i].buf);
// Don't lose the selected cell:
if ( mSelIndex >= 0 )
mTl->setSelectedCell( Point2I( 0, mEntries.size() - mSelIndex - 1 ) );
mRevNum = mEntries.size() - 1;
}
//------------------------------------------------------------------------------
bool GuiPopUpMenuCtrl::getFontColor( ColorI &fontColor, S32 id, bool selected, bool mouseOver )
{
U32 i;
Entry* entry = NULL;
for ( i = 0; i < mEntries.size(); i++ )
{
if ( mEntries[i].id == id )
{
entry = &mEntries[i];
break;
}
}
if ( !entry )
return( false );
if ( entry->scheme != 0 )
{
// Find the entry's color scheme:
for ( i = 0; i < mSchemes.size(); i++ )
{
if ( mSchemes[i].id == entry->scheme )
{
fontColor = selected ? mSchemes[i].fontColorSEL : mouseOver ? mSchemes[i].fontColorHL : mSchemes[i].fontColor;
return( true );
}
}
}
// Default color scheme...
fontColor = selected ? mProfile->mFontColorSEL : mouseOver ? mProfile->mFontColorHL : mProfile->mFontColor;
return( true );
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::onMouseDown(const GuiEvent &event)
{
event;
onAction();
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::onMouseUp(const GuiEvent &event)
{
event;
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::setupAutoScroll(const GuiEvent &event)
{
GuiControl *parent = getParent();
if (! parent) return;
Point2I mousePt = mSc->globalToLocalCoord(event.mousePoint);
mEventSave = event;
if(mLastYvalue != mousePt.y)
{
mScrollDir = GuiScrollCtrl::None;
if(mousePt.y > mSc->mBounds.extent.y || mousePt.y < 0)
{
S32 topOrBottom = (mousePt.y > mSc->mBounds.extent.y) ? 1 : 0;
mSc->scrollTo(0, topOrBottom);
return;
}
F32 percent = (F32)mousePt.y / (F32)mSc->mBounds.extent.y;
if(percent > 0.7f && mousePt.y > mLastYvalue)
{
mIncValue = percent - 0.5f;
mScrollDir = GuiScrollCtrl::DownArrow;
}
else if(percent < 0.3f && mousePt.y < mLastYvalue)
{
mIncValue = 0.5f - percent;
mScrollDir = GuiScrollCtrl::UpArrow;
}
mLastYvalue = mousePt.y;
}
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::autoScroll()
{
mScrollCount += mIncValue;
while(mScrollCount > 1)
{
mSc->autoScroll(mScrollDir);
mScrollCount -= 1;
}
mTl->onMouseMove(mEventSave);
}
//------------------------------------------------------------------------------
void GuiPopUpMenuCtrl::replaceText(S32 boolVal)
{
mReplaceText = boolVal;
}
// EOF //

View File

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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/controls/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 onMouseDown(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;
};
struct Scheme
{
U32 id;
ColorI fontColor;
ColorI fontColorHL;
ColorI fontColorSEL;
};
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;
virtual void addChildren();
virtual void repositionPopup();
public:
GuiPopUpMenuCtrl(void);
~GuiPopUpMenuCtrl();
GuiScrollCtrl::Region mScrollDir;
bool onAdd();
void onSleep();
void sort();
void addEntry(const char *buf, S32 id, 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 onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void setupAutoScroll(const GuiEvent &event);
void autoScroll();
bool onKeyDown(const GuiEvent &event);
void reverseTextList();
bool getFontColor(ColorI &fontColor, S32 id, bool selected, bool mouseOver);
S32 getSelected();
void setSelected(S32 id);
const char *getScriptValue();
const char *getTextById(S32 id);
S32 findText( const char* text );
S32 getNumEntries() { return( mEntries.size() ); }
void replaceText(S32);
DECLARE_CONOBJECT(GuiPopUpMenuCtrl);
static void initPersistFields(void);
};
#endif //_GUI_POPUPMENU_CTRL_H

View File

@ -0,0 +1,18 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiRadioCtrl.h"
#include "console/consoleTypes.h"
//---------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiRadioCtrl);
GuiRadioCtrl::GuiRadioCtrl()
{
mButtonType = ButtonTypeRadio;
}

View File

@ -0,0 +1,26 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIRADIOCTRL_H_
#define _GUIRADIOCTRL_H_
#ifndef _GUICHECKBOXCTRLL_H_
#include "gui/controls/guiCheckBoxCtrl.h"
#endif
// the radio button renders exactly the same as the check box
// the only difference is it sends messages to its siblings to
// turn themselves off.
class GuiRadioCtrl : public GuiCheckBoxCtrl
{
typedef GuiCheckBoxCtrl Parent;
public:
DECLARE_CONOBJECT(GuiRadioCtrl);
GuiRadioCtrl();
};
#endif //_GUI_RADIO_CTRL_H

View File

@ -0,0 +1,250 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "dgl/gTexManager.h"
#include "gui/controls/guiSliderCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "platform/event.h"
IMPLEMENT_CONOBJECT(GuiSliderCtrl);
//----------------------------------------------------------------------------
GuiSliderCtrl::GuiSliderCtrl(void)
{
mActive = true;
mRange.set( 0.0f, 1.0f );
mTicks = 10;
mValue = 0.5f;
mThumbSize.set(8,20);
mShiftPoint = 5;
mShiftExtent = 10;
mDisplayValue = false;
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup( "Slider" );
addField("range", TypePoint2F, Offset(mRange, GuiSliderCtrl));
addField("ticks", TypeS32, Offset(mTicks, GuiSliderCtrl));
addField("value", TypeF32, Offset(mValue, GuiSliderCtrl));
endGroup( "Slider" );
}
//----------------------------------------------------------------------------
ConsoleMethod( GuiSliderCtrl, getValue, F32, 2, 2, "Get the position of the slider.")
{
return object->getValue();
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::setScriptValue(const char *val)
{
mValue = dAtof(val);
updateThumb(mValue);
}
//----------------------------------------------------------------------------
bool GuiSliderCtrl::onWake()
{
if (! Parent::onWake())
return false;
if(mThumbSize.y + mProfile->mFont->getHeight()-4 <= mBounds.extent.y)
mDisplayValue = true;
else
mDisplayValue = false;
updateThumb( mValue, true );
return true;
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return;
mouseLock();
setFirstResponder();
Point2I curMousePos = globalToLocalCoord(event.mousePoint);
F32 value;
if (mBounds.extent.x >= mBounds.extent.y)
value = F32(curMousePos.x-mShiftPoint) / F32(mBounds.extent.x-mShiftExtent)*(mRange.y-mRange.x) + mRange.x;
else
value = F32(curMousePos.y) / F32(mBounds.extent.y)*(mRange.y-mRange.x) + mRange.x;
updateThumb(value);
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseDragged(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return;
Point2I curMousePos = globalToLocalCoord(event.mousePoint);
F32 value;
if (mBounds.extent.x >= mBounds.extent.y)
value = F32(curMousePos.x-mShiftPoint) / F32(mBounds.extent.x-mShiftExtent)*(mRange.y-mRange.x) + mRange.x;
else
value = F32(curMousePos.y) / F32(mBounds.extent.y)*(mRange.y-mRange.x) + mRange.x;
if (value > mRange.y)
value = mRange.y;
else if (value < mRange.x)
value = mRange.x;
if ((event.modifier & SI_SHIFT) && mTicks > 2) {
// 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");
}
updateThumb(value);
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseUp(const GuiEvent &)
{
if ( !mActive || !mAwake || !mVisible )
return;
mouseUnlock();
if (mConsoleCommand[0])
{
char buf[16];
dSprintf(buf, sizeof(buf), "%d", getId());
Con::setVariable("$ThisControl", buf);
Con::evaluate(mConsoleCommand, false);
}
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::updateThumb( F32 value, bool onWake )
{
mValue = value;
// clamp the thumb to legal values
if (mValue < mRange.x) mValue = mRange.x;
if (mValue > mRange.y) mValue = mRange.y;
Point2I ext = mBounds.extent;
ext.x -= ( mShiftExtent + mThumbSize.x ) / 2;
// update the bounding thumb rect
if (mBounds.extent.x >= mBounds.extent.y)
{ // 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 && mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand, false );
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onRender(Point2I offset, const RectI &updateRect)
{
Point2I pos(offset.x+mShiftPoint, offset.y);
Point2I ext(mBounds.extent.x - mShiftExtent, mBounds.extent.y);
if (mBounds.extent.x >= mBounds.extent.y)
{
Point2I mid(ext.x, ext.y/2);
if(mDisplayValue)
mid.set(ext.x, mThumbSize.y/2);
glColor4f(0, 0, 0, 1);
glBegin(GL_LINES);
// horz rule
glVertex2i(pos.x, pos.y+mid.y);
glVertex2i(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));
glVertex2i(pos.x+x, pos.y+mid.y-mShiftPoint);
glVertex2i(pos.x+x, pos.y+mid.y+mShiftPoint);
}
glEnd();
}
else
{
Point2I mid(ext.x/2, ext.y);
glColor4f(0, 0, 0, 1);
glBegin(GL_LINES);
// horz rule
glVertex2i(pos.x+mid.x, pos.y);
glVertex2i(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));
glVertex2i(pos.x+mid.x-mShiftPoint, pos.y+y);
glVertex2i(pos.x+mid.x+mShiftPoint, pos.y+y);
}
glEnd();
mDisplayValue = false;
}
// draw the thumb
RectI thumb = mThumb;
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+mBounds.extent.x)
textStart.x -=((textStart.x + txt_w) - (offset.x+mBounds.extent.x));
dglSetBitmapModulation(mProfile->mFontColor);
dglDrawText(mProfile->mFont, textStart, buf, mProfile->mFontColors);
}
renderChildControls(offset, updateRect);
}

View File

@ -0,0 +1,48 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUISLIDERCTRL_H_
#define _GUISLIDERCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiSliderCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
Point2F mRange;
U32 mTicks;
F32 mValue;
RectI mThumb;
Point2I mThumbSize;
void updateThumb( F32 value, bool onWake = false );
S32 mShiftPoint;
S32 mShiftExtent;
bool mDisplayValue;
public:
//creation methods
DECLARE_CONOBJECT(GuiSliderCtrl);
GuiSliderCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &);
F32 getValue() { return mValue; }
void setScriptValue(const char *val);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif

View File

@ -0,0 +1,227 @@
//-----------------------------------------------------------------------------
// Justin DuJardin
// Gui Tab Page Control Class
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiTabPageCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/editor/guiEditCtrl.h"
IMPLEMENT_CONOBJECT(GuiTabPageCtrl);
GuiTabPageCtrl::GuiTabPageCtrl(void)
{
mBounds.extent.set(100, 200);
mMinSize.set(50, 50);
dStrcpy(mText,(UTF8*)"TabPage");
mActive = true;
}
void GuiTabPageCtrl::initPersistFields()
{
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);
}
}
void GuiTabPageCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset )
{
// 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 );
}
}
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;
}
bool GuiTabPageCtrl::onKeyDown(const GuiEvent &event)
{
if ((event.keyCode == KEY_TAB) && (event.modifier & SI_CTRL))
{
//find the next sibling window, and select it
GuiControl *parent = getParent();
if (parent)
{
GuiTabPageCtrl *firstWindow = NULL;
iterator i;
for (i = parent->begin(); i != parent->end(); i++)
{
GuiTabPageCtrl *ctrl = dynamic_cast<GuiTabPageCtrl *>(*i);
if (ctrl && ctrl->getTabIndex() == mTabIndex + 1)
{
ctrl->selectWindow();
return true;
}
else if (ctrl && ctrl->getTabIndex() == 0)
{
firstWindow = ctrl;
}
}
//recycle from the beginning
if (firstWindow != this)
{
firstWindow->selectWindow();
return true;
}
}
}
return Parent::onKeyDown(event);
}
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)
{
RectI winRect;
winRect.point = offset;
winRect.extent = mBounds.extent;
dglSetClipRect( winRect );
if( mProfile->mTextureHandle && ( mProfile->mBitmapArrayRects.size() == 0 ) )
{
TextureObject* texture = (TextureObject *) mProfile->mTextureHandle;
RectI srcRegion;
RectI dstRegion;
float xdone = ((float)mBounds.extent.x/(float)texture->bitmapWidth)+1;
float ydone = ((float)mBounds.extent.y/(float)texture->bitmapHeight)+1;
int xshift = texture->bitmapWidth;
int yshift = texture->bitmapHeight;
for(int y = 0; y < ydone; ++y)
{
for(int x = 0; x < xdone; ++x)
{
srcRegion.set(0,0,texture->bitmapWidth,texture->bitmapHeight);
dstRegion.set( ((texture->bitmapWidth*x)+offset.x)-xshift,
((texture->bitmapHeight*y)+offset.y)-yshift,
texture->bitmapWidth,
texture->bitmapHeight);
dglDrawBitmapStretchSR(texture,dstRegion, srcRegion, false);
}
}
}
else
dglDrawRectFill(winRect, mProfile->mFillColor);
//render the children
renderChildControls( offset, updateRect );
}

View File

@ -0,0 +1,47 @@
//-----------------------------------------------------------------------------
// Justin DuJardin
// Gui Tab Page Control Class
//-----------------------------------------------------------------------------
#ifndef _GUITABPAGECTRL_H_
#define _GUITABPAGECTRL_H_
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
class GuiTabPageCtrl : public GuiTextCtrl
{
private:
typedef GuiTextCtrl Parent;
Point2I mMinSize;
S32 mTabIndex;
public:
GuiTabPageCtrl();
DECLARE_CONOBJECT(GuiTabPageCtrl);
static void initPersistFields();
bool onWake(); ///< The page awakens (becomes active)!
void onSleep(); ///< The page sleeps (zzzzZZ - becomes inactive)
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
void 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);
bool onKeyDown(const GuiEvent &event); ///< Called when a keyDown event occurs
void selectWindow(void); ///< Select this window
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,197 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "core/color.h"
#include "gui/controls/guiTextCtrl.h"
#include "dgl/dgl.h"
#include "i18n/lang.h"
// -----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiTextCtrl);
GuiTextCtrl::GuiTextCtrl()
{
//default fonts
mInitialText = StringTable->insert("");
mInitialTextID = StringTable->insert("");
mText[0] = '\0';
mMaxStrLen = GuiTextCtrl::MAX_STRING_LENGTH;
}
ConsoleMethod( GuiTextCtrl, setText, void, 3, 3, "obj.setText( newText )" )
{
argc;
object->setText( argv[2] );
}
ConsoleMethod( GuiTextCtrl, setTextID, void, 3, 3, "obj.setTextID( newText )" )
{
argc;
object->setTextID( argv[2] );
}
void GuiTextCtrl::initPersistFields()
{
Parent::initPersistFields();
addField( "text", TypeCaseString, Offset( mInitialText, GuiTextCtrl ) );
addField( "textID", TypeString, Offset( mInitialTextID, GuiTextCtrl ) );
addField( "maxLength", TypeS32, Offset( mMaxStrLen, GuiTextCtrl ) );
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
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
setText(mInitialText);
}
bool GuiTextCtrl::onWake()
{
if ( !Parent::onWake() )
return false;
mFont = mProfile->mFont;
AssertFatal(mFont, "GuiTextCtrl::onWake: invalid font in profile" );
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
if ( mProfile->mAutoSizeWidth )
{
if ( mProfile->mAutoSizeHeight )
resize( mBounds.point, Point2I( mFont->getStrWidth((const UTF8 *) mText ), mFont->getHeight() + 4 ) );
else
resize( mBounds.point, Point2I( mFont->getStrWidth((const UTF8 *) mText ), mBounds.extent.y ) );
}
else if ( mProfile->mAutoSizeHeight )
resize( mBounds.point, Point2I( mBounds.extent.x, mFont->getHeight() + 4 ) );
return true;
}
void GuiTextCtrl::onSleep()
{
Parent::onSleep();
mFont = NULL;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
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';
//Make sure we have a font
mProfile->incRefCount();
mFont = mProfile->mFont;
//resize
if (mProfile->mAutoSizeWidth)
{
if (mProfile->mAutoSizeHeight)
resize(mBounds.point, Point2I(mFont->getStrWidth((const UTF8 *)mText), mFont->getHeight() + 4));
else
resize(mBounds.point, Point2I(mFont->getStrWidth((const UTF8 *)mText), mBounds.extent.y));
}
else if (mProfile->mAutoSizeHeight)
{
resize(mBounds.point, Point2I(mBounds.extent.x, mFont->getHeight() + 4));
}
setVariable((char*)mText);
setUpdate();
//decrement the profile referrence
mProfile->decRefCount();
}
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()
{
const char * var = getVariable();
if(var && var[0] && dStricmp((char*)mText, var))
setText(var);
}
//------------------------------------------------------------------------------
void GuiTextCtrl::onRender(Point2I offset, const RectI &updateRect)
{
dglSetBitmapModulation( mProfile->mFontColor );
renderJustifiedText(offset, mBounds.extent, (char*)mText);
//render the child controls
renderChildControls(offset, updateRect);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
const char *GuiTextCtrl::getScriptValue()
{
return getText();
}
void GuiTextCtrl::setScriptValue(const char *val)
{
setText(val);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
// EOF //

View File

@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUITEXTCTRL_H_
#define _GUITEXTCTRL_H_
#ifndef _GFONT_H_
#include "dgl/gNewFont.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiTextCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
enum Constants { MAX_STRING_LENGTH = 255 };
protected:
StringTableEntry mInitialText;
StringTableEntry mInitialTextID;
UTF8 mText[MAX_STRING_LENGTH + 1];
S32 mMaxStrLen; // max string len, must be less then or equal to 255
Resource<GFont> mFont;
public:
//creation methods
DECLARE_CONOBJECT(GuiTextCtrl);
GuiTextCtrl();
static void initPersistFields();
//Parental methods
bool onAdd();
virtual bool onWake();
virtual void onSleep();
//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; }
void inspectPostApply();
//rendering methods
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void displayText( S32 xOffset, S32 yOffset );
//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,118 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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 GuiTextEditCtrl : public GuiTextCtrl
{
private:
typedef GuiTextCtrl Parent;
static U32 smNumAwake;
protected:
StringBuffer mTextBuffer;
StringTableEntry mValidateCommand;
StringTableEntry mEscapeCommand;
AudioProfile* mDeniedSound;
// for animating the cursor
S32 mNumFramesElapsed;
U32 mTimeLastCursorFlipped;
ColorI mCursorColor;
bool mCursorOn;
//Edit Cursor
GuiCursor* mEditCursor;
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;
S32 setCursorPos(const Point2I &offset);
bool mHistoryDirty;
S32 mHistoryLast;
S32 mHistoryIndex;
S32 mHistorySize;
bool mPasswordText;
StringTableEntry mPasswordMask;
bool mSinkAllKeyEvents; // any non-ESC key is handled here or not at all
UTF8 **mHistoryBuf;
void updateHistory(StringBuffer *txt, bool moveIndex);
void playDeniedSound();
void execConsoleCallback();
public:
GuiTextEditCtrl();
~GuiTextEditCtrl();
DECLARE_CONOBJECT(GuiTextEditCtrl);
static void initPersistFields();
bool onAdd();
bool onWake();
void onSleep();
void getText(char *dest); // dest must be of size
// StructDes::MAX_STRING_LEN + 1
bool initCursors();
void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
void setText(S32 tag);
virtual void setText(const char *txt);
S32 getCursorPos() { return( mCursorPos ); }
void reallySetCursorPos( const S32 newPos );
const char *getScriptValue();
void setScriptValue(const char *value);
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
virtual void setFirstResponder();
virtual void onLoseFirstResponder();
void parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent);
bool hasText();
void onStaticModified(const char* slotName);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
virtual void drawText( const RectI &drawRect, bool isFocused );
};
#endif //_GUI_TEXTEDIT_CTRL_H

View File

@ -0,0 +1,271 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiTextEditSliderCtrl.h"
IMPLEMENT_CONOBJECT(GuiTextEditSliderCtrl);
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;
}
GuiTextEditSliderCtrl::~GuiTextEditSliderCtrl()
{
}
void GuiTextEditSliderCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("format", TypeString, Offset(mFormat, GuiTextEditSliderCtrl));
addField("range", TypePoint2F, Offset(mRange, GuiTextEditSliderCtrl));
addField("increment", TypeF32, Offset(mIncAmount, GuiTextEditSliderCtrl));
}
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];
dSprintf(buf,sizeof(buf),mFormat, mValue);
Parent::setText(buf);
}
void GuiTextEditSliderCtrl::onMouseDown(const GuiEvent &event)
{
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(mBounds.point);
if(camPos.x > point.x + mBounds.extent.x - 14)
{
if(camPos.y > point.y + (mBounds.extent.y/2))
{
mValue -=mIncAmount;
mTextAreaHit = ArrowDown;
mMulInc = -0.15f;
}
else
{
mValue +=mIncAmount;
mTextAreaHit = ArrowUp;
mMulInc = 0.15f;
}
checkRange();
setValue();
mouseLock();
return;
}
Parent::onMouseDown(event);
}
void GuiTextEditSliderCtrl::onMouseDragged(const GuiEvent &event)
{
if(mTextAreaHit == None || mTextAreaHit == Slider)
{
mTextAreaHit = Slider;
GuiControl *parent = getParent();
if(!parent)
return;
Point2I camPos = event.mousePoint;
Point2I point = parent->localToGlobalCoord(mBounds.point);
F32 maxDis = 100;
F32 val;
if(camPos.y < point.y)
{
if(point.y < maxDis)
maxDis = 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 + mBounds.extent.y)
{
GuiCanvas *root = getRoot();
val = root->mBounds.extent.y - (point.y + mBounds.extent.y);
if(val < maxDis)
maxDis = val;
if( val > 0)
mMulInc= -(float)(camPos.y - (point.y + mBounds.extent.y))/maxDis;
else
mMulInc = -1.0f;
checkIncValue();
return;
}
mTextAreaHit = None;
Parent::onMouseDragged(event);
}
}
void GuiTextEditSliderCtrl::onMouseUp(const GuiEvent &event)
{
mMulInc = 0.0f;
mouseUnlock();
//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())
Con::evaluate(mConsoleCommand, false);
mTextAreaHit = None;
}
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();
}
}
}
Parent::onRender(offset, updateRect);
Point2I start(offset.x + mBounds.extent.x - 14, offset.y);
Point2I midPoint(start.x + 7, start.y + (mBounds.extent.y/2));
dglDrawRectFill(Point2I(start.x+1,start.y+1), Point2I(start.x+13,start.y+mBounds.extent.y-1) , mProfile->mFillColor);
dglDrawLine(start, Point2I(start.x, start.y+mBounds.extent.y),mProfile->mFontColor);
dglDrawLine(Point2I(start.x,midPoint.y),
Point2I(start.x+14,midPoint.y),
mProfile->mFontColor);
glColor3i(0,0,0);
glBegin(GL_TRIANGLES);
if(mTextAreaHit == ArrowUp)
{
glVertex2i(midPoint.x, start.y+1);
glVertex2i(start.x+11,midPoint.y-2);
glVertex2i(start.x+3,midPoint.y-2);
}
else
{
glVertex2i(midPoint.x, start.y+2);
glVertex2i(start.x+11,midPoint.y-1);
glVertex2i(start.x+3,midPoint.y-1);
}
if(mTextAreaHit == ArrowDown)
{
glVertex2i(midPoint.x, start.y+mBounds.extent.y-1);
glVertex2i(start.x+11,midPoint.y+3);
glVertex2i(start.x+3,midPoint.y+3);
}
else
{
glVertex2i(midPoint.x, start.y+mBounds.extent.y-2);
glVertex2i(start.x+11,midPoint.y+2);
glVertex2i(start.x+3,midPoint.y+2);
}
glEnd();
}
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,67 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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
{
private:
typedef GuiTextEditCtrl Parent;
Point2F mRange;
F32 mIncAmount;
F32 mValue;
F32 mIncCounter;
F32 mMulInc;
StringTableEntry mFormat;
U32 mMouseDownTime;
// max string len, must be less then or equal to 255
public:
enum CtrlArea
{
None,
Slider,
ArrowUp,
ArrowDown
};
private:
CtrlArea mTextAreaHit;
public:
GuiTextEditSliderCtrl();
~GuiTextEditSliderCtrl();
DECLARE_CONOBJECT(GuiTextEditSliderCtrl);
static void initPersistFields();
void getText(char *dest); // dest must be of size
// StructDes::MAX_STRING_LEN + 1
void setText(S32 tag);
void setText(const char *txt);
void setValue();
void checkRange();
void checkIncValue();
void timeInc(U32 elapseTime);
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif //_GUI_TEXTEDIT_CTRL_H

View File

@ -0,0 +1,580 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/controls/guiTextListCtrl.h"
#include "gui/containers/guiScrollCtrl.h"
IMPLEMENT_CONOBJECT(GuiTextListCtrl);
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 = dStricmp( 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;
mEnumerate = false;
mResizeCell = true;
mSize.set(1, 0);
mColumnOffsets.push_back(0);
mFitParentWidth = true;
mClipColumnText = false;
}
void GuiTextListCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("enumerate", TypeBool, Offset(mEnumerate, GuiTextListCtrl));
addField("resizeCell", TypeBool, Offset(mResizeCell, GuiTextListCtrl));
addField("columns", TypeS32Vector, Offset(mColumnOffsets, GuiTextListCtrl));
addField("fitParentWidth", TypeBool, Offset(mFitParentWidth, GuiTextListCtrl));
addField("clipColumnText", TypeBool, Offset(mClipColumnText, GuiTextListCtrl));
}
ConsoleMethod(GuiTextListCtrl, getSelectedId, S32, 2, 2, "Get the ID of the currently selected item.")
{
return object->getSelectedId();
}
ConsoleMethod( GuiTextListCtrl, setSelectedById, void, 3, 3, "(int id)"
"Finds the specified entry by id, then marks its row as selected.")
{
S32 index = object->findEntryById(dAtoi(argv[2]));
if(index < 0)
return ;
object->setSelectedCell(Point2I(0, index));
}
ConsoleMethod( GuiTextListCtrl, setSelectedRow, void, 3, 3, "(int rowNum)"
"Selects the specified row.")
{
object->setSelectedCell( Point2I( 0, dAtoi( argv[2] ) ) );
}
ConsoleMethod( GuiTextListCtrl, clearSelection, void, 2, 2, "Set the selection to nothing.")
{
object->setSelectedCell(Point2I(-1, -1));
}
ConsoleMethod(GuiTextListCtrl, addRow, S32, 4, 5, "(int id, string text, int index=0)"
"Returns row number of the new item.")
{
S32 ret = object->mList.size();
if(argc < 5)
object->addEntry(dAtoi(argv[2]), argv[3]);
else
object->insertEntry(dAtoi(argv[2]), argv[3], dAtoi(argv[4]));
return ret;
}
ConsoleMethod( GuiTextListCtrl, setRowById, void, 4, 4, "(int id, string text)")
{
object->setEntry(dAtoi(argv[2]), argv[3]);
}
ConsoleMethod( GuiTextListCtrl, sort, void, 3, 4, "(int columnID, bool increasing=false)"
"Performs a standard (alphabetical) sort on the values in the specified column.")
{
if ( argc == 3 )
object->sort(dAtoi(argv[2]));
else
object->sort( dAtoi( argv[2] ), dAtob( argv[3] ) );
}
ConsoleMethod(GuiTextListCtrl, sortNumerical, void, 3, 4, "(int columnID, bool increasing=false)"
"Perform a numerical sort on the values in the specified column.")
{
if ( argc == 3 )
object->sortNumerical( dAtoi( argv[2] ) );
else
object->sortNumerical( dAtoi( argv[2] ), dAtob( argv[3] ) );
}
ConsoleMethod( GuiTextListCtrl, clear, void, 2, 2, "Clear the list.")
{
object->clear();
}
ConsoleMethod( GuiTextListCtrl, rowCount, S32, 2, 2, "Get the number of rows.")
{
return object->getNumEntries();
}
ConsoleMethod( GuiTextListCtrl, getRowId, S32, 3, 3, "(int index)"
"Get the row ID for an index.")
{
U32 index = dAtoi(argv[2]);
if(index >= object->getNumEntries())
return -1;
return object->mList[index].id;
}
ConsoleMethod( GuiTextListCtrl, getRowTextById, const char*, 3, 3, "(int id)"
"Get the text of a row with the specified id.")
{
S32 index = object->findEntryById(dAtoi(argv[2]));
if(index < 0)
return "";
return object->mList[index].text;
}
ConsoleMethod( GuiTextListCtrl, getRowNumById, S32, 3, 3, "(int id)"
"Get the row number for a specified id.")
{
S32 index = object->findEntryById(dAtoi(argv[2]));
if(index < 0)
return -1;
return index;
}
ConsoleMethod( GuiTextListCtrl, getRowText, const char*, 3, 3, "(int index)"
"Get the text of the row with the specified index.")
{
U32 index = dAtoi(argv[2]);
if(index < 0 || index >= object->mList.size())
return "";
return object->mList[index].text;
}
ConsoleMethod( GuiTextListCtrl, removeRowById, void, 3, 3,"(int id)"
"Remove row with the specified id.")
{
object->removeEntry(dAtoi(argv[2]));
}
ConsoleMethod( GuiTextListCtrl, removeRow, void, 3, 3, "(int index)"
"Remove a row from the table, based on its index.")
{
U32 index = dAtoi(argv[2]);
object->removeEntryByIndex(index);
}
ConsoleMethod( GuiTextListCtrl, scrollVisible, void, 3, 3, "(int rowNum)"
"Scroll so the specified row is visible.")
{
object->scrollCellVisible(Point2I(0, dAtoi(argv[2])));
}
ConsoleMethod( GuiTextListCtrl, findTextIndex, S32, 3, 3, "(string needle)"
"Find needle in the list, and return the row number it was found in.")
{
return( object->findEntryByText( argv[2] ) );
}
ConsoleMethod( GuiTextListCtrl, setRowActive, void, 4, 4, "(int rowNum, bool active)"
"Mark a specified row as active/not.")
{
object->setEntryActive( U32( dAtoi( argv[2] ) ), dAtob( argv[3] ) );
}
ConsoleMethod( GuiTextListCtrl, isRowActive, bool, 3, 3, "(int rowNum)"
"Is the specified row currently active?")
{
return( object->isEntryActive( U32( dAtoi( argv[2] ) ) ) );
}
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;
}
void GuiTextListCtrl::onCellSelected(Point2I cell)
{
Con::executef(this, 3, "onSelect", Con::getIntArg(mList[cell.y].id), mList[cell.y].text);
if (mConsoleCommand[0])
Con::evaluate(mConsoleCommand, false);
}
void GuiTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
{
if ( mList[cell.y].active )
{
if (selected || (mProfile->mMouseOverSelected && mouseOver))
{
dglDrawRectFill(RectI(offset.x, offset.y, mCellSize.x, mCellSize.y), mProfile->mFillColorHL);
dglSetBitmapModulation(mProfile->mFontColorHL);
}
else
dglSetBitmapModulation(mouseOver ? mProfile->mFontColorHL : mProfile->mFontColor);
}
else
dglSetBitmapModulation( 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 = dglGetClipRect();
RectI clipRect(pos, Point2I(mColumnOffsets[index+1] - mColumnOffsets[index] - 4, mCellSize.y));
if(clipRect.intersect(saveClipRect))
{
clipped = true;
dglSetClipRect(clipRect);
}
}
dglDrawTextN(mFont, pos, text, slen, mProfile->mFontColors);
if(clipped)
dglSetClipRect(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(&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 );
resize( mBounds.point, 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:
if ( mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand, false );
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() )
Con::executef( this, 2, "onDeleteKey", 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 );
}

View File

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#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;
bool mEnumerate;
bool mResizeCell;
protected:
enum ScrollConst
{
UP = 0,
DOWN = 1
};
enum {
InvalidId = 0xFFFFFFFF
};
Vector<S32> mColumnOffsets;
bool mFitParentWidth;
bool mClipColumnText;
U32 getRowWidth(Entry *row);
void onCellSelected(Point2I cell);
public:
GuiTextListCtrl();
DECLARE_CONOBJECT(GuiTextListCtrl);
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();
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,389 @@
#ifndef _GUI_TREEVIEWCTRL_H
#define _GUI_TREEVIEWCTRL_H
#include "core/bitSet.h"
#include "math/mRect.h"
#include "dgl/gFont.h"
#include "gui/core/guiControl.h"
#include "gui/core/guiArrayCtrl.h"
//------------------------------------------------------------------------------
class GuiTreeViewCtrl : public GuiArrayCtrl
{
private:
typedef GuiArrayCtrl Parent;
protected:
/// @section GuiControl_Intro Introduction
/// @nosubgrouping
///
struct Item
{
enum ItemState
{
Selected = BIT(0),
Expanded = BIT(1),
Focus = BIT(2),
MouseOverBmp = BIT(3),
MouseOverText = BIT(4),
InspectorData = BIT(5), ///< Set if we're representing some inspector
/// info (ie, use mInspectorInfo, not mScriptInfo)
VirtualParent = BIT(6), ///< 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).
};
BitSet32 mState;
S16 mId;
U16 mTabLevel;
Item * mParent;
Item * mChild;
Item * mNext;
Item * mPrevious;
S32 mIcon; //stores the icon that will represent the item in the tree
Item();
~Item();
struct ScriptTag
{
S8 mNormalImage;
S8 mExpandedImage;
char* mText;
char* mValue;
} mScriptInfo;
struct InspectorTag
{
SimObjectPtr<SimObject> mObject;
} mInspectorInfo;
/// @name Get Methods
/// @{
///
const S8 getNormalImage() const;
const S8 getExpandedImage() const;
char *getText();
char *getValue();
inline const S16 getID() const { return mId; };
SimObject *getObject();
const U32 getDisplayTextLength();
const S32 getDisplayTextWidth(GFont *font);
void getDisplayText(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);
/// 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(char *txt);
/// Set the items script value (data)
void setValue(const char *val);
/// Set the items virtual parent flag
void setVirtualParent( bool value );
/// @}
/// @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.
const bool isExpanded() const;
/// Returns true if an item is inspector data
/// or false if it's just an item.
inline const 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.
const bool isParent() const;
/// @}
/// @name Searching Methods
/// @{
/// 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);
/// @}
};
/// @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.
IsDeletable = BIT(3), ///< We allow items to be deleted.
ShowTreeLines = BIT(4), ///< Should we render tree lines or just icons?
};
protected:
enum
{
MaxIcons = 32,
};
enum Icons
{
Default1 = 0,
SimGroup1,
SimGroup2,
SimGroup3,
SimGroup4,
Audio,
Camera,
fxFoliageReplicator,
fxLight,
fxShapeReplicator,
fxSunLight,
Hidden,
Interior,
Lightning,
Lock1,
Lock2,
MissionArea,
Particle,
Path,
Pathmarker,
PhysicalArea,
Precipitation,
Shape,
Sky,
StaticShape,
Sun,
Terrain,
Trigger,
Water,
Default,
Icon31,
Icon32
};
enum mDragMidPointFlags
{
NomDragMidPoint,
AbovemDragMidPoint,
BelowmDragMidPoint
};
///
enum HitFlags
{
OnIndent = BIT(0),
OnImage = BIT(1),
OnText = BIT(2),
OnRow = BIT(3),
};
///
enum BmpIndices
{
BmpDunno,
BmpLastChild,
BmpChild,
BmpExp,
BmpExpN,
BmpExpP,
BmpExpPN,
BmpCon,
BmpConN,
BmpConP,
BmpConPN,
BmpLine,
BmpGlow,
};
/// @}
public:
///
Vector<Item*> mItems;
Vector<Item*> mVisibleItems;
Vector<Item*> mSelectedItems;
Vector<S32> mSelected; ///< Used for tracking stuff that was
/// selected, but may not have been
/// created at time of selection
S32 mItemCount;
Item * mItemFreeList; ///< We do our own free list, as we
/// we want to be able to recycle
/// item ids and do some other clever
/// things.
Item * mRoot;
S32 mInstantGroup;
S32 mMaxWidth;
S32 mSelectedItem;
S32 mDraggedToItem;
S32 mTempItem;
S32 mStart;
BitSet32 mFlags;
protected:
TextureHandle mIconTable[MaxIcons];
// for debugging
bool mDebug;
S32 mTabSize;
S32 mTextOffset;
bool mFullRowSelect;
S32 mItemHeight;
bool mDestroyOnSleep;
bool mSupportMouseDragging;
bool mMultipleSelections;
S32 mOldDragY;
S32 mCurrentDragCell;
S32 mPreviousDragCell;
S32 mDragMidPoint;
bool mMouseDragged;
StringTableEntry mBitmapBase;
TextureHandle mTexRollover;
TextureHandle mTexSelected;
ColorI mAltFontColor;
ColorI mAltFontColorHL;
ColorI mAltFontColorSE;
SimObjectPtr<SimObject> mRootObject;
void destroyChildren(Item * item, Item * parent);
void destroyItem(Item * item);
void destroyTree();
void deleteItem(Item *item);
void buildItem(Item * item, U32 tabLevel);
void buildVisibleTree();
bool hitTest(const Point2I & pnt, Item* & item, BitSet32 & flags);
virtual bool onVirtualParentBuild(Item *item);
virtual bool onVirtualParentExpand(Item *item);
virtual bool onVirtualParentCollapse(Item *item);
virtual void onItemSelected( Item *item );
void addInspectorDataItem(Item *parent, SimObject *obj);
public:
GuiTreeViewCtrl();
virtual ~GuiTreeViewCtrl();
/// Used for syncing the mSelected and mSelectedItems lists.
void syncSelection();
void lockSelection(bool lock);
void hideSelection(bool hide);
void addSelection(S32 itemId);
/// 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 * getItem(S32 itemId);
Item * createItem(S32 icon);
bool editItem( S32 itemId, const char* newText, const char* newValue );
// 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 buildIconTable(const char * icons);
void setInstantGroup(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();
void moveItemUp( S32 itemId );
// misc.
bool scrollVisible( Item *item );
void deleteSelection();
void clearSelection();
S32 findItemByName(const char *name);
// GuiControl
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 onMouseDragged(const GuiEvent &event);
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);
// GuiArrayCtrl
void onRenderCell(Point2I offset, Point2I cell, bool, bool);
void onRender(Point2I offset, const RectI &updateRect);
static void initPersistFields();
void inspectObject(SimObject * obj, bool okToEdit);
DECLARE_CONOBJECT(GuiTreeViewCtrl);
};
#endif

420
engine/gui/core/guiArrayCtrl.cc Executable file
View File

@ -0,0 +1,420 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "platform/event.h"
#include "gui/containers/guiScrollCtrl.h"
#include "gui/core/guiArrayCtrl.h"
IMPLEMENT_CONOBJECT(GuiArrayCtrl);
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
GuiArrayCtrl::GuiArrayCtrl()
{
mActive = true;
mCellSize.set(80, 30);
mSize = Point2I(5, 30);
mSelectedCell.set(-1, -1);
mMouseOverCell.set(-1, -1);
mHeaderDim.set(0, 0);
}
bool GuiArrayCtrl::onWake()
{
if (! Parent::onWake())
return false;
//get the font
mFont = mProfile->mFont;
return true;
}
void GuiArrayCtrl::onSleep()
{
Parent::onSleep();
mFont = NULL;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
void GuiArrayCtrl::setSize(Point2I newSize)
{
mSize = newSize;
Point2I newExtent(newSize.x * mCellSize.x + mHeaderDim.x, newSize.y * mCellSize.y + mHeaderDim.y);
resize(mBounds.point, newExtent);
}
void GuiArrayCtrl::getScrollDimensions(S32 &cell_size, S32 &num_cells)
{
cell_size = mCellSize.y;
num_cells = mSize.y;
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
bool GuiArrayCtrl::cellSelected(Point2I cell)
{
if (cell.x < 0 || cell.x >= mSize.x || cell.y < 0 || cell.y >= mSize.y)
{
mSelectedCell = Point2I(-1,-1);
return false;
}
mSelectedCell = cell;
scrollSelectionVisible();
onCellSelected(cell);
setUpdate();
return true;
}
void GuiArrayCtrl::onCellSelected(Point2I cell)
{
Con::executef(this, 3, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y));
//call the console function
if (mConsoleCommand[0])
Con::evaluate(mConsoleCommand, false);
}
void GuiArrayCtrl::setSelectedCell(Point2I cell)
{
cellSelected(cell);
}
Point2I GuiArrayCtrl::getSelectedCell()
{
return mSelectedCell;
}
void GuiArrayCtrl::scrollSelectionVisible()
{
scrollCellVisible(mSelectedCell);
}
void GuiArrayCtrl::scrollCellVisible(Point2I cell)
{
//make sure we have a parent
//make sure we have a valid cell selected
GuiScrollCtrl *parent = dynamic_cast<GuiScrollCtrl*>(getParent());
if(!parent || cell.x < 0 || cell.y < 0)
return;
RectI cellBounds(cell.x * mCellSize.x, cell.y * mCellSize.y, mCellSize.x, mCellSize.y);
parent->scrollRectVisible(cellBounds);
}
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
void GuiArrayCtrl::onRenderColumnHeaders(Point2I offset, Point2I parentOffset, Point2I headerDim)
{
if (mProfile->mBorder)
{
RectI cellR(offset.x + headerDim.x, parentOffset.y, mBounds.extent.x - headerDim.x, headerDim.y);
dglDrawRectFill(cellR, mProfile->mBorderColor);
}
}
void GuiArrayCtrl::onRenderRowHeader(Point2I offset, Point2I parentOffset, Point2I headerDim, Point2I cell)
{
ColorI color;
RectI cellR;
if (cell.x % 2)
color.set(255, 0, 0, 255);
else
color.set(0, 255, 0, 255);
cellR.point.set(parentOffset.x, offset.y);
cellR.extent.set(headerDim.x, mCellSize.y);
dglDrawRectFill(cellR, color);
}
void GuiArrayCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
{
ColorI color(255 * (cell.x % 2), 255 * (cell.y % 2), 255 * ((cell.x + cell.y) % 2), 255);
if (selected)
{
color.set(255, 0, 0, 255);
}
else if (mouseOver)
{
color.set(0, 0, 255, 255);
}
//draw the cell
RectI cellR(offset.x, offset.y, mCellSize.x, mCellSize.y);
dglDrawRectFill(cellR, color);
}
void GuiArrayCtrl::onRender(Point2I offset, const RectI &updateRect)
{
//make sure we have a parent
GuiControl *parent = getParent();
if (! parent)
return;
S32 i, j;
RectI headerClip;
RectI clipRect(updateRect.point, updateRect.extent);
Point2I parentOffset = parent->localToGlobalCoord(Point2I(0, 0));
//if we have column headings
if (mHeaderDim.y > 0)
{
headerClip.point.x = parentOffset.x + mHeaderDim.x;
headerClip.point.y = parentOffset.y;
headerClip.extent.x = clipRect.extent.x;// - headerClip.point.x; // This seems to fix some strange problems with some Gui's, bug? -pw
headerClip.extent.y = mHeaderDim.y;
if (headerClip.intersect(clipRect))
{
dglSetClipRect(headerClip);
//now render the header
onRenderColumnHeaders(offset, parentOffset, mHeaderDim);
clipRect.point.y = headerClip.point.y + headerClip.extent.y - 1;
}
offset.y += mHeaderDim.y;
}
//if we have row headings
if (mHeaderDim.x > 0)
{
clipRect.point.x = getMax(clipRect.point.x, parentOffset.x + mHeaderDim.x);
offset.x += mHeaderDim.x;
}
//save the original for clipping the row headers
RectI origClipRect = clipRect;
for (j = 0; j < mSize.y; j++)
{
//skip until we get to a visible row
if ((j + 1) * mCellSize.y + offset.y < updateRect.point.y)
continue;
//break once we've reached the last visible row
if(j * mCellSize.y + offset.y >= updateRect.point.y + updateRect.extent.y)
break;
//render the header
if (mHeaderDim.x > 0)
{
headerClip.point.x = parentOffset.x;
headerClip.extent.x = mHeaderDim.x;
headerClip.point.y = offset.y + j * mCellSize.y;
headerClip.extent.y = mCellSize.y;
if (headerClip.intersect(origClipRect))
{
dglSetClipRect(headerClip);
//render the row header
onRenderRowHeader(Point2I(0, offset.y + j * mCellSize.y),
Point2I(parentOffset.x, offset.y + j * mCellSize.y),
mHeaderDim, Point2I(0, j));
}
}
//render the cells for the row
for (i = 0; i < mSize.x; i++)
{
//skip past columns off the left edge
if ((i + 1) * mCellSize.x + offset.x < updateRect.point.x)
continue;
//break once past the last visible column
if (i * mCellSize.x + offset.x >= updateRect.point.x + updateRect.extent.x)
break;
S32 cellx = offset.x + i * mCellSize.x;
S32 celly = offset.y + j * mCellSize.y;
RectI cellClip(cellx, celly, mCellSize.x, mCellSize.y);
//make sure the cell is within the update region
if (cellClip.intersect(clipRect))
{
//set the clip rect
dglSetClipRect(cellClip);
//render the cell
onRenderCell(Point2I(cellx, celly), Point2I(i, j),
i == mSelectedCell.x && j == mSelectedCell.y,
i == mMouseOverCell.x && j == mMouseOverCell.y);
}
}
}
}
void GuiArrayCtrl::onMouseDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return;
//let the guiControl method take care of the rest
Parent::onMouseDown(event);
Point2I pt = globalToLocalCoord(event.mousePoint);
pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
Point2I cell(
(pt.x < 0 ? -1 : pt.x / mCellSize.x),
(pt.y < 0 ? -1 : pt.y / mCellSize.y)
);
if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
//store the previously selected cell
Point2I prevSelected = mSelectedCell;
//select the new cell
cellSelected(Point2I(cell.x, cell.y));
//if we double clicked on the *same* cell, evaluate the altConsole Command
if ( ( event.mouseClickCount > 1 ) && ( prevSelected == mSelectedCell ) && mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand, false );
}
}
void GuiArrayCtrl::onMouseEnter(const GuiEvent &event)
{
Point2I pt = globalToLocalCoord(event.mousePoint);
pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
//get the cell
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
mMouseOverCell = cell;
setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x,
cell.y * mCellSize.y + mHeaderDim.y), mCellSize );
}
}
void GuiArrayCtrl::onMouseLeave(const GuiEvent & /*event*/)
{
setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x,
mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize);
mMouseOverCell.set(-1,-1);
}
void GuiArrayCtrl::onMouseDragged(const GuiEvent &event)
{
// for the array control, the behaviour of onMouseDragged is the same
// as on mouse moved - basically just recalc the currend mouse over cell
// and set the update regions if necessary
GuiArrayCtrl::onMouseMove(event);
}
void GuiArrayCtrl::onMouseMove(const GuiEvent &event)
{
Point2I pt = globalToLocalCoord(event.mousePoint);
pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
if (cell.x != mMouseOverCell.x || cell.y != mMouseOverCell.y)
{
if (mMouseOverCell.x != -1)
{
setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x,
mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize);
}
if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x,
cell.y * mCellSize.y + mHeaderDim.y), mCellSize);
mMouseOverCell = cell;
}
else
mMouseOverCell.set(-1,-1);
}
}
bool GuiArrayCtrl::onKeyDown(const GuiEvent &event)
{
//if this control is a dead end, kill the event
if ((! mVisible) || (! mActive) || (! mAwake)) return true;
//get the parent
S32 pageSize = 1;
GuiControl *parent = getParent();
if (parent && mCellSize.y > 0)
{
pageSize = getMax(1, (parent->mBounds.extent.y / mCellSize.y) - 1);
}
Point2I delta(0,0);
switch (event.keyCode)
{
case KEY_LEFT:
delta.set(-1, 0);
break;
case KEY_RIGHT:
delta.set(1, 0);
break;
case KEY_UP:
delta.set(0, -1);
break;
case KEY_DOWN:
delta.set(0, 1);
break;
case KEY_PAGE_UP:
delta.set(0, -pageSize);
break;
case KEY_PAGE_DOWN:
delta.set(0, pageSize);
break;
case KEY_HOME:
cellSelected( Point2I( 0, 0 ) );
return( true );
case KEY_END:
cellSelected( Point2I( 0, mSize.y - 1 ) );
return( true );
default:
return Parent::onKeyDown(event);
}
if (mSize.x < 1 || mSize.y < 1)
return true;
//select the first cell if no previous cell was selected
if (mSelectedCell.x == -1 || mSelectedCell.y == -1)
{
cellSelected(Point2I(0,0));
return true;
}
//select the cell
Point2I cell = mSelectedCell;
cell.x = getMax(0, getMin(mSize.x - 1, cell.x + delta.x));
cell.y = getMax(0, getMin(mSize.y - 1, cell.y + delta.y));
cellSelected(cell);
return true;
}
void GuiArrayCtrl::onRightMouseDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return;
Parent::onRightMouseDown( event );
Point2I pt = globalToLocalCoord( event.mousePoint );
pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
char buf[32];
dSprintf( buf, sizeof( buf ), "%d %d", event.mousePoint.x, event.mousePoint.y );
// Pass it to the console:
Con::executef(this, 4, "onRightMouseDown", Con::getIntArg(cell.x), Con::getIntArg(cell.y), buf);
}
}

78
engine/gui/core/guiArrayCtrl.h Executable file
View File

@ -0,0 +1,78 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIARRAYCTRL_H_
#define _GUIARRAYCTRL_H_
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
/// Renders a grid of cells.
class GuiArrayCtrl : public GuiControl
{
typedef GuiControl Parent;
protected:
Point2I mHeaderDim;
Point2I mSize;
Point2I mCellSize;
Point2I mSelectedCell;
Point2I mMouseOverCell;
Resource<GFont> mFont;
bool cellSelected(Point2I cell);
virtual void onCellSelected(Point2I cell);
public:
GuiArrayCtrl();
DECLARE_CONOBJECT(GuiArrayCtrl);
bool onWake();
void onSleep();
/// @name Array attribute methods
/// @{
Point2I getSize() { return mSize; }
virtual void setSize(Point2I size);
void setHeaderDim(const Point2I &dim) { mHeaderDim = dim; }
void getScrollDimensions(S32 &cell_size, S32 &num_cells);
/// @}
/// @name Selected cell methods
/// @{
void setSelectedCell(Point2I cell);
void deselectCells() { mSelectedCell.set(-1,-1); }
Point2I getSelectedCell();
void scrollSelectionVisible();
void scrollCellVisible(Point2I cell);
/// @}
/// @name Rendering methods
/// @{
virtual void onRenderColumnHeaders(Point2I offset, Point2I parentOffset, Point2I headerDim);
virtual void onRenderRowHeader(Point2I offset, Point2I parentOffset, Point2I headerDim, Point2I cell);
virtual void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
void onRender(Point2I offset, const RectI &updateRect);
/// @}
/// @name Mouse input methods
/// @{
void onMouseDown(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseEnter(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
bool onKeyDown(const GuiEvent &event);
void onRightMouseDown(const GuiEvent &event);
/// @}
};
#endif //_GUI_ARRAY_CTRL_H

1333
engine/gui/core/guiCanvas.cc Executable file

File diff suppressed because it is too large Load Diff

334
engine/gui/core/guiCanvas.h Executable file
View File

@ -0,0 +1,334 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUICANVAS_H_
#define _GUICANVAS_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
/// A canvas on which rendering occurs.
///
///
/// @section GuiCanvas_contents What a GUICanvas Can Contain...
///
/// @subsection GuiCanvas_content_contentcontrol Content Control
/// A content control is the top level GuiControl for a screen. This GuiControl
/// will be the parent control for all other GuiControls on that particular
/// screen.
///
/// @subsection GuiCanvas_content_dialogs Dialogs
///
/// A dialog is essentially another screen, only it gets overlaid on top of the
/// current content control, and all input goes to the dialog. This is most akin
/// to the "Open File" dialog box found in most operating systems. When you
/// choose to open a file, and the "Open File" dialog pops up, you can no longer
/// send input to the application, and must complete or cancel the open file
/// request. Torque keeps track of layers of dialogs. The dialog with the highest
/// layer is on top and will get all the input, unless the dialog is
/// modeless, which is a profile option.
///
/// @see GuiControlProfile
///
/// @section GuiCanvas_dirty Dirty Rectangles
///
/// The GuiCanvas is based on dirty regions.
///
/// Every frame the canvas paints only the areas of the canvas that are 'dirty'
/// or need updating. In most cases, this only is the area under the mouse cursor.
/// This is why if you look in guiCanvas.cc the call to glClear is commented out.
/// If you want a really good idea of what exactly dirty regions are and how they
/// work, un-comment that glClear line in the renderFrame method of guiCanvas.cc
///
/// What you will see is a black screen, except in the dirty regions, where the
/// screen will be painted normally. If you are making an animated GuiControl
/// you need to add your control to the dirty areas of the canvas.
///
class GuiCanvas : public GuiControl
{
protected:
typedef GuiControl Parent;
typedef SimObject Grandparent;
/// @name Rendering
/// @{
///
RectI mOldUpdateRects[2];
RectI mCurUpdateRect;
F32 rLastFrameTime;
/// @}
/// @name Cursor Properties
/// @{
F32 mPixelsPerMickey; ///< This is the scale factor which relates
/// mouse movement in pixels (one unit of
/// mouse movement is a mickey) to units in
/// the GUI.
bool cursorON;
bool mShowCursor;
bool mRenderFront;
Point2F cursorPt;
Point2I lastCursorPt;
GuiCursor *defaultCursor;
GuiCursor *lastCursor;
bool lastCursorON;
/// @}
/// @name Mouse Input
/// @{
SimObjectPtr<GuiControl> mMouseCapturedControl; ///< All mouse events will go to this ctrl only
SimObjectPtr<GuiControl> mMouseControl; ///< the control the mouse was last seen in unless some other one captured it
bool mMouseControlClicked; ///< whether the current ctrl has been clicked - used by helpctrl
U32 mPrevMouseTime; ///< this determines how long the mouse has been in the same control
U32 mNextMouseTime; ///< used for onMouseRepeat()
U32 mInitialMouseDelay; ///< also used for onMouseRepeat()
bool mMouseButtonDown; ///< Flag to determine if the button is depressed
bool mMouseRightButtonDown; ///< bool to determine if the right button is depressed
bool mMouseMiddleButtonDown; ///< Middle button flag
GuiEvent mLastEvent;
U8 mLastMouseClickCount;
S32 mLastMouseDownTime;
bool mLeftMouseLast;
bool mMiddleMouseLast;
virtual void findMouseControl(const GuiEvent &event);
virtual void refreshMouseControl();
/// @}
/// @name Keyboard Input
/// @{
GuiControl *keyboardControl; ///< All keyboard events will go to this ctrl first
U32 nextKeyTime;
/// Accelerator key map
struct AccKeyMap
{
GuiControl *ctrl;
U32 index;
U32 keyCode;
U32 modifier;
};
Vector <AccKeyMap> mAcceleratorMap;
//for tooltip rendering
U32 hoverControlStart;
GuiControl* hoverControl;
Point2I hoverPosition;
bool hoverPositionSet;
U32 hoverLeftControlTime;
/// @}
public:
DECLARE_CONOBJECT(GuiCanvas);
GuiCanvas();
virtual ~GuiCanvas();
/// @name Rendering methods
///
/// @{
/// Repaints the dirty regions of the canvas
/// @param preRenderOnly If set to true, only the onPreRender methods of all the GuiControls will be called
/// @param bufferSwap If set to true, it will swap buffers at the end. This is to support canvas-subclassing.
virtual void renderFrame(bool preRenderOnly, bool bufferSwap = true);
/// Repaints the entire canvas by calling resetUpdateRegions() and then renderFrame()
virtual void paint();
/// Adds a dirty area to the canvas so it will be updated on the next frame
/// @param pos Screen-coordinates of the upper-left hand corner of the dirty area
/// @param ext Width/height of the dirty area
virtual void addUpdateRegion(Point2I pos, Point2I ext);
/// Resets the update regions so that the next call to renderFrame will
/// repaint the whole canvas
virtual void resetUpdateRegions();
/// This builds a rectangle which encompasses all of the dirty regions to be
/// repainted
/// @param updateUnion (out) Rectangle which surrounds all dirty areas
virtual void buildUpdateUnion(RectI *updateUnion);
/// This will swap the buffers at the end of renderFrame. It was added for canvas
/// sub-classes in case they wanted to do some custom code before the buffer
/// flip occured.
virtual void swapBuffers();
/// @}
/// @name Canvas Content Management
/// @{
/// This sets the content control to something different
/// @param gui New content control
virtual void setContentControl(GuiControl *gui);
/// Returns the content control
virtual GuiControl *getContentControl();
/// Adds a dialog control onto the stack of dialogs
/// @param gui Dialog to add
/// @param layer Layer to put dialog on
virtual void pushDialogControl(GuiControl *gui, S32 layer = 0);
/// Removes a specific layer of dialogs
/// @param layer Layer to pop off from
virtual void popDialogControl(S32 layer = 0);
/// Removes a specific dialog control
/// @param gui Dialog to remove from the dialog stack
virtual void popDialogControl(GuiControl *gui);
///@}
/// This turns on/off front-buffer rendering
/// @param front True if all rendering should be done to the front buffer
virtual void setRenderFront(bool front) { mRenderFront = front; }
/// @name Cursor commands
/// A cursor can be on, but not be shown. If a cursor is not on, than it does not
/// process input.
/// @{
/// Sets the cursor for the canvas.
/// @param cursor New cursor to use.
virtual void setCursor(GuiCursor *cursor);
/// Returns true if the cursor is on.
virtual bool isCursorON() {return cursorON; }
/// Turns the cursor on or off.
/// @param onOff True if the cursor should be on.
virtual void setCursorON(bool onOff);
/// Sets the position of the cursor
/// @param pt Point, in screenspace for the cursor
virtual void setCursorPos(const Point2I &pt) { cursorPt.x = F32(pt.x); cursorPt.y = F32(pt.y); }
/// Returns the point, in screenspace, at which the cursor is located.
virtual Point2I getCursorPos() { return Point2I(S32(cursorPt.x), S32(cursorPt.y)); }
/// Enable/disable rendering of the cursor.
/// @param state True if we should render cursor
virtual void showCursor(bool state) { mShowCursor = state; }
/// Returns true if the cursor is being rendered.
virtual bool isCursorShown() { return(mShowCursor); }
/// @}
///used by the tooltip resource
Point2I getCursorExtent() { return defaultCursor->getExtent(); }
/// @name Input Processing
/// @{
/// Processes an input event
/// @see InputEvent
/// @param event Input event to process
virtual bool processInputEvent(const InputEvent *event);
/// Processes a mouse movement event
/// @see MouseMoveEvent
/// @param event Mouse move event to process
virtual void processMouseMoveEvent(const MouseMoveEvent *event);
/// @}
/// @name Mouse Methods
/// @{
/// When a control gets the mouse lock this means that that control gets
/// ALL mouse input and no other control recieves any input.
/// @param lockingControl Control to lock mouse to
virtual void mouseLock(GuiControl *lockingControl);
/// Unlocks the mouse from a control
/// @param lockingControl Control to unlock from
virtual void mouseUnlock(GuiControl *lockingControl);
/// Returns the control which the mouse is over
virtual GuiControl* getMouseControl() { return mMouseControl; }
/// Returns the control which the mouse is locked to if any
virtual GuiControl* getMouseLockedControl() { return mMouseCapturedControl; }
/// Returns true if the left mouse button is down
virtual bool mouseButtonDown(void) { return mMouseButtonDown; }
/// Returns true if the right mouse button is down
virtual bool mouseRightButtonDown(void) { return mMouseRightButtonDown; }
virtual void checkLockMouseMove(const GuiEvent &event);
/// @}
/// @name Mouse input methods
/// These events process the events before passing them down to the
/// controls they effect. This allows for things such as the input
/// locking and such.
///
/// Each of these methods corosponds to the action in it's method name
/// and processes the GuiEvent passd as a parameter
/// @{
virtual void rootMouseUp(const GuiEvent &event);
virtual void rootMouseDown(const GuiEvent &event);
virtual void rootMouseMove(const GuiEvent &event);
virtual void rootMouseDragged(const GuiEvent &event);
virtual void rootRightMouseDown(const GuiEvent &event);
virtual void rootRightMouseUp(const GuiEvent &event);
virtual void rootRightMouseDragged(const GuiEvent &event);
virtual void rootMiddleMouseDown(const GuiEvent &event);
virtual void rootMiddleMouseUp(const GuiEvent &event);
virtual void rootMiddleMouseDragged(const GuiEvent &event);
virtual void rootMouseWheelUp(const GuiEvent &event);
virtual void rootMouseWheelDown(const GuiEvent &event);
/// @}
/// @name Keyboard input methods
/// First responders
///
/// A first responder is a the GuiControl which responds first to input events
/// before passing them off for further processing.
/// @{
/// Moves the first responder to the next tabable controle
virtual void tabNext(void);
/// Moves the first responder to the previous tabable control
virtual void tabPrev(void);
/// Setups a keyboard accelerator which maps to a GuiControl.
///
/// @param ctrl GuiControl to map to.
/// @param index
/// @param keyCode Key code.
/// @param modifier Shift, ctrl, etc.
virtual void addAcceleratorKey(GuiControl *ctrl, U32 index, U32 keyCode, U32 modifier);
/// Sets the first responder.
/// @param firstResponder Control to designate as first responder
virtual void setFirstResponder(GuiControl *firstResponder);
/// @}
};
extern GuiCanvas *Canvas;
#endif

1389
engine/gui/core/guiControl.cc Executable file

File diff suppressed because it is too large Load Diff

569
engine/gui/core/guiControl.h Executable file
View File

@ -0,0 +1,569 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUICONTROL_H_
#define _GUICONTROL_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _MRECT_H_
#include "math/mRect.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
#ifndef _LANG_H_
#include "i18n/lang.h"
#endif
class GuiCanvas;
class GuiEditCtrl;
/// Root class for all GUI controls in Torque.
///
/// @see GUI for an overview of the Torque GUI system.
///
/// @section GuiControl_Intro Introduction
///
/// GuiControl is the base class for GUI controls in Torque. It provides these
/// basic areas of functionality:
/// - Inherits from SimGroup, so that controls can have children.
/// - Interfacing with a GuiControlProfile.
/// - An abstraction from the details of handling user input
/// and so forth, providing friendly hooks like onMouseEnter(), onMouseMove(),
/// and onMouseLeave(), onKeyDown(), and so forth.
/// - An abstraction from the details of rendering and resizing.
/// - Helper functions to manipulate the mouse (mouseLock and
/// mouseUnlock), and convert coordinates (localToGlobalCoord() and
/// globalToLocalCoord()).
///
/// @ref GUI has an overview of the GUI system.
///
/// @nosubgrouping
class GuiControl : public SimGroup
{
private:
typedef SimGroup Parent;
public:
/// @name Control State
/// @{
GuiControlProfile *mProfile;
GuiControlProfile *mTooltipProfile;
bool mVisible;
bool mActive;
bool mAwake;
bool mSetFirstResponder;
S32 mLayer;
RectI mBounds;
Point2I mMinExtent;
StringTableEntry mLangTableName;
LangTable *mLangTable;
static bool smDesignTime; ///< static GuiControl boolean that specifies if the GUI Editor is active
/// @}
/// @name Design Time Editor Access
/// @{
static GuiEditCtrl *smEditorHandle; ///< static GuiEditCtrl pointer that gives controls access to editor-NULL if editor is closed
/// @}
/// @name Keyboard Input
/// @{
GuiControl *mFirstResponder;
static GuiControl *smPrevResponder;
static GuiControl *smCurResponder;
/// @}
enum horizSizingOptions
{
horizResizeRight = 0, ///< fixed on the left and width
horizResizeWidth, ///< fixed on the left and right
horizResizeLeft, ///< fixed on the right and width
horizResizeCenter,
horizResizeRelative ///< resize relative
};
enum vertSizingOptions
{
vertResizeBottom = 0, ///< fixed on the top and in height
vertResizeHeight, ///< fixed on the top and bottom
vertResizeTop, ///< fixed in height and on the bottom
vertResizeCenter,
vertResizeRelative ///< resize relative
};
protected:
/// @name Control State
/// @{
S32 mHorizSizing; ///< Set from horizSizingOptions.
S32 mVertSizing; ///< Set from vertSizingOptions.
StringTableEntry mConsoleVariable;
StringTableEntry mConsoleCommand;
StringTableEntry mAltConsoleCommand;
StringTableEntry mAcceleratorKey;
StringTableEntry mTooltip;
/// @}
/// @name Console
/// The console variable collection of functions allows a console variable to be bound to the GUI control.
///
/// This allows, say, an edit field to be bound to '$foo'. The value of the console
/// variable '$foo' would then be equal to the text inside the text field. Changing
/// either changes the other.
/// @{
/// Sets the value of the console variable bound to this control
/// @param value String value to assign to control's console variable
void setVariable(const char *value);
/// Sets the value of the console variable bound to this control
/// @param value Integer value to assign to control's console variable
void setIntVariable(S32 value);
/// Sets the value of the console variable bound to this control
/// @param value Float value to assign to control's console variable
void setFloatVariable(F32 value);
const char* getVariable(); ///< Returns value of control's bound variable as a string
S32 getIntVariable(); ///< Returns value of control's bound variable as a integer
F32 getFloatVariable(); ///< Returns value of control's bound variable as a float
public:
/// Set the name of the console variable which this GuiObject is bound to
/// @param variable Variable name
void setConsoleVariable(const char *variable);
/// Set the name of the console function bound to, such as a script function
/// a button calls when clicked.
/// @param newCmd Console function to attach to this GuiControl
void setConsoleCommand(const char *newCmd);
const char * getConsoleCommand(); ///< Returns the name of the function bound to this GuiControl
LangTable *getGUILangTable(void);
const UTF8 *getGUIString(S32 id);
/// @}
/// @name Editor
/// These functions are used by the GUI Editor
/// @{
/// Sets the size of the GuiControl
/// @param horz Width of the control
/// @param vert Height of the control
void setSizing(S32 horz, S32 vert);
/// @}
public:
/// @name Initialization
/// @{
DECLARE_CONOBJECT(GuiControl);
GuiControl();
virtual ~GuiControl();
static void initPersistFields();
/// @}
/// @name Accessors
/// @{
const Point2I& getPosition() { return mBounds.point; } ///< Returns position of the control
const Point2I& getExtent() { return mBounds.extent; } ///< Returns extents of the control
const Point2I& getMinExtent() { return mMinExtent; } ///< Returns minimum size the control can be
const S32 getLeft() { return mBounds.point.x; } ///< Returns the X position of the control
const S32 getTop() { return mBounds.point.y; } ///< Returns the Y position of the control
const S32 getWidth() { return mBounds.extent.x; } ///< Returns the width of the control
const S32 getHeight() { return mBounds.extent.y; } ///< Returns the height of the control
/// @}
/// @name Flags
/// @{
/// Sets the visibility of the control
/// @param value True if object should be visible
virtual void setVisible(bool value);
inline bool isVisible() { return mVisible; } ///< Returns true if the object is visible
/// Sets the status of this control as active and responding or inactive
/// @param value True if this is active
void setActive(bool value);
bool isActive() { return mActive; } ///< Returns true if this control is active
bool isAwake() { return mAwake; } ///< Returns true if this control is awake
/// @}
/// Get information about the size of a scroll line.
///
/// @param rowHeight The height, in pixels, of a row
/// @param columnWidth The width, in pixels, of a column
virtual void getScrollLineSizes(U32 *rowHeight, U32 *columnWidth);
/// Get information about the cursor.
/// @param cursor Cursor information will be stored here
/// @param showCursor Will be set to true if the cursor is visible
/// @param lastGuiEvent GuiEvent containing cursor position and modifyer keys (ie ctrl, shift, alt etc)
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
/// @name Children
/// @{
/// Adds an object as a child of this object.
/// @param obj New child object of this control
void addObject(SimObject *obj);
/// Removes a child object from this control.
/// @param obj Object to remove from this control
void removeObject(SimObject *obj);
GuiControl *getParent(); ///< Returns the control which owns this one.
GuiCanvas *getRoot(); ///< Returns the root canvas of this control.
/// @}
/// @name Coordinates
/// @{
/// Translates local coordinates (wrt this object) into global coordinates
///
/// @param src Local coordinates to translate
Point2I localToGlobalCoord(const Point2I &src);
/// Returns global coordinates translated into local space
///
/// @param src Global coordinates to translate
Point2I globalToLocalCoord(const Point2I &src);
/// @}
/// @name Resizing
/// @{
/// Changes the size and/or position of this control
/// @param newPosition New position of this control
/// @param newExtent New size of this control
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
/// Changes the position of this control
/// @param newPosition New position of this control
virtual void setPosition( const Point2I &newPosition );
/// Changes the size of this control
/// @param newExtent New size of this control
virtual void setExtent( const Point2I &newExtent );
/// Changes the X position of this control
/// @param newXPosition New X Position of this control
virtual void setLeft( S32 newLeft );
/// Changes the Y position of this control
/// @param newYPosition New Y Position of this control
virtual void setTop( S32 newTop );
/// Changes the width of this control
/// @param newWidth New width of this control
virtual void setWidth( S32 newWidth );
/// Changes the height of this control
/// @param newHeight New Height of this control
virtual void setHeight( S32 newHeight );
/// Called when a child control of the object is resized
/// @param child Child object
virtual void childResized(GuiControl *child);
/// Called when this objects parent is resized
/// @param oldParentExtent The old size of the parent object
/// @param newParentExtent The new size of the parent object
virtual void parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent);
/// @}
/// @name Rendering
/// @{
/// Called when this control is to render itself
/// @param offset The location this control is to begin rendering
/// @param updateRect The screen area this control has drawing access to
virtual void onRender(Point2I offset, const RectI &updateRect);
virtual bool renderTooltip(Point2I cursorPos);
/// Called when this control should render its children
/// @param offset The location this control is to begin rendering
/// @param updateRect The screen area this control has drawing access to
void renderChildControls(Point2I offset, const RectI &updateRect);
/// Sets the area (local coordinates) this control wants refreshed each frame
/// @param pos UpperLeft point on rectangle of refresh area
/// @param ext Extent of update rect
void setUpdateRegion(Point2I pos, Point2I ext);
/// Sets the update area of the control to encompass the whole control
void setUpdate();
/// @}
//child hierarchy calls
void awaken(); ///< Called when this control and its children have been wired up.
void sleep(); ///< Called when this control is no more.
void preRender(); ///< Prerender this control and all its children.
/// @name Events
///
/// If you subclass these, make sure to call the Parent::'s versions.
///
/// @{
/// Called when this object is asked to wake up returns true if it's actually awake at the end
virtual bool onWake();
/// Called when this object is asked to sleep
virtual void onSleep();
/// Do special pre-render proecessing
virtual void onPreRender();
/// Called when this object is removed
virtual void onRemove();
/// Called when one of this objects children is removed
virtual void onChildRemoved( GuiControl *child );
/// Called when this object is added to the scene
bool onAdd();
/// Called when this object has a new child
virtual void onChildAdded( GuiControl *child );
/// @}
/// @name Console
/// @{
/// Returns the value of the variable bound to this object
virtual const char *getScriptValue();
/// Sets the value of the variable bound to this object
virtual void setScriptValue(const char *value);
/// @}
/// @name Input (Keyboard/Mouse)
/// @{
/// This function will return true if the provided coordinates (wrt parent object) are
/// within the bounds of this control
/// @param parentCoordPoint Coordinates to test
virtual bool pointInControl(const Point2I& parentCoordPoint);
/// Returns true if the global cursor is inside this control
bool cursorInControl();
/// Returns the control which the provided point is under, with layering
/// @param pt Point to test
/// @param initialLayer Layer of gui objects to begin the search
virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
/// Lock the mouse within the provided control
/// @param lockingControl Control to lock the mouse within
void mouseLock(GuiControl *lockingControl);
/// Turn on mouse locking with last used lock control
void mouseLock();
/// Unlock the mouse
void mouseUnlock();
/// Returns true if the mouse is locked
bool isMouseLocked();
/// @}
/// General input handler.
virtual bool onInputEvent(const InputEvent &event);
/// @name Mouse Events
/// These functions are called when the input event which is
/// in the name of the function occurs.
/// @{
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);
/// Called when a mouseDown event occurs on a control and the GUI editor is active
/// @param event the GuiEvent which caused the call to this function
/// @param offset the offset which is representative of the units x and y that the editor takes up on screen
virtual void onMouseDownEditor(const GuiEvent &event, Point2I offset);
/// Called when a rightMouseDown event occurs on a control and the GUI editor is active
/// @param event the GuiEvent which caused the call to this function
/// @param offset the offset which is representative of the units x and y that the editor takes up on screen
virtual void onRightMouseDownEditor(const GuiEvent &event, Point2I offset);
/// @}
/// @name Tabs
/// @{
/// Find the first tab-accessable child of this control
virtual GuiControl* findFirstTabable();
/// Find the last tab-accessable child of this control
/// @param firstCall Set to true to clear the global previous responder
virtual GuiControl* findLastTabable(bool firstCall = true);
/// Find previous tab-accessable control with respect to the provided one
/// @param curResponder Current control
/// @param firstCall Set to true to clear the global previous responder
virtual GuiControl* findPrevTabable(GuiControl *curResponder, bool firstCall = true);
/// Find next tab-accessable control with regards to the provided control.
///
/// @param curResponder Current control
/// @param firstCall Set to true to clear the global current responder
virtual GuiControl* findNextTabable(GuiControl *curResponder, bool firstCall = true);
/// @}
/// Returns true if the provided control is a child (grandchild, or greatgrandchild) of this one.
///
/// @param child Control to test
virtual bool ControlIsChild(GuiControl *child);
/// @name First Responder
/// A first responder is the control which reacts first, in it's responder chain, to keyboard events
/// The responder chain is set for each parent and so there is only one first responder amongst it's
/// children.
/// @{
/// Sets the first responder for child controls
/// @param firstResponder First responder for this chain
virtual void setFirstResponder(GuiControl *firstResponder);
/// Sets up this control to be the first in it's group to respond to an input event
/// @param value True if this should be a first responder
virtual void makeFirstResponder(bool value);
/// Returns true if this control is a first responder
bool isFirstResponder();
/// Sets this object to be a first responder
void setFirstResponder();
/// Clears the first responder for this chain
void clearFirstResponder();
/// Returns the first responder for this chain
GuiControl *getFirstResponder() { return mFirstResponder; }
/// Occurs when the first responder for this chain is lost
virtual void onLoseFirstResponder();
/// @}
/// @name Keyboard Events
/// @{
/// Adds the accelerator key for this object to the canvas
void addAcceleratorKey();
/// Adds this control's accelerator key to the accelerator map, and
/// recursively tells all children to do the same.
virtual void buildAcceleratorMap();
/// Occurs when the accelerator key for this control is pressed
///
/// @param index Index in the acclerator map of the key
virtual void acceleratorKeyPress(U32 index);
/// Occurs when the accelerator key for this control is released
///
/// @param index Index in the acclerator map of the key
virtual void acceleratorKeyRelease(U32 index);
/// Happens when a key is depressed
/// @param event Event descriptor (which contains the key)
virtual bool onKeyDown(const GuiEvent &event);
/// Happens when a key is released
/// @param event Event descriptor (which contains the key)
virtual bool onKeyUp(const GuiEvent &event);
/// Happens when the same key that was pressed last press is pressed again
/// @param event Event descriptor (which contains the key)
virtual bool onKeyRepeat(const GuiEvent &event);
/// @}
/// Sets the control profile for this control.
///
/// @see GuiControlProfile
/// @param prof Control profile to apply
void setControlProfile(GuiControlProfile *prof);
/// Occurs when this control performs its "action"
virtual void onAction();
/// @name Peer Messaging
/// Used to send a message to other GUIControls which are children of the same parent.
///
/// This is mostly used by radio controls.
/// @{
void messageSiblings(S32 message); ///< Send a message to all siblings
virtual void onMessage(GuiControl *sender, S32 msg); ///< Receive a message from another control
/// @}
/// @name Canvas Events
/// Functions called by the canvas
/// @{
/// Called if this object is a dialog, when it is added to the visible layers
virtual void onDialogPush();
/// Called if this object is a dialog, when it is removed from the visible layers
virtual void onDialogPop();
/// @}
/// Renders justified text using the profile.
///
/// @note This should move into the graphics library at some point
void renderJustifiedText(Point2I offset, Point2I extent, const char *text);
void inspectPostApply();
void inspectPreApply();
};
#endif

View File

@ -0,0 +1,134 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (c) 2002 GarageGames.Com
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/core/guiTypes.h"
#include "core/color.h"
#include "math/mRect.h"
static ColorI colorLightGray(192, 192, 192);
static ColorI colorGray(128, 128, 128);
static ColorI colorDarkGray(64, 64, 64);
static ColorI colorWhite(255,255,255);
static ColorI colorBlack(0,0,0);
void renderRaisedBox(RectI &bounds, GuiControlProfile *profile)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
dglDrawRectFill( bounds, profile->mFillColor);
dglDrawLine(l, t, l, b - 1, profile->mBevelColorHL);
dglDrawLine(l, t, r - 1, t, profile->mBevelColorHL);
dglDrawLine(l, b, r, b, profile->mBevelColorLL);
dglDrawLine(r, b - 1, r, t, profile->mBevelColorLL);
dglDrawLine(l + 1, b - 1, r - 1, b - 1, profile->mBorderColor);
dglDrawLine(r - 1, b - 2, r - 1, t + 1, profile->mBorderColor);
}
void renderSlightlyRaisedBox(RectI &bounds, GuiControlProfile *profile)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
dglDrawRectFill( bounds, profile->mFillColor);
dglDrawLine(l, t, l, b, profile->mBevelColorHL);
dglDrawLine(l, t, r, t, profile->mBevelColorHL);
dglDrawLine(l + 1, b, r, b, profile->mBorderColor);
dglDrawLine(r, t + 1, r, b - 1, profile->mBorderColor);
}
void renderLoweredBox(RectI &bounds, GuiControlProfile *profile)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
dglDrawRectFill( bounds, profile->mFillColor);
dglDrawLine(l, b, r, b, profile->mBevelColorHL);
dglDrawLine(r, b - 1, r, t, profile->mBevelColorHL);
dglDrawLine(l, t, r - 1, t, profile->mBevelColorLL);
dglDrawLine(l, t + 1, l, b - 1, profile->mBevelColorLL);
dglDrawLine(l + 1, t + 1, r - 2, t + 1, profile->mBorderColor);
dglDrawLine(l + 1, t + 2, l + 1, b - 2, profile->mBorderColor);
}
void renderSlightlyLoweredBox(RectI &bounds, GuiControlProfile *profile)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
dglDrawRectFill( bounds, profile->mFillColor);
dglDrawLine(l, b, r, b, profile->mBevelColorHL);
dglDrawLine(r, t, r, b - 1, profile->mBevelColorHL);
dglDrawLine(l, t, l, b - 1, profile->mBorderColor);
dglDrawLine(l + 1, t, r - 1, t, profile->mBorderColor);
}
void renderBorder(RectI &bounds, GuiControlProfile *profile)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
switch(profile->mBorder)
{
case 1:
dglDrawRect(bounds, profile->mBorderColor);
break;
case 2:
dglDrawLine(l + 1, t + 1, l + 1, b - 2, profile->mBevelColorHL);
dglDrawLine(l + 2, t + 1, r - 2, t + 1, profile->mBevelColorHL);
dglDrawLine(r, t, r, b, profile->mBevelColorHL);
dglDrawLine(l, b, r - 1, b, profile->mBevelColorHL);
dglDrawLine(l, t, r - 1, t, profile->mBorderColorNA);
dglDrawLine(l, t + 1, l, b - 1, profile->mBorderColorNA);
dglDrawLine(l + 1, b - 1, r - 1, b - 1, profile->mBorderColorNA);
dglDrawLine(r - 1, t + 1, r - 1, b - 2, profile->mBorderColorNA);
break;
case 3:
dglDrawLine(l, b, r, b, profile->mBevelColorHL);
dglDrawLine(r, t, r, b - 1, profile->mBevelColorHL);
dglDrawLine(l + 1, b - 1, r - 1, b - 1, profile->mFillColor);
dglDrawLine(r - 1, t + 1, r - 1, b - 2, profile->mFillColor);
dglDrawLine(l, t, l, b - 1, profile->mBorderColorNA);
dglDrawLine(l + 1, t, r - 1, t, profile->mBorderColorNA);
dglDrawLine(l + 1, t + 1, l + 1, b - 2, profile->mBevelColorLL);
dglDrawLine(l + 2, t + 1, r - 2, t + 1, profile->mBevelColorLL);
break;
case 4:
dglDrawLine(l, t, l, b - 1, profile->mBevelColorHL);
dglDrawLine(l + 1, t, r, t, profile->mBevelColorHL);
dglDrawLine(l, b, r, b, profile->mBevelColorLL);
dglDrawLine(r, t + 1, r, b - 1, profile->mBevelColorLL);
dglDrawLine(l + 1, b - 1, r - 1, b - 1, profile->mBorderColor);
dglDrawLine(r - 1, t + 1, r - 1, b - 2, profile->mBorderColor);
break;
case 5:
renderFilledBorder( bounds, profile );
break;
}
}
void renderFilledBorder( RectI &bounds, GuiControlProfile *profile )
{
renderFilledBorder( bounds, profile->mBorderColorHL, profile->mFillColor );
}
void renderFilledBorder( RectI &bounds, ColorI &borderColor, ColorI &fillColor )
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
RectI fillBounds = bounds;
fillBounds.inset( 1, 1 );
dglDrawRect( bounds, borderColor );
dglDrawRectFill( fillBounds, fillColor );
}

View File

@ -0,0 +1,19 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (c) 2002 GarageGames.Com
//-----------------------------------------------------------------------------
#ifndef _H_GUIDEFAULTCONTROLRENDER_
#define _H_GUIDEFAULTCONTROLRENDER_
class GuiControlProfile;
void renderRaisedBox(RectI &bounds, GuiControlProfile *profile);
void renderSlightlyRaisedBox(RectI &bounds, GuiControlProfile *profile);
void renderLoweredBox(RectI &bounds, GuiControlProfile *profile);
void renderSlightlyLoweredBox(RectI &bounds, GuiControlProfile *profile);
void renderBorder(RectI &bounds, GuiControlProfile *profile);
void renderFilledBorder( RectI &bounds, GuiControlProfile *profile );
void renderFilledBorder( RectI &bounds, ColorI &borderColor, ColorI &fillColor );
#endif

160
engine/gui/core/guiTSControl.cc Executable file
View File

@ -0,0 +1,160 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/core/guiTSControl.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneLighting.h"
#include "sceneGraph/sceneGraph.h"
IMPLEMENT_CONOBJECT(GuiTSCtrl);
U32 GuiTSCtrl::smFrameCount = 0;
GuiTSCtrl::GuiTSCtrl()
{
mApplyFilterToChildren = true;
mCameraZRot = 0;
mForceFOV = 0;
for(U32 i = 0; i < 16; i++)
{
mSaveModelview[i] = 0;
mSaveProjection[i] = 0;
}
mSaveModelview[0] = 1;
mSaveModelview[5] = 1;
mSaveModelview[10] = 1;
mSaveModelview[15] = 1;
mSaveProjection[0] = 1;
mSaveProjection[5] = 1;
mSaveProjection[10] = 1;
mSaveProjection[15] = 1;
mSaveViewport[0] = 0;
mSaveViewport[1] = 0;
mSaveViewport[2] = 2;
mSaveViewport[3] = 2;
}
void GuiTSCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("applyFilterToChildren", TypeBool, Offset(mApplyFilterToChildren, GuiTSCtrl));
addField("cameraZRot", TypeF32, Offset(mCameraZRot, GuiTSCtrl));
addField("forceFOV", TypeF32, Offset(mForceFOV, GuiTSCtrl));
}
void GuiTSCtrl::consoleInit()
{
Con::addVariable("$TSControl::frameCount", TypeS32, &smFrameCount);
}
void GuiTSCtrl::onPreRender()
{
setUpdate();
}
bool GuiTSCtrl::processCameraQuery(CameraQuery *)
{
return false;
}
void GuiTSCtrl::renderWorld(const RectI& /*updateRect*/)
{
}
bool GuiTSCtrl::project(const Point3F &pt, Point3F *dest)
{
GLdouble winx, winy, winz;
GLint result = gluProject(pt.x, pt.y, pt.z,
mSaveModelview, mSaveProjection, mSaveViewport,
&winx, &winy, &winz);
if(result == GL_FALSE || winz < 0 || winz > 1)
return false;
dest->set(winx, winy, winz);
return true;
}
bool GuiTSCtrl::unproject(const Point3F &pt, Point3F *dest)
{
GLdouble objx, objy, objz;
GLint result = gluUnProject(pt.x, pt.y, pt.z,
mSaveModelview, mSaveProjection, mSaveViewport,
&objx, &objy, &objz);
if(result == GL_FALSE)
return false;
dest->set(objx, objy, objz);
return true;
}
void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if(SceneLighting::isLighting())
return;
CameraQuery newCam = mLastCameraQuery;
if(processCameraQuery(&newCam))
mLastCameraQuery = newCam;
if(mForceFOV != 0)
newCam.fov = mDegToRad(mForceFOV);
if(mCameraZRot)
{
MatrixF rotMat(EulerF(0, 0, mDegToRad(mCameraZRot)));
newCam.cameraMatrix.mul(rotMat);
}
// set up the camera and viewport stuff:
F32 left, right, top, bottom;
if (newCam.ortho)
{
left = -newCam.leftRight;
right = newCam.leftRight;
top = newCam.topBottom;
bottom = -newCam.topBottom;
}
else
{
F32 wwidth = newCam.nearPlane * mTan(newCam.fov / 2);
F32 wheight = F32(mBounds.extent.y) / F32(mBounds.extent.x) * wwidth;
F32 hscale = wwidth * 2 / F32(mBounds.extent.x);
F32 vscale = wheight * 2 / F32(mBounds.extent.y);
left = (updateRect.point.x - offset.x) * hscale - wwidth;
right = (updateRect.point.x + updateRect.extent.x - offset.x) * hscale - wwidth;
top = wheight - vscale * (updateRect.point.y - offset.y);
bottom = wheight - vscale * (updateRect.point.y + updateRect.extent.y - offset.y);
}
dglSetViewport(updateRect);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
dglSetFrustum(left, right, bottom, top, newCam.nearPlane, (gClientSceneGraph ? (F64)gClientSceneGraph->getVisibleDistanceMod() : newCam.farPlane), newCam.ortho);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
newCam.cameraMatrix.inverse();
dglMultMatrix(&newCam.cameraMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, mSaveProjection);
glGetDoublev(GL_MODELVIEW_MATRIX, mSaveModelview);
mSaveViewport[0] = updateRect.point.x;
mSaveViewport[1] = updateRect.point.y + updateRect.extent.y;
mSaveViewport[2] = updateRect.extent.x;
mSaveViewport[3] = -updateRect.extent.y;
renderWorld(updateRect);
if(mApplyFilterToChildren)
renderChildControls(offset, updateRect);
smFrameCount++;
}

73
engine/gui/core/guiTSControl.h Executable file
View File

@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUITSCONTROL_H_
#define _GUITSCONTROL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _DGL_H_
#include "dgl/dgl.h"
#endif
struct CameraQuery
{
CameraQuery()
{
ortho = false;
}
SimObject* object;
F32 nearPlane;
F32 farPlane;
F32 fov;
MatrixF cameraMatrix;
F32 leftRight;
F32 topBottom;
bool ortho;
//Point3F position;
//Point3F viewVector;
//Point3F upVector;
};
class GuiTSCtrl : public GuiControl
{
typedef GuiControl Parent;
protected:
GLdouble mSaveModelview[16];
GLdouble mSaveProjection[16];
GLint mSaveViewport[4];
static U32 smFrameCount;
F32 mCameraZRot;
F32 mForceFOV;
bool mApplyFilterToChildren;
public:
CameraQuery mLastCameraQuery;
GuiTSCtrl();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
virtual bool processCameraQuery(CameraQuery *query);
virtual void renderWorld(const RectI &updateRect);
static void initPersistFields();
static void consoleInit();
bool project(const Point3F &pt, Point3F *dest); // returns screen space X,Y and Z for world space point
bool unproject(const Point3F &pt, Point3F *dest); // returns world space point for X, Y and Z
DECLARE_CONOBJECT(GuiTSCtrl);
};
#endif

371
engine/gui/core/guiTypes.cc Executable file
View File

@ -0,0 +1,371 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/types.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/gNewFont.h"
#include "dgl/dgl.h"
#include "gui/core/guiTypes.h"
#include "dgl/gBitmap.h"
#include "dgl/gTexManager.h"
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
IMPLEMENT_CONOBJECT(GuiCursor);
GuiCursor::GuiCursor()
{
mHotSpot.set(0,0);
mRenderOffset.set(0.0f,0.0f);
mExtent.set(1,1);
}
GuiCursor::~GuiCursor()
{
}
void GuiCursor::initPersistFields()
{
Parent::initPersistFields();
addField("hotSpot", TypePoint2I, Offset(mHotSpot, GuiCursor));
addField("renderOffset",TypePoint2F, Offset(mRenderOffset, GuiCursor));
addField("bitmapName", TypeFilename, Offset(mBitmapName, GuiCursor));
}
bool GuiCursor::onAdd()
{
if(!Parent::onAdd())
return false;
Sim::getGuiDataGroup()->addObject(this);
return true;
}
void GuiCursor::onRemove()
{
Parent::onRemove();
}
void GuiCursor::render(const Point2I &pos)
{
if (!mTextureHandle && mBitmapName && mBitmapName[0])
{
mTextureHandle = TextureHandle(mBitmapName, BitmapTexture);
if(!mTextureHandle)
return;
mExtent.set(mTextureHandle.getWidth(), mTextureHandle.getHeight());
}
// Render the cursor centered according to dimensions of texture
S32 texWidth = mTextureHandle.getWidth();
S32 texHeight = mTextureHandle.getWidth();
Point2I renderPos = pos;
renderPos.x -= ( texWidth * mRenderOffset.x );
renderPos.y -= ( texHeight * mRenderOffset.y );
dglClearBitmapModulation();
dglDrawBitmap(mTextureHandle, renderPos);
}
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiControlProfile);
static EnumTable::Enums alignEnums[] =
{
{ GuiControlProfile::LeftJustify, "left" },
{ GuiControlProfile::CenterJustify, "center" },
{ GuiControlProfile::RightJustify, "right" }
};
static EnumTable gAlignTable(3, &alignEnums[0]);
static EnumTable::Enums charsetEnums[]=
{
{ TGE_ANSI_CHARSET, "ANSI" },
{ TGE_SYMBOL_CHARSET, "SYMBOL" },
{ TGE_SHIFTJIS_CHARSET, "SHIFTJIS" },
{ TGE_HANGEUL_CHARSET, "HANGEUL" },
{ TGE_HANGUL_CHARSET, "HANGUL" },
{ TGE_GB2312_CHARSET, "GB2312" },
{ TGE_CHINESEBIG5_CHARSET, "CHINESEBIG5" },
{ TGE_OEM_CHARSET, "OEM" },
{ TGE_JOHAB_CHARSET, "JOHAB" },
{ TGE_HEBREW_CHARSET, "HEBREW" },
{ TGE_ARABIC_CHARSET, "ARABIC" },
{ TGE_GREEK_CHARSET, "GREEK" },
{ TGE_TURKISH_CHARSET, "TURKISH" },
{ TGE_VIETNAMESE_CHARSET, "VIETNAMESE" },
{ TGE_THAI_CHARSET, "THAI" },
{ TGE_EASTEUROPE_CHARSET, "EASTEUROPE" },
{ TGE_RUSSIAN_CHARSET, "RUSSIAN" },
{ TGE_MAC_CHARSET, "MAC" },
{ TGE_BALTIC_CHARSET, "BALTIC" },
};
#define NUMCHARSETENUMS (sizeof(charsetEnums) / sizeof(EnumTable::Enums))
static EnumTable gCharsetTable(NUMCHARSETENUMS, &charsetEnums[0]);
StringTableEntry GuiControlProfile::sFontCacheDirectory = "";
GuiControlProfile::GuiControlProfile(void) :
mFontColor(mFontColors[BaseColor]),
mFontColorHL(mFontColors[ColorHL]),
mFontColorNA(mFontColors[ColorNA]),
mFontColorSEL(mFontColors[ColorSEL])
{
mRefCount = 0;
mBitmapArrayRects.clear();
mBorderThickness = 1;
mMouseOverSelected = false;
mBitmapName = NULL;
mFontCharset = TGE_ANSI_CHARSET;
GuiControlProfile *def = dynamic_cast<GuiControlProfile*>(Sim::findObject("GuiDefaultProfile"));
if (def)
{
mTabable = def->mTabable;
mCanKeyFocus = def->mCanKeyFocus;
mOpaque = def->mOpaque;
mFillColor = def->mFillColor;
mFillColorHL = def->mFillColorHL;
mFillColorNA = def->mFillColorNA;
mBorder = def->mBorder;
mBorderThickness = def->mBorderThickness;
mBorderColor = def->mBorderColor;
mBorderColorHL = def->mBorderColorHL;
mBorderColorNA = def->mBorderColorNA;
mBevelColorHL = def->mBevelColorHL;
mBevelColorLL = def->mBevelColorLL;
// default font
mFontType = def->mFontType;
mFontSize = def->mFontSize;
mFontCharset = def->mFontCharset;
for(U32 i = 0; i < 10; i++)
mFontColors[i] = def->mFontColors[i];
// default bitmap
mBitmapName = def->mBitmapName;
mTextOffset = def->mTextOffset;
// default sound
mSoundButtonDown = def->mSoundButtonDown;
mSoundButtonOver = def->mSoundButtonOver;
//used by GuiTextCtrl
mModal = def->mModal;
mAlignment = def->mAlignment;
mAutoSizeWidth = def->mAutoSizeWidth;
mAutoSizeHeight= def->mAutoSizeHeight;
mReturnTab = def->mReturnTab;
mNumbersOnly = def->mNumbersOnly;
mCursorColor = def->mCursorColor;
}
}
GuiControlProfile::~GuiControlProfile()
{
}
void GuiControlProfile::initPersistFields()
{
Parent::initPersistFields();
addField("tab", TypeBool, Offset(mTabable, GuiControlProfile));
addField("canKeyFocus", TypeBool, Offset(mCanKeyFocus, GuiControlProfile));
addField("mouseOverSelected", TypeBool, Offset(mMouseOverSelected, GuiControlProfile));
addField("modal", TypeBool, Offset(mModal, GuiControlProfile));
addField("opaque", TypeBool, Offset(mOpaque, GuiControlProfile));
addField("fillColor", TypeColorI, Offset(mFillColor, GuiControlProfile));
addField("fillColorHL", TypeColorI, Offset(mFillColorHL, GuiControlProfile));
addField("fillColorNA", TypeColorI, Offset(mFillColorNA, GuiControlProfile));
addField("border", TypeS32, Offset(mBorder, GuiControlProfile));
addField("borderThickness",TypeS32, Offset(mBorderThickness, GuiControlProfile));
addField("borderColor", TypeColorI, Offset(mBorderColor, GuiControlProfile));
addField("borderColorHL", TypeColorI, Offset(mBorderColorHL, GuiControlProfile));
addField("borderColorNA", TypeColorI, Offset(mBorderColorNA, GuiControlProfile));
addField("bevelColorHL", TypeColorI, Offset(mBevelColorHL, GuiControlProfile));
addField("bevelColorLL", TypeColorI, Offset(mBevelColorLL, GuiControlProfile));
addField("fontType", TypeString, Offset(mFontType, GuiControlProfile));
addField("fontSize", TypeS32, Offset(mFontSize, GuiControlProfile));
addField("fontCharset", TypeEnum, Offset(mFontCharset, GuiControlProfile), 1, &gCharsetTable);
addField("fontColors", TypeColorI, Offset(mFontColors, GuiControlProfile), 10);
addField("fontColor", TypeColorI, Offset(mFontColors[BaseColor], GuiControlProfile));
addField("fontColorHL", TypeColorI, Offset(mFontColors[ColorHL], GuiControlProfile));
addField("fontColorNA", TypeColorI, Offset(mFontColors[ColorNA], GuiControlProfile));
addField("fontColorSEL", TypeColorI, Offset(mFontColors[ColorSEL], GuiControlProfile));
addField("fontColorLink", TypeColorI, Offset(mFontColors[ColorUser0], GuiControlProfile));
addField("fontColorLinkHL", TypeColorI, Offset(mFontColors[ColorUser1], GuiControlProfile));
addField("justify", TypeEnum, Offset(mAlignment, GuiControlProfile), 1, &gAlignTable);
addField("textOffset", TypePoint2I, Offset(mTextOffset, GuiControlProfile));
addField("autoSizeWidth", TypeBool, Offset(mAutoSizeWidth, GuiControlProfile));
addField("autoSizeHeight",TypeBool, Offset(mAutoSizeHeight, GuiControlProfile));
addField("returnTab", TypeBool, Offset(mReturnTab, GuiControlProfile));
addField("numbersOnly", TypeBool, Offset(mNumbersOnly, GuiControlProfile));
addField("cursorColor", TypeColorI, Offset(mCursorColor, GuiControlProfile));
addField("bitmap", TypeFilename, Offset(mBitmapName, GuiControlProfile));
addField("soundButtonDown", TypeAudioProfilePtr, Offset(mSoundButtonDown, GuiControlProfile));
addField("soundButtonOver", TypeAudioProfilePtr, Offset(mSoundButtonOver, GuiControlProfile));
}
bool GuiControlProfile::onAdd()
{
if(!Parent::onAdd())
return false;
Sim::getGuiDataGroup()->addObject(this);
return true;
}
S32 GuiControlProfile::constructBitmapArray()
{
if(mBitmapArrayRects.size())
return mBitmapArrayRects.size();
GBitmap *bmp = mTextureHandle.getBitmap();
//get the separator color
ColorI sepColor;
if ( !bmp || !bmp->getColor( 0, 0, sepColor ) )
{
Con::errorf("Failed to create bitmap array from %s for profile %s - couldn't ascertain seperator color!", mBitmapName, getName());
AssertFatal( false, avar("Failed to create bitmap array from %s for profile %s - couldn't ascertain seperator color!", mBitmapName, getName()));
return 0;
}
//now loop through all the scroll pieces, and find the bounding rectangle for each piece in each state
S32 curY = 0;
// ascertain the height of this row...
ColorI color;
mBitmapArrayRects.clear();
while(curY < bmp->getHeight())
{
// skip any sep colors
bmp->getColor( 0, curY, color);
if(color == sepColor)
{
curY++;
continue;
}
// ok, process left to right, grabbing bitmaps as we go...
S32 curX = 0;
while(curX < bmp->getWidth())
{
bmp->getColor(curX, curY, color);
if(color == sepColor)
{
curX++;
continue;
}
S32 startX = curX;
while(curX < bmp->getWidth())
{
bmp->getColor(curX, curY, color);
if(color == sepColor)
break;
curX++;
}
S32 stepY = curY;
while(stepY < bmp->getHeight())
{
bmp->getColor(startX, stepY, color);
if(color == sepColor)
break;
stepY++;
}
mBitmapArrayRects.push_back(RectI(startX, curY, curX - startX, stepY - curY));
}
// ok, now skip to the next seperation color on column 0
while(curY < bmp->getHeight())
{
bmp->getColor(0, curY, color);
if(color == sepColor)
break;
curY++;
}
}
return mBitmapArrayRects.size();
}
void GuiControlProfile::incRefCount()
{
if(!mRefCount++)
{
sFontCacheDirectory = Con::getVariable("$GUI::fontCacheDirectory");
//verify the font
mFont = GFont::create(mFontType, mFontSize, sFontCacheDirectory);
if (mFont.isNull())
Con::errorf("Failed to load/create profile font (%s/%d)", mFontType, mFontSize);
//verify the bitmap
mTextureHandle = TextureHandle(mBitmapName, BitmapKeepTexture);
if (!(bool)mTextureHandle)
Con::errorf("Failed to load profile bitmap (%s)",mBitmapName);
}
}
void GuiControlProfile::decRefCount()
{
AssertFatal(mRefCount, "GuiControlProfile::decRefCount: zero ref count");
if(!mRefCount)
return;
if(!--mRefCount)
{
mFont = NULL;
mTextureHandle = NULL;
}
}
ConsoleType( GuiProfile, TypeGuiProfile, sizeof(GuiControlProfile*) )
ConsoleSetType( TypeGuiProfile )
{
GuiControlProfile *profile = NULL;
if(argc == 1)
Sim::findObject(argv[0], profile);
AssertWarn(profile != NULL, avar("GuiControlProfile: requested gui profile (%s) does not exist.", argv[0]));
if(!profile)
profile = dynamic_cast<GuiControlProfile*>(Sim::findObject("GuiDefaultProfile"));
AssertFatal(profile != NULL, avar("GuiControlProfile: unable to find specified profile (%s) and GuiDefaultProfile does not exist!", argv[0]));
GuiControlProfile **obj = (GuiControlProfile **)dptr;
if((*obj) == profile)
return;
if(*obj)
(*obj)->decRefCount();
*obj = profile;
(*obj)->incRefCount();
}
ConsoleGetType( TypeGuiProfile )
{
static char returnBuffer[256];
GuiControlProfile **obj = (GuiControlProfile**)dptr;
dSprintf(returnBuffer, sizeof(returnBuffer), "%s", *obj ? (*obj)->getName() : "");
return returnBuffer;
}

169
engine/gui/core/guiTypes.h Executable file
View File

@ -0,0 +1,169 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUITYPES_H_
#define _GUITYPES_H_
#ifndef _GFONT_H_
#include "dgl/gFont.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _PLATFORMAUDIO_H_
#include "platform/platformAudio.h"
#endif
#ifndef _AUDIODATABLOCK_H_
#include "audio/audioDataBlock.h"
#endif
class GBitmap;
/// Represents a single GUI event.
///
/// This is passed around to all the relevant controls so they know what's going on.
struct GuiEvent
{
U16 ascii; ///< ascii character code 'a', 'A', 'b', '*', etc (if device==keyboard) - possibly a uchar or something
U8 modifier; ///< SI_LSHIFT, etc
U8 keyCode; ///< for unprintables, 'tab', 'return', ...
Point2I mousePoint; ///< for mouse events
U8 mouseClickCount; ///< to determine double clicks, etc...
};
class GuiCursor : public SimObject
{
private:
typedef SimObject Parent;
StringTableEntry mBitmapName;
Point2I mHotSpot;
Point2F mRenderOffset;
Point2I mExtent;
TextureHandle mTextureHandle;
public:
Point2I getHotSpot() { return mHotSpot; }
Point2I getExtent() { return mExtent; }
DECLARE_CONOBJECT(GuiCursor);
GuiCursor(void);
~GuiCursor(void);
static void initPersistFields();
bool onAdd(void);
void onRemove();
void render(const Point2I &pos);
};
/// A GuiControlProfile is used by every GuiObject and is akin to a
/// datablock. It is used to control information that does not change
/// or is unlikely to change during execution of a program. It is also
/// a level of abstraction between script and GUI control so that you can
/// use the same control, say a button, and have it look completly different
/// just with a different profile.
class GuiControlProfile : public SimObject
{
private:
typedef SimObject Parent;
public:
S32 mRefCount; ///< Used to determine if any controls are using this profile
bool mTabable; ///< True if this object is accessable from using the tab key
static StringTableEntry sFontCacheDirectory;
bool mCanKeyFocus; ///< True if the object can be given keyboard focus (in other words, made a first responder @see GuiControl)
bool mModal; ///< True if this is a Modeless dialog meaning it will pass input through instead of taking it all
bool mOpaque; ///< True if this object is not translucent
ColorI mFillColor; ///< Fill color, this is used to fill the bounds of the control if it is opaque
ColorI mFillColorHL; ///< This is used insetead of mFillColor if the object is highlited
ColorI mFillColorNA; ///< This is used to instead of mFillColor if the object is not active or disabled
S32 mBorder; ///< For most controls, if mBorder is > 0 a border will be drawn, some controls use this to draw different types of borders however @see guiDefaultControlRender.cc
S32 mBorderThickness; ///< Border thickness
ColorI mBorderColor; ///< Border color, used to draw a border around the bounds if border is enabled
ColorI mBorderColorHL; ///< Used instead of mBorderColor when the object is highlited
ColorI mBorderColorNA; ///< Used instead of mBorderColor when the object is not active or disabled
ColorI mBevelColorHL; ///< Used for the high-light part of the bevel
ColorI mBevelColorLL; ///< Used for the low-light part of the bevel
// font members
StringTableEntry mFontType; ///< Font face name for the control
S32 mFontSize; ///< Font size for the control
enum {
BaseColor = 0,
ColorHL,
ColorNA,
ColorSEL,
ColorUser0,
ColorUser1,
ColorUser2,
ColorUser3,
ColorUser4,
ColorUser5,
};
ColorI mFontColors[10]; ///< Array of font colors used for drawText with escape characters for changing color mid-string
ColorI& mFontColor; ///< Main font color
ColorI& mFontColorHL; ///< Highlited font color
ColorI& mFontColorNA; ///< Font color when object is not active/disabled
ColorI& mFontColorSEL; ///< Font color when object/text is selected
FontCharset mFontCharset; ///< Font character set
Resource<GFont> mFont; ///< Font resource
enum AlignmentType
{
LeftJustify,
RightJustify,
CenterJustify
};
AlignmentType mAlignment; ///< Horizontal text alignment
bool mAutoSizeWidth; ///< Auto-size the width-bounds of the control to fit it's contents
bool mAutoSizeHeight; ///< Auto-size the height-bounds of the control to fit it's contents
bool mReturnTab; ///< Used in GuiTextEditCtrl to specify if a tab-event should be simulated when return is pressed.
bool mNumbersOnly; ///< For text controls, true if this should only accept numerical data
bool mMouseOverSelected; ///< True if this object should be "selected" while the mouse is over it
ColorI mCursorColor; ///< Color for the blinking cursor in text fields (for example)
Point2I mTextOffset; ///< Text offset for the control
// bitmap members
StringTableEntry mBitmapName; ///< Bitmap file name for the bitmap of the control
TextureHandle mTextureHandle; ///< Texture handle for the control
Vector<RectI> mBitmapArrayRects; ///< Used for controls which use an array of bitmaps such as checkboxes
// sound members
AudioProfile *mSoundButtonDown; ///< Sound played when the object is "down" ie a button is pushed
AudioProfile *mSoundButtonOver; ///< Sound played when the mouse is over the object
public:
DECLARE_CONOBJECT(GuiControlProfile);
GuiControlProfile();
~GuiControlProfile();
static void initPersistFields();
bool onAdd();
/// This method creates an array of bitmaps from one single bitmap with
/// seperator color. The seperator color is whatever color is in pixel 0,0
/// of the bitmap. For an example see darkWindow.png and some of the other
/// UI textures. It returns the number of bitmaps in the array it created
/// It also stores the sizes in the mBitmapArrayRects vector.
S32 constructBitmapArray();
void incRefCount();
void decRefCount();
};
DefineConsoleType( TypeGuiProfile)
#endif //_GUITYPES_H

View File

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/controls/guiPopUpCtrl.h"
class GuiControlListPopUp : public GuiPopUpMenuCtrl
{
typedef GuiPopUpMenuCtrl Parent;
public:
bool onAdd();
DECLARE_CONOBJECT(GuiControlListPopUp);
};
IMPLEMENT_CONOBJECT(GuiControlListPopUp);
bool GuiControlListPopUp::onAdd()
{
if(!Parent::onAdd())
return false;
clear();
for(AbstractClassRep *rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass())
{
ConsoleObject *obj = rep->create();
if(obj && dynamic_cast<GuiControl *>(obj))
addEntry(rep->getClassName(), 0);
delete obj;
}
// We want to be alphabetical!
sort();
return true;
}

702
engine/gui/editor/guiDebugger.cc Executable file
View File

@ -0,0 +1,702 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/editor/guiDebugger.h"
#include "core/stream.h"
IMPLEMENT_CONOBJECT(DbgFileView);
static const char* itoa(S32 i)
{
static char buf[32];
dSprintf(buf, sizeof(buf), "%d", i);
return buf;
}
static const char* itoa2(S32 i)
{
static char buf[32];
dSprintf(buf, sizeof(buf), "%d", i);
return buf;
}
DbgFileView::~DbgFileView()
{
clear();
}
DbgFileView::DbgFileView()
{
VECTOR_SET_ASSOCIATION(mFileView);
mFileName = NULL;
mPCFileName = NULL;
mPCCurrentLine = -1;
mBlockStart = -1;
mBlockEnd = -1;
mFindString[0] = '\0';
mFindLineNumber = -1;
mSize.set(1, 0);
}
ConsoleMethod(DbgFileView, setCurrentLine, void, 4, 4, "(int line, bool selected)"
"Set the current highlighted line.")
{
object->setCurrentLine(dAtoi(argv[2]), dAtob(argv[3]));
}
ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()"
"Get the currently executing file and line, if any.\n\n"
"@returns A string containing the file, a tab, and then the line number."
" Use getField() with this.")
{
S32 lineNum;
const char *file = object->getCurrentLine(lineNum);
char* ret = Con::getReturnBuffer(256);
dSprintf(ret, sizeof(ret), "%s\t%d", file, lineNum);
return ret;
}
ConsoleMethod(DbgFileView, open, bool, 3, 3, "(string filename)"
"Open a file for viewing.\n\n"
"@note This loads the file from the local system.")
{
return object->openFile(argv[2]);
}
ConsoleMethod(DbgFileView, clearBreakPositions, void, 2, 2, "()"
"Clear all break points in the current file.")
{
object->clearBreakPositions();
}
ConsoleMethod(DbgFileView, setBreakPosition, void, 3, 3, "(int line)"
"Set a breakpoint at the specified line.")
{
object->setBreakPosition(dAtoi(argv[2]));
}
ConsoleMethod(DbgFileView, setBreak, void, 3, 3, "(int line)"
"Set a breakpoint at the specified line.")
{
object->setBreakPointStatus(dAtoi(argv[2]), true);
}
ConsoleMethod(DbgFileView, removeBreak, void, 3, 3, "(int line)"
"Remove a breakpoint from the specified line.")
{
object->setBreakPointStatus(dAtoi(argv[2]), false);
}
ConsoleMethod(DbgFileView, findString, bool, 3, 3, "(string findThis)"
"Find the specified string in the currently viewed file and "
"scroll it into view.")
{
return object->findString(argv[2]);
}
//this value is the offset used in the ::onRender() method...
static S32 gFileXOffset = 44;
void DbgFileView::AdjustCellSize()
{
if (! bool(mFont))
return;
S32 maxWidth = 1;
for (U32 i = 0; i < mFileView.size(); i++)
{
S32 cellWidth = gFileXOffset + mFont->getStrWidth((const UTF8 *)mFileView[i].text);
maxWidth = getMax(maxWidth, cellWidth);
}
mCellSize.set(maxWidth, mFont->getHeight() + 2);
setSize(mSize);
}
bool DbgFileView::onWake()
{
if (! Parent::onWake())
return false;
//clear the mouse over var
mMouseOverVariable[0] = '\0';
mbMouseDragging = false;
//adjust the cellwidth to the maximum line length
AdjustCellSize();
mSize.set(1, mFileView.size());
return true;
}
void DbgFileView::addLine(const char *string, U32 strLen)
{
// first compute the size
U32 size = 1; // for null
for(U32 i = 0; i < strLen; i++)
{
if(string[i] == '\t')
size += 3;
else if(string[i] != '\r')
size++;
}
FileLine fl;
fl.breakPosition = false;
fl.breakOnLine = false;
if(size)
{
fl.text = (char *) dMalloc(size);
U32 dstIndex = 0;
for(U32 i = 0; i < strLen; i++)
{
if(string[i] == '\t')
{
fl.text[dstIndex] = ' ';
fl.text[dstIndex + 1] = ' ';
fl.text[dstIndex + 2] = ' ';
dstIndex += 3;
}
else if(string[i] != '\r')
fl.text[dstIndex++] = string[i];
}
fl.text[dstIndex] = 0;
}
else
fl.text = NULL;
mFileView.push_back(fl);
}
void DbgFileView::clear()
{
for(Vector<FileLine>::iterator i = mFileView.begin(); i != mFileView.end(); i++)
dFree(i->text);
mFileView.clear();
}
bool DbgFileView::findString(const char *text)
{
//make sure we have a valid string to find
if (!text || !text[0])
return false;
//see which line we start searching from
S32 curLine = 0;
bool searchAgain = false;
if (mFindLineNumber >= 0 && !dStricmp(mFindString, text))
{
searchAgain = true;
curLine = mFindLineNumber;
}
else
mFindLineNumber = -1;
//copy the search text
dStrncpy(mFindString, text, 255);
S32 length = dStrlen(mFindString);
//loop through looking for the next instance
while (curLine < mFileView.size())
{
char *curText;
if (curLine == mFindLineNumber && mBlockStart >= 0)
curText = &mFileView[curLine].text[mBlockStart + 1];
else
curText = &mFileView[curLine].text[0];
//search for the string (the hard way... - apparently dStrupr is broken...
char *found = NULL;
char *curTextPtr = curText;
while (*curTextPtr != '\0')
{
if (!dStrnicmp(mFindString, curTextPtr, length))
{
found = curTextPtr;
break;
}
else
curTextPtr++;
}
//did we find it?
if (found)
{
//scroll first
mFindLineNumber = curLine;
scrollToLine(mFindLineNumber + 1);
//then hilite
mBlockStart = (S32)(found - &mFileView[curLine].text[0]);
mBlockEnd = mBlockStart + length;
return true;
}
else
curLine++;
}
//didn't find anything - reset the vars for the next search
mBlockStart = -1;
mBlockEnd = -1;
mFindLineNumber = -1;
setSelectedCell(Point2I(-1, -1));
return false;
}
void DbgFileView::setCurrentLine(S32 lineNumber, bool setCurrentLine)
{
//update the line number
if (setCurrentLine)
{
mPCFileName = mFileName;
mPCCurrentLine = lineNumber;
mBlockStart = -1;
mBlockEnd = -1;
if (lineNumber >= 0)
scrollToLine(mPCCurrentLine);
}
else
{
scrollToLine(lineNumber);
}
}
const char* DbgFileView::getCurrentLine(S32 &lineNumber)
{
lineNumber = mPCCurrentLine;
return mPCFileName;
}
bool DbgFileView::openFile(const char *fileName)
{
if ((! fileName) || (! fileName[0]))
return false;
StringTableEntry newFile = StringTable->insert(fileName);
if (mFileName == newFile)
return true;
U32 fileSize = ResourceManager->getSize(fileName);
char *fileBuf;
if (fileSize)
{
fileBuf = new char [fileSize+1];
Stream *s = ResourceManager->openStream(fileName);
if (s)
{
s->read(fileSize, fileBuf);
ResourceManager->closeStream(s);
fileBuf[fileSize] = '\0';
}
else
{
delete [] fileBuf;
fileBuf = NULL;
}
}
if (!fileSize || !fileBuf)
{
Con::printf("DbgFileView: unable to open file %s.", fileName);
return false;
}
//copy the file name
mFileName = newFile;
//clear the old mFileView
clear();
setSize(Point2I(1, 0));
//begin reading and parsing at each '\n'
char *parsePtr = fileBuf;
for(;;) {
char *tempPtr = dStrchr(parsePtr, '\n');
if(tempPtr)
addLine(parsePtr, tempPtr - parsePtr);
else if(parsePtr[0])
addLine(parsePtr, dStrlen(parsePtr));
if(!tempPtr)
break;
parsePtr = tempPtr + 1;
}
//delete the buffer
delete [] fileBuf;
//set the file size
AdjustCellSize();
setSize(Point2I(1, mFileView.size()));
return true;
}
void DbgFileView::scrollToLine(S32 lineNumber)
{
GuiControl *parent = getParent();
if (! parent)
return;
S32 yOffset = (lineNumber - 1) * mCellSize.y;
//see if the line is already visible
if (! (yOffset + mBounds.point.y >= 0 && yOffset + mBounds.point.y < parent->mBounds.extent.y - mCellSize.y))
{
//reposition the control
S32 newYOffset = getMin(0, getMax(parent->mBounds.extent.y - mBounds.extent.y, (mCellSize.y * 4) - yOffset));
resize(Point2I(mBounds.point.x, newYOffset), mBounds.extent);
}
//hilite the line
cellSelected(Point2I(0, lineNumber - 1));
}
S32 DbgFileView::findMouseOverChar(const char *text, S32 stringPosition)
{
static char tempBuf[512];
char *bufPtr = &tempBuf[1];
// Handle the empty string correctly.
if (text[0] == '\0') {
return -1;
}
// Copy the line's text into the scratch buffer.
dStrncpy(tempBuf, text, 512);
// Make sure we have a null terminator.
tempBuf[511] = '\0';
// Loop over the characters...
bool found = false;
bool finished = false;
do {
// Note the current character, then replace it with EOL.
char c = *bufPtr;
*bufPtr = '\0';
// Is the resulting string long enough to include the current
// mouse position?
if ((S32)mFont->getStrWidth((const UTF8 *)tempBuf) > stringPosition) {
// Yep.
// Restore the character.
*bufPtr = c;
// Set bufPtr to point to the char under the mouse.
bufPtr--;
// Bail.
found = true;
finished = true;
}
else {
// Nope.
// Restore the character.
*bufPtr = c;
// Move on to the next character.
bufPtr++;
// Bail if EOL.
if (*bufPtr == '\0') finished = true;
}
} while (!finished);
// Did we find a character under the mouse?
if (found) {
// If so, return its position.
return bufPtr - tempBuf;
}
// If not, return -1.
else return -1;
}
bool DbgFileView::findMouseOverVariable()
{
GuiCanvas *root = getRoot();
AssertFatal(root, "Unable to get the root Canvas.");
Point2I curMouse = root->getCursorPos();
Point2I pt = globalToLocalCoord(curMouse);
//find out which cell was hit
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
S32 stringPosition = pt.x - gFileXOffset;
char tempBuf[256], *varNamePtr = &tempBuf[1];
dStrcpy(tempBuf, mFileView[cell.y].text);
//find the current mouse over char
S32 charNum = findMouseOverChar(mFileView[cell.y].text, stringPosition);
if (charNum >= 0)
{
varNamePtr = &tempBuf[charNum];
}
else
{
mMouseOverVariable[0] = '\0';
mMouseOverValue[0] = '\0';
return false;
}
//now make sure we can go from the current cursor mPosition to the beginning of a var name
bool found = false;
while (varNamePtr >= &tempBuf[0])
{
if (*varNamePtr == '%' || *varNamePtr == '$')
{
found = true;
break;
}
else if ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') ||
(*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':')
{
varNamePtr--;
}
else
{
break;
}
}
//mouse wasn't over a possible variable name
if (! found)
{
mMouseOverVariable[0] = '\0';
mMouseOverValue[0] = '\0';
return false;
}
//set the var char start positions
mMouseVarStart = varNamePtr - tempBuf;
//now copy the (possible) var name into the buf
char *tempPtr = &mMouseOverVariable[0];
//copy the leading '%' or '$'
*tempPtr++ = *varNamePtr++;
//now copy letters and numbers until the end of the name
while ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') ||
(*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':')
{
*tempPtr++ = *varNamePtr++;
}
*tempPtr = '\0';
//set the var char end positions
mMouseVarEnd = varNamePtr - tempBuf;
return true;
}
return false;
}
void DbgFileView::clearBreakPositions()
{
for(Vector<FileLine>::iterator i = mFileView.begin(); i != mFileView.end(); i++)
{
i->breakPosition = false;
i->breakOnLine = false;
}
}
void DbgFileView::setBreakPosition(U32 line)
{
if(line > mFileView.size())
return;
mFileView[line-1].breakPosition = true;
}
void DbgFileView::setBreakPointStatus(U32 line, bool set)
{
if(line > mFileView.size())
return;
mFileView[line-1].breakOnLine = set;
}
void DbgFileView::onPreRender()
{
setUpdate();
char oldVar[256];
dStrcpy(oldVar, mMouseOverVariable);
bool found = findMouseOverVariable();
if (found && mPCCurrentLine >= 0)
{
//send the query only when the var changes
if (dStricmp(oldVar, mMouseOverVariable))
Con::executef(2, "DbgSetCursorWatch", mMouseOverVariable);
}
else
Con::executef(2, "DbgSetCursorWatch", "");
}
void DbgFileView::onMouseDown(const GuiEvent &event)
{
if (! mActive)
{
Parent::onMouseDown(event);
return;
}
Point2I pt = globalToLocalCoord(event.mousePoint);
bool doubleClick = (event.mouseClickCount > 1);
//find out which cell was hit
Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
//if we clicked on the breakpoint mark
if (pt.x >= 0 && pt.x <= 12)
{
//toggle the break point
if (mFileView[cell.y].breakPosition)
{
if (mFileView[cell.y].breakOnLine)
Con::executef(this, 2, "onRemoveBreakPoint", itoa(cell.y + 1));
else
Con::executef(this, 2, "onSetBreakPoint", itoa(cell.y + 1));
}
}
else
{
Point2I prevSelected = mSelectedCell;
Parent::onMouseDown(event);
mBlockStart= -1;
mBlockEnd = -1;
//open the file view
if (mSelectedCell.y == prevSelected.y && doubleClick && mMouseOverVariable[0])
{
Con::executef(this, 2, "onSetWatch", mMouseOverVariable);
mBlockStart = mMouseVarStart;
mBlockEnd = mMouseVarEnd;
}
else
{
S32 stringPosition = pt.x - gFileXOffset;
//find which character we're over
S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition);
if (charNum >= 0)
{
//lock the mouse
mouseLock();
setFirstResponder();
//set the block hilite start and end
mbMouseDragging = true;
mMouseDownChar = charNum;
}
}
}
}
else
{
Parent::onMouseDown(event);
}
}
void DbgFileView::onMouseDragged(const GuiEvent &event)
{
if (mbMouseDragging)
{
Point2I pt = globalToLocalCoord(event.mousePoint);
S32 stringPosition = pt.x - gFileXOffset;
//find which character we're over
S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition);
if (charNum >= 0)
{
if (charNum < mMouseDownChar)
{
mBlockEnd = mMouseDownChar + 1;
mBlockStart = charNum;
}
else
{
mBlockEnd = charNum + 1;
mBlockStart = mMouseDownChar;
}
}
//otherwise, the cursor is past the end of the string
else
{
mBlockStart = mMouseDownChar;
mBlockEnd = dStrlen(mFileView[mSelectedCell.y].text) + 1;
}
}
}
void DbgFileView::onMouseUp(const GuiEvent &)
{
//unlock the mouse
mouseUnlock();
mbMouseDragging = false;
}
void DbgFileView::onRenderCell(Point2I offset, Point2I cell, bool selected, bool)
{
Point2I cellOffset = offset;
cellOffset.x += 4;
//draw the break point marks
if (mFileView[cell.y].breakOnLine)
{
dglSetBitmapModulation(mProfile->mFontColorHL);
dglDrawText(mFont, cellOffset, "#");
}
else if (mFileView[cell.y].breakPosition)
{
dglSetBitmapModulation(mProfile->mFontColor);
dglDrawText(mFont, cellOffset, "-");
}
cellOffset.x += 8;
//draw in the "current line" indicator
if (mFileName == mPCFileName && (cell.y + 1 == mPCCurrentLine))
{
dglSetBitmapModulation(mProfile->mFontColorHL);
dglDrawText(mFont, cellOffset, "=>");
}
//by this time, the cellOffset has been incremented by 44 - the value of gFileXOffset
cellOffset.x += 32;
//hilite the line if selected
if (selected)
{
if (mBlockStart == -1)
{
dglDrawRectFill(RectI(cellOffset.x - 2, cellOffset.y - 3,
mCellSize.x + 4, mCellSize.y + 6), mProfile->mFillColorHL);
}
else if (mBlockStart >= 0 && mBlockEnd > mBlockStart && mBlockEnd <= S32(dStrlen(mFileView[cell.y].text) + 1))
{
S32 startPos, endPos;
char tempBuf[256];
dStrcpy(tempBuf, mFileView[cell.y].text);
//get the end coord
tempBuf[mBlockEnd] = '\0';
endPos = mFont->getStrWidth((const UTF8 *)tempBuf);
//get the start coord
tempBuf[mBlockStart] = '\0';
startPos = mFont->getStrWidth((const UTF8 *)tempBuf);
//draw the hilite
dglDrawRectFill(RectI(cellOffset.x + startPos, cellOffset.y - 3, endPos - startPos + 2, mCellSize.y + 6), mProfile->mFillColorHL);
}
}
//draw the line of text
dglSetBitmapModulation(mFileView[cell.y].breakOnLine ? mProfile->mFontColorHL : mProfile->mFontColor);
dglDrawText(mFont, cellOffset, mFileView[cell.y].text);
}

82
engine/gui/editor/guiDebugger.h Executable file
View File

@ -0,0 +1,82 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIDEBUGGER_H_
#define _GUIDEBUGGER_H_
#ifndef _GUIARRAYCTRL_H_
#include "gui/core/guiArrayCtrl.h"
#endif
class DbgFileView : public GuiArrayCtrl
{
private:
typedef GuiArrayCtrl Parent;
struct FileLine
{
bool breakPosition;
bool breakOnLine;
char *text;
};
Vector<FileLine> mFileView;
StringTableEntry mFileName;
void AdjustCellSize();
//used to display the program counter
StringTableEntry mPCFileName;
S32 mPCCurrentLine;
//vars used to highlight the selected line segment for copying
bool mbMouseDragging;
S32 mMouseDownChar;
S32 mBlockStart;
S32 mBlockEnd;
char mMouseOverVariable[256];
char mMouseOverValue[256];
S32 findMouseOverChar(const char *text, S32 stringPosition);
bool findMouseOverVariable();
S32 mMouseVarStart;
S32 mMouseVarEnd;
//find vars
char mFindString[256];
S32 mFindLineNumber;
public:
DbgFileView();
~DbgFileView();
DECLARE_CONOBJECT(DbgFileView);
bool onWake();
void clear();
void clearBreakPositions();
void setCurrentLine(S32 lineNumber, bool setCurrentLine);
const char *getCurrentLine(S32 &lineNumber);
bool openFile(const char *fileName);
void scrollToLine(S32 lineNumber);
void setBreakPointStatus(U32 lineNumber, bool value);
void setBreakPosition(U32 line);
void addLine(const char *text, U32 textLen);
bool findString(const char *text);
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onPreRender();
void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
};
#endif //_GUI_DEBUGGER_H

1139
engine/gui/editor/guiEditCtrl.cc Executable file

File diff suppressed because it is too large Load Diff

98
engine/gui/editor/guiEditCtrl.h Executable file
View File

@ -0,0 +1,98 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIEDITCTRL_H_
#define _GUIEDITCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiEditCtrl : public GuiControl
{
typedef GuiControl Parent;
Vector<GuiControl *> mSelectedControls;
GuiControl* mCurrentAddSet;
GuiControl* mContentControl;
Point2I mLastMousePos;
Point2I mSelectionAnchor;
Point2I mGridSnap;
Point2I mDragBeginPoint;
Vector<Point2I> mDragBeginPoints;
// Sizing Cursors
GuiCursor* mDefaultCursor;
GuiCursor* mLeftRightCursor;
GuiCursor* mUpDownCursor;
GuiCursor* mNWSECursor;
GuiCursor* mNESWCursor;
GuiCursor* mMoveCursor;
enum mouseModes { Selecting, MovingSelection, SizingSelection, DragSelecting };
enum sizingModes { sizingNone = 0, sizingLeft = 1, sizingRight = 2, sizingTop = 4, sizingBottom = 8 };
mouseModes mMouseDownMode;
sizingModes mSizingMode;
public:
GuiEditCtrl();
DECLARE_CONOBJECT(GuiEditCtrl);
bool onWake();
void onSleep();
void select(GuiControl *ctrl);
void setRoot(GuiControl *ctrl);
void setEditMode(bool value);
S32 getSizingHitKnobs(const Point2I &pt, const RectI &box);
void getDragRect(RectI &b);
void drawNut(const Point2I &nut, ColorI &outlineColor, ColorI &nutColor);
void drawNuts(RectI &box, ColorI &outlineColor, ColorI &nutColor);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void addNewControl(GuiControl *ctrl);
bool selectionContains(GuiControl *ctrl);
void setCurrentAddSet(GuiControl *ctrl);
void setSelection(GuiControl *ctrl, bool inclusive = false);
// Sizing Cursors
bool initCursors();
void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
const Vector<GuiControl *> *getSelected() const { return &mSelectedControls; }
const GuiControl *getAddSet() const { return mCurrentAddSet; }; //JDD
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onRightMouseDown(const GuiEvent &event);
enum Justification {
JUSTIFY_LEFT,
JUSTIFY_CENTER,
JUSTIFY_RIGHT,
JUSTIFY_TOP,
JUSTIFY_BOTTOM,
SPACING_VERTICAL,
SPACING_HORIZONTAL
};
void justifySelection( Justification j);
void moveSelection(const Point2I &delta);
void saveSelection(const char *filename);
void loadSelection(const char *filename);
void addSelection(S32 id);
void removeSelection(S32 id);
void deleteSelection();
void clearSelection();
void selectAll();
void bringToFront();
void pushToBack();
};
#endif //_GUI_EDIT_CTRL_H

View File

@ -0,0 +1,241 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "dgl/gTexManager.h"
#include "gui/editor/guiFilterCtrl.h"
#include "platform/event.h"
#include "math/mMath.h"
IMPLEMENT_CONOBJECT(GuiFilterCtrl);
GuiFilterCtrl::GuiFilterCtrl()
{
mControlPointRequest = 7;
mFilter.setSize(7);
identity();
}
void GuiFilterCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("controlPoints", TypeS32, Offset(mControlPointRequest, GuiFilterCtrl));
addField("filter", TypeF32Vector, Offset(mFilter, GuiFilterCtrl));
}
ConsoleMethod( GuiFilterCtrl, getValue, const char*, 2, 2, "Return a tuple containing all the values in the filter.")
{
argv;
static char buffer[512];
const Filter *filter = object->get();
*buffer = 0;
for (U32 i=0; i < filter->size(); i++)
{
char value[32];
dSprintf(value, 31, "%1.5f ", *(filter->begin()+i) );
dStrcat(buffer, value);
}
return buffer;
}
ConsoleMethod( GuiFilterCtrl, setValue, void, 3, 20, "(f1, f2, ...)"
"Reset the filter to use the specified points, spread equidistantly across the domain.")
{
Filter filter;
argc -= 2;
argv += 2;
filter.set(argc, argv);
object->set(filter);
}
ConsoleMethod( GuiFilterCtrl, identity, void, 2, 2, "Reset the filtering.")
{
object->identity();
}
bool GuiFilterCtrl::onWake()
{
if (!Parent::onWake())
return false;
if (U32(mControlPointRequest) != mFilter.size())
{
mFilter.setSize(mControlPointRequest);
identity();
}
return true;
}
void GuiFilterCtrl::identity()
{
S32 size = mFilter.size()-1;
for (U32 i=0; S32(i) <= size; i++)
mFilter[i] = (F32)i/(F32)size;
}
void GuiFilterCtrl::onMouseDown(const GuiEvent &event)
{
mouseLock();
setFirstResponder();
Point2I p = globalToLocalCoord(event.mousePoint);
// determine which knot (offset same as in onRender)
F32 w = F32(mBounds.extent.x-4) / F32(mFilter.size()-1);
F32 val = (F32(p.x) + (w / 2.f)) / w;
mCurKnot = S32(val);
mFilter[mCurKnot] = 1.0f - F32(getMin(getMax(0, p.y), mBounds.extent.y)/(F32)mBounds.extent.y);
setUpdate();
}
void GuiFilterCtrl::onMouseDragged(const GuiEvent &event)
{
mouseLock();
setFirstResponder();
Point2I p = globalToLocalCoord(event.mousePoint);
mFilter[mCurKnot] = 1.0f - F32(getMin(getMax(0, p.y), mBounds.extent.y)/(F32)mBounds.extent.y);
setUpdate();
}
void GuiFilterCtrl::onMouseUp(const GuiEvent &)
{
mouseUnlock();
if (mConsoleCommand[0])
Con::evaluate(mConsoleCommand, false);
}
void GuiFilterCtrl::onPreRender()
{
if(U32(mControlPointRequest) != mFilter.size())
{
mFilter.setSize(mControlPointRequest);
identity();
setUpdate();
}
}
void GuiFilterCtrl::onRender(Point2I offset, const RectI &updateRect)
{
Point2I pos = offset;
Point2I ext = mBounds.extent;
RectI r(pos, ext);
dglDrawRectFill(r, ColorI(255,255,255));
dglDrawRect(r, ColorI(0,0,0));
// shrink by 2 pixels
pos.x += 2;
pos.y += 2;
ext.x -= 4;
ext.y -= 4;
// draw the identity line
glColor3f(0.9, 0.9, 0.9);
glBegin(GL_LINES);
glVertex2i(pos.x, pos.y+ext.y);
glVertex2i(pos.x+ext.x, pos.y);
glEnd();
// draw the curv
glColor3f(0.4, 0.4, 0.4);
glBegin(GL_LINE_STRIP);
F32 scale = 1.0f/F32(ext.x);
for (U32 i=0; S32(i) < ext.x; i++)
{
F32 index = F32(i)*scale;
S32 y = (S32)(ext.y*(1.0f-mFilter.getValue(index)));
glVertex2i(pos.x+i, pos.y+y );
}
glEnd();
// draw the knots
for (U32 k=0; k < mFilter.size(); k++)
{
RectI r;
r.point.x = (S32)(((F32)ext.x/(F32)(mFilter.size()-1)*(F32)k));
r.point.y = (S32)(ext.y - ((F32)ext.y * mFilter[k]));
r.point += pos + Point2I(-2,-2);
r.extent = Point2I(5,5);
dglDrawRectFill(r, ColorI(255,0,0));
}
renderChildControls(offset, updateRect);
}
//--------------------------------------
void Filter::set(S32 argc, const char *argv[])
{
setSize(0);
if (argc == 1)
{ // in the form of one string "1.0 1.0 1.0"
char list[1024];
dStrcpy(list, *argv); // strtok modifies the string so we need to copy it
char *value = dStrtok(list, " ");
while (value)
{
push_back(dAtof(value));
value = dStrtok(NULL, " ");
}
}
else
{ // in the form of seperate strings "1.0" "1.0" "1.0"
for (; argc ; argc--, argv++)
push_back(dAtof(*argv));
}
}
//--------------------------------------
F32 Filter::getValue(F32 x) const
{
if (size() < 2)
return 0.0f;
x = mClampF(x, 0.0f, 1.0f);
x *= F32(size()-1);
F32 p0,p1,p2,p3;
S32 i1 = (S32)mFloor(x);
S32 i2 = i1+1;
F32 dt = x - F32(i1);
p1 = *(begin()+i1);
p2 = *(begin()+i2);
if (i1 == 0)
p0 = p1 + (p1 - p2);
else
p0 = *(begin()+i1-1);
if (i2 == S32(size()-1))
p3 = p2 + (p2 - p1);
else
p3 = *(begin()+i2+1);
return mClampF( mCatmullrom(dt, p0, p1, p2, p3), 0.0f, 1.0f );
}

View File

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIFILTERCTRL_H_
#define _GUIFILTERCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
//--------------------------------------
// helper class
class Filter: public Vector<F32>
{
public:
Filter() : Vector<F32>(__FILE__, __LINE__) { }
void set(S32 argc, const char *argv[]);
F32 getValue(F32 t) const;
};
//--------------------------------------
class GuiFilterCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
S32 mControlPointRequest;
S32 mCurKnot;
Filter mFilter;
public:
//creation methods
DECLARE_CONOBJECT(GuiFilterCtrl);
GuiFilterCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &);
F32 getValue(S32 n);
const Filter* get() { return &mFilter; }
void set(const Filter &f);
S32 getNumControlPoints() {return mFilter.size(); }
void identity();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect );
};
inline F32 GuiFilterCtrl::getValue(S32 n)
{
S32 index = getMin(getMax(n,0), (S32)mFilter.size()-1);
return mFilter[n];
}
inline void GuiFilterCtrl::set(const Filter &f)
{
mControlPointRequest = f.size();
mFilter = f;
}
#endif

350
engine/gui/editor/guiGraphCtrl.cc Executable file
View File

@ -0,0 +1,350 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "gui/editor/guiGraphCtrl.h"
IMPLEMENT_CONOBJECT(GuiGraphCtrl);
GuiGraphCtrl::GuiGraphCtrl()
{
for(int i = 0; i < MaxPlots; i++)
{
mPlots[i].mAutoPlot = NULL;
mPlots[i].mAutoPlotDelay = 0;
mPlots[i].mGraphColor = ColorF(1.0, 1.0, 1.0);
VECTOR_SET_ASSOCIATION(mPlots[i].mGraphData);
mPlots[i].mGraphMax = 1;
mPlots[i].mGraphType = Polyline;
for(int j = 0; j < MaxDataPoints; j++)
mPlots[i].mGraphData.push_front(0.0);
}
AssertWarn(MaxPlots == 6, "Only 6 plot colors initialized. Update following code if you change MaxPlots.");
mPlots[0].mGraphColor = ColorF(1.0, 1.0, 1.0);
mPlots[1].mGraphColor = ColorF(1.0, 0.0, 0.0);
mPlots[2].mGraphColor = ColorF(0.0, 1.0, 0.0);
mPlots[3].mGraphColor = ColorF(0.0, 0.0, 1.0);
mPlots[4].mGraphColor = ColorF(0.0, 1.0, 1.0);
mPlots[5].mGraphColor = ColorF(0.0, 0.0, 0.0);
}
static EnumTable::Enums enumGraphTypes[] = {
{ GuiGraphCtrl::Bar, "bar" },
{ GuiGraphCtrl::Filled, "filled" },
{ GuiGraphCtrl::Point, "point" },
{ GuiGraphCtrl::Polyline , "polyline" },
};
static EnumTable gGraphTypeTable( 4, &enumGraphTypes[0] );
bool GuiGraphCtrl::onWake()
{
if (! Parent::onWake())
return false;
setActive(true);
return true;
}
void GuiGraphCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if (mProfile->mBorder)
{
RectI rect(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
dglDrawRect(rect, mProfile->mBorderColor);
}
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
glEnable(GL_BLEND);
ColorF color(1.0, 1.0, 1.0, 0.5);
dglDrawRectFill(updateRect, color);
glDisable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ZERO);
for (int k = 0; k < MaxPlots; k++)
{
// Check if there is an autoplot and the proper amount of time has passed, if so add datum.
if((mPlots[k].mAutoPlot!=NULL) &&
(mPlots[k].mAutoPlotDelay < (Sim::getCurrentTime() - mPlots[k].mAutoPlotLastDisplay)))
{
mPlots[k].mAutoPlotLastDisplay = Sim::getCurrentTime();
addDatum(k, Con::getFloatVariable(mPlots[k].mAutoPlot));
Con::setIntVariable(mPlots[k].mAutoPlot, 0);
}
// Adjust scale to max value + 5% so we can see high values
F32 Scale = (F32(getExtent().y) / (F32(mPlots[k].mGraphMax*1.05)));
// Nothing to graph
if (mPlots[k].mGraphData.size() == 0)
continue;
// Bar graph
if(mPlots[k].mGraphType == Bar)
{
glBegin(GL_QUADS);
glColor3fv(mPlots[k].mGraphColor);
S32 temp1,temp2;
temp1 = 0;
for (S32 sample = 0; sample < getExtent().x; sample++)
{
if(mPlots[k].mGraphData.size() >= getExtent().x)
temp2 = sample;
else
temp2 = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);
glVertex2i(getPosition().x + temp1,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
glVertex2i(getPosition().x + temp2,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
glVertex2i(getPosition().x + temp2,
getPosition().y + getExtent().y);
glVertex2i(getPosition().x + temp1,
getPosition().y + getExtent().y);
temp1 = temp2;
}
glEnd();
}
// Filled graph
else if(mPlots[k].mGraphType == Filled)
{
glBegin(GL_QUADS);
glColor3fv(mPlots[k].mGraphColor);
S32 temp1,temp2;
temp1 = 0;
for (S32 sample = 0; sample < (getExtent().x-1); sample++)
{
if(mPlots[k].mGraphData.size() >= getExtent().x)
temp2 = sample;
else
temp2 = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);
glVertex2i(getPosition().x + temp1,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
glVertex2i(getPosition().x + temp2,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample+1) * Scale));
glVertex2i(getPosition().x + temp2,
getPosition().y + getExtent().y);
glVertex2i(getPosition().x + temp1,
getPosition().y + getExtent().y);
temp1 = temp2;
}
// last point
S32 sample = getExtent().x;
if(mPlots[k].mGraphData.size() >= getExtent().x)
temp2 = sample;
else
temp2 = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);
glVertex2i(getPosition().x + temp1,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
glVertex2i(getPosition().x + temp2,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
glVertex2i(getPosition().x + temp2,
getPosition().y + getExtent().y);
glVertex2i(getPosition().x + temp1,
getPosition().y + getExtent().y);
glEnd();
}
// Point or Polyline graph
else if((mPlots[k].mGraphType == Point) || (mPlots[k].mGraphType == Polyline))
{
if(mPlots[k].mGraphType == Point)
glBegin(GL_POINTS);
else
glBegin(GL_LINE_STRIP);
glColor3fv(mPlots[k].mGraphColor);
for (S32 sample = 0; sample < getExtent().x; sample++)
{
S32 temp;
if(mPlots[k].mGraphData.size() >= getExtent().x)
temp = sample;
else
temp = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample);
glVertex2i(getPosition().x + temp,
(getPosition().y + getExtent().y) - (S32)(getDatum(k, sample) * Scale));
}
glEnd();
}
}
}
void GuiGraphCtrl::addDatum(S32 plotID, F32 v)
{
AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!");
// Add the data and trim the vector...
mPlots[plotID].mGraphData.push_front( v );
if(mPlots[plotID].mGraphData.size() > MaxDataPoints)
mPlots[plotID].mGraphData.pop_back();
// Keep record of maximum data value for scaling purposes.
if(v > mPlots[plotID].mGraphMax)
mPlots[plotID].mGraphMax = v;
}
float GuiGraphCtrl::getDatum( int plotID, int sample)
{
AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!");
AssertFatal(sample > -1 && sample < MaxDataPoints, "Invalid sample specified!");
return mPlots[plotID].mGraphData[sample];
}
void GuiGraphCtrl::addAutoPlot(S32 plotID, const char *variable, S32 update)
{
mPlots[plotID].mAutoPlot = StringTable->insert(variable);
mPlots[plotID].mAutoPlotDelay = update;
Con::setIntVariable(mPlots[plotID].mAutoPlot, 0);
}
void GuiGraphCtrl::removeAutoPlot(S32 plotID)
{
mPlots[plotID].mAutoPlot = NULL;
}
void GuiGraphCtrl::setGraphType(S32 plotID, const char *graphType)
{
AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!");
if(!dStricmp(graphType,"Bar"))
mPlots[plotID].mGraphType = Bar;
else if(!dStricmp(graphType,"Filled"))
mPlots[plotID].mGraphType = Filled;
else if(!dStricmp(graphType,"Point"))
mPlots[plotID].mGraphType = Point;
else if(!dStricmp(graphType,"Polyline"))
mPlots[plotID].mGraphType = Polyline;
else AssertWarn(true, "Invalid graph type!");
}
ConsoleMethod(GuiGraphCtrl, addDatum, void, 4, 4, "(int plotID, float v)"
"Add a data point to the given plot.")
{
S32 plotID = dAtoi(argv[2]);
if(plotID > object->MaxPlots)
{
Con::errorf("Invalid plotID.");
return;
}
object->addDatum( plotID, dAtof(argv[3]));
}
ConsoleMethod(GuiGraphCtrl, getDatum, F32, 4, 4, "(int plotID, int samples)"
"Get a data point from the plot specified, samples from the start of the graph.")
{
S32 plotID = dAtoi(argv[2]);
S32 samples = dAtoi(argv[3]);
if(plotID > object->MaxPlots)
{
Con::errorf("Invalid plotID.");
return -1;
}
if(samples > object->MaxDataPoints)
{
Con::errorf("Invalid sample.");
return -1;
}
return object->getDatum(plotID, samples);
}
ConsoleMethod(GuiGraphCtrl, addAutoPlot, void, 5, 5, "(int plotID, string variable, int update)"
"Adds a data point with value variable, every update ms.")
{
S32 plotID = dAtoi(argv[2]);
if(plotID > object->MaxPlots)
{
Con::errorf("Invalid plotID.");
return;
}
object->addAutoPlot(plotID,argv[3],dAtoi(argv[4]));
}
ConsoleMethod(GuiGraphCtrl, removeAutoPlot, void, 3, 3, "(int plotID)"
"Stops automatic pointing over set interval.")
{
S32 plotID = dAtoi(argv[2]);
if(plotID > object->MaxPlots)
{
Con::errorf("Invalid plotID.");
return;
}
object->removeAutoPlot(plotID);
}
ConsoleMethod(GuiGraphCtrl, setGraphType, void, 4, 4, "(int plotID, string graphType)"
"Change GraphType of plot plotID.")
{
S32 plotID = dAtoi(argv[2]);
if(plotID > object->MaxPlots)
{
Con::errorf("Invalid plotID.");
return;
}
object->setGraphType(dAtoi(argv[2]), argv[3]);
}
ConsoleMethod(GuiGraphCtrl, matchScale, void, 3, GuiGraphCtrl::MaxPlots+2, "(int plotID, int plotID, ...)"
"Sets the scale of all specified plots to the maximum scale among them.")
{
F32 Max = 0;
for(int i=0; i < (argc-2); i++)
{
if(dAtoi(argv[2+i]) > object->MaxPlots)
{
Con::errorf("Invalid plotID.");
return;
}
if (object->mPlots[dAtoi(argv[2+i])].mGraphMax > Max)
Max = object->mPlots[dAtoi(argv[2+i])].mGraphMax;
}
for(int i=0; i < (argc-2); i++)
object->mPlots[dAtoi(argv[2+i])].mGraphMax = Max;
}

View File

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIGRAPHCTRL_H_
#define _GUIGRAPHCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
class GuiGraphCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
enum Constants {
MaxPlots = 6,
MaxDataPoints = 200
};
enum GraphType {
Point,
Polyline,
Filled,
Bar
};
struct PlotInfo
{
const char *mAutoPlot;
U32 mAutoPlotDelay;
SimTime mAutoPlotLastDisplay;
ColorF mGraphColor;
Vector<F32> mGraphData;
F32 mGraphMax;
GraphType mGraphType;
};
PlotInfo mPlots[MaxPlots];
//creation methods
DECLARE_CONOBJECT(GuiGraphCtrl);
GuiGraphCtrl();
//Parental methods
bool onWake();
void onRender(Point2I offset, const RectI &updateRect);
// Graph interface
void addDatum(S32 plotID, F32 v);
F32 getDatum(S32 plotID, S32 samples);
void addAutoPlot(S32 plotID, const char *variable, S32 update);
void removeAutoPlot(S32 plotID);
void setGraphType(S32 plotID, const char *graphType);
};
#endif

1273
engine/gui/editor/guiInspector.cc Executable file

File diff suppressed because it is too large Load Diff

258
engine/gui/editor/guiInspector.h Executable file
View File

@ -0,0 +1,258 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUI_INSPECTOR_H_
#define _GUI_INSPECTOR_H_
#ifndef _DYNAMIC_CONSOLETYPES_H_
#include "console/dynamicTypes.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUISTACKCTRL_H_
#include "gui/containers/guiStackCtrl.h"
#endif
#ifndef _H_GUIDEFAULTCONTROLRENDER_
#include "gui/core/guiDefaultControlRender.h"
#endif
#ifndef _GUITICKCTRL_H_
#include "gui/shiny/guiTickCtrl.h"
#endif
#ifndef _GUISCROLLCTRL_H_
#include "gui/containers/guiScrollCtrl.h"
#endif
#ifndef _GUITEXTEDITCTRL_H_
#include "gui/controls/guiTextEditCtrl.h"
#endif
#ifndef _GUIBITMAPBUTTON_H_
#include "gui/controls/guiBitmapButtonCtrl.h"
#endif
#ifndef _GUIPOPUPCTRL_H_
#include "gui/controls/guiPopUpCtrl.h"
#endif
// Forward Declare GuiInspectorGroup
class GuiInspectorGroup;
// Forward Declare GuiInspectorField
class GuiInspectorField;
// Forward Declare GuiInspectorDatablockField
class GuiInspectorDatablockField;
class GuiInspector : public GuiStackControl
{
private:
typedef GuiStackControl Parent;
public:
// Members
Vector<GuiInspectorGroup*> mGroups;
SimObjectPtr<SimObject> mTarget;
GuiInspector();
~GuiInspector();
DECLARE_CONOBJECT(GuiInspector);
virtual void parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent);
void inspectObject( SimObject *object );
void setName( StringTableEntry newName );
void clearGroups();
bool onAdd();
bool findExistentGroup( StringTableEntry groupName );
};
class GuiInspectorField : public GuiControl
{
private:
typedef GuiControl Parent;
public:
// Static Caption Width (in percentage) for all inspector fields
static S32 smCaptionWidth;
// Members
StringTableEntry mCaption;
GuiInspectorGroup* mParent;
SimObjectPtr<SimObject> mTarget;
AbstractClassRep::Field* mField;
// Constructed Field Edit Control
GuiControl* mEdit;
GuiInspectorField( GuiInspectorGroup* parent, SimObjectPtr<SimObject> target, AbstractClassRep::Field* field );
GuiInspectorField();
~GuiInspectorField();
DECLARE_CONOBJECT(GuiInspectorField);
virtual void setTarget( SimObjectPtr<SimObject> target ) { mTarget = target; };
virtual void setParent( GuiInspectorGroup* parent ) { mParent = parent; };
virtual void setField( AbstractClassRep::Field *field ) { mField = field; mCaption = field->pFieldname; };
protected:
void registerEditControl( GuiControl *ctrl );
public:
virtual GuiControl* constructEditControl();
virtual void updateValue( StringTableEntry newValue );
virtual StringTableEntry getFieldName() { return ( mField != NULL ) ? mField->pFieldname : ""; };
virtual void setData( StringTableEntry data );
virtual StringTableEntry getData();
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
virtual bool onAdd();
virtual void onRender(Point2I offset, const RectI &updateRect);
};
class GuiInspectorGroup : public GuiTickCtrl
{
private:
typedef GuiControl Parent;
public:
// Members
StringTableEntry mCaption;
Point2I mBarWidth;
bool mIsExpanded;
bool mIsAnimating;
bool mCollapsing;
S32 mAnimateDestHeight;
S32 mAnimateStep;
S32 mChildHeight;
SimObjectPtr<SimObject> mTarget;
SimObjectPtr<GuiInspector> mParent;
Vector<GuiInspectorField*> mChildren;
GuiStackControl* mStack;
// Constructor/Destructor/Conobject Declaration
GuiInspectorGroup();
GuiInspectorGroup( SimObjectPtr<SimObject> target, StringTableEntry groupName, SimObjectPtr<GuiInspector> parent );
~GuiInspectorGroup();
DECLARE_CONOBJECT(GuiInspectorGroup);
// Persistence ( Inspector Exposed Fields )
static void initPersistFields();
// Mouse Events
virtual void onMouseDown( const GuiEvent &event );
// Sizing Helpers
virtual S32 getExpandedHeight();
void resize( const Point2I &newPosition, const Point2I &newExtent );
// Sizing Animation Functions
void animateTo( S32 height );
virtual void processTick();
virtual GuiInspectorField* constructField( S32 fieldType );
virtual GuiInspectorField* findField( StringTableEntry fieldName );
// Control Rendering
virtual void onRender(Point2I offset, const RectI &updateRect);
// Publicly Accessible Information about this group
StringTableEntry getGroupName() { return mCaption; };
SimObjectPtr<SimObject> getGroupTarget() { return mTarget; };
SimObjectPtr<GuiInspector> getContentCtrl() { return mParent; };
bool onAdd();
virtual bool inspectGroup();
};
class GuiInspectorDynamicField : public GuiInspectorField
{
private:
typedef GuiInspectorField Parent;
SimObjectPtr<GuiControl> mRenameCtrl;
public:
SimFieldDictionary::Entry* mDynField;
GuiInspectorDynamicField( GuiInspectorGroup* parent, SimObjectPtr<SimObject> target, SimFieldDictionary::Entry* field );
GuiInspectorDynamicField() {};
~GuiInspectorDynamicField() {};
DECLARE_CONOBJECT(GuiInspectorDynamicField);
virtual void setData( StringTableEntry data );
virtual StringTableEntry getData();
virtual StringTableEntry getFieldName() { return ( mDynField != NULL ) ? mDynField->slotName : ""; };
// Override onAdd so we can construct our custom field name edit control
virtual bool onAdd();
// Rename a dynamic field
void renameField( StringTableEntry newFieldName );
// Create an edit control to overlay the field name (for renaming dynamic fields)
GuiControl* constructRenameControl();
// Rendering (We custom render this field type because it contains no caption rendering)
virtual void onRender(Point2I offset, const RectI &updateRect);
// Override parentResized so we can resize our renaming control
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
};
class GuiInspectorDynamicGroup : public GuiInspectorGroup
{
private:
typedef GuiInspectorGroup Parent;
public:
GuiInspectorDynamicGroup( SimObjectPtr<SimObject> target, StringTableEntry groupName, SimObjectPtr<GuiInspector> parent ) : GuiInspectorGroup( target, groupName, parent) {};
//////////////////////////////////////////////////////////////////////////
// inspectGroup is overridden in GuiInspectorDynamicGroup to inspect an
// objects FieldDictionary (dynamic fields) instead of regular persistent
// fields.
bool inspectGroup();
// For scriptable dynamic field additions
void addDynamicField();
// To make sure we expand to show add field 'button'
virtual S32 getExpandedHeight();
// Clear our fields (delete them)
void clearFields();
// For handling creation of dynamic fields
virtual void onMouseDown( const GuiEvent &event );
// Find an already existent field by name in the dictionary
virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary( StringTableEntry fieldName );
// Find an already existent field by name in the stack control
virtual GuiInspectorDynamicField* findDynamicField( StringTableEntry fieldName );
// Override onRender to add an 'Add Field' button
virtual void onRender(Point2I offset, const RectI &updateRect);
};
//////////////////////////////////////////////////////////////////////////
// GuiInspectorDatablockField - custom field type for datablock enumeration
//////////////////////////////////////////////////////////////////////////
class GuiInspectorDatablockField : public GuiInspectorField
{
private:
typedef GuiInspectorField Parent;
AbstractClassRep *mDesiredClass;
public:
DECLARE_CONOBJECT(GuiInspectorDatablockField);
GuiInspectorDatablockField( StringTableEntry className );
GuiInspectorDatablockField() { mDesiredClass = NULL; };
void setClassName( StringTableEntry className );
//////////////////////////////////////////////////////////////////////////
// Override able methods for custom edit fields (Both are REQUIRED)
//////////////////////////////////////////////////////////////////////////
virtual GuiControl* constructEditControl();
};
#endif

View File

@ -0,0 +1,350 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/editor/guiInspectorTypes.h"
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeEnum
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeEnum);
GuiControl* GuiInspectorTypeEnum::constructEditControl()
{
GuiControl* retCtrl = new GuiPopUpMenuCtrl();
// If we couldn't construct the control, bail!
if( retCtrl == NULL )
return retCtrl;
GuiPopUpMenuCtrl *menu = dynamic_cast<GuiPopUpMenuCtrl*>(retCtrl);
// Let's make it look pretty.
retCtrl->setField( "profile", "InspectorTypeEnumProfile" );
menu->setField("text", getData());
registerEditControl( retCtrl );
// Configure it to update our value when the popup is closed
char szBuffer[512];
dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(),menu->getId() );
menu->setField("Command", szBuffer );
//now add the entries
for(S32 i = 0; i < mField->table->size; i++)
menu->addEntry(mField->table->table[i].label, mField->table->table[i].index);
return retCtrl;
}
void GuiInspectorTypeEnum::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeEnum)->setInspectorFieldType("GuiInspectorTypeEnum");
}
void GuiInspectorTypeEnum::updateValue( StringTableEntry newValue )
{
GuiPopUpMenuCtrl *ctrl = dynamic_cast<GuiPopUpMenuCtrl*>( mEdit );
if( ctrl != NULL )
ctrl->setText( newValue );
}
void GuiInspectorTypeEnum::setData( StringTableEntry data )
{
if( mField == NULL || mTarget == NULL )
return;
mTarget->setDataField( mField->pFieldname, NULL, data );
// Force our edit to update
updateValue( data );
}
StringTableEntry GuiInspectorTypeEnum::getData()
{
if( mField == NULL || mTarget == NULL )
return "";
return mTarget->getDataField( mField->pFieldname, NULL );
}
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeCheckBox
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeCheckBox);
GuiControl* GuiInspectorTypeCheckBox::constructEditControl()
{
GuiControl* retCtrl = new GuiCheckBoxCtrl();
// If we couldn't construct the control, bail!
if( retCtrl == NULL )
return retCtrl;
GuiCheckBoxCtrl *check = dynamic_cast<GuiCheckBoxCtrl*>(retCtrl);
// Let's make it look pretty.
retCtrl->setField( "profile", "InspectorTypeCheckboxProfile" );
retCtrl->setField( "text", "" );
check->mIndent = 4;
retCtrl->setScriptValue( getData() );
registerEditControl( retCtrl );
// Configure it to update our value when the popup is closed
char szBuffer[512];
dSprintf( szBuffer, 512, "%d.apply(%d.getValue());",getId(),check->getId() );
check->setField("Command", szBuffer );
return retCtrl;
}
void GuiInspectorTypeCheckBox::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeBool)->setInspectorFieldType("GuiInspectorTypeCheckBox");
}
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeGuiProfile
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeGuiProfile);
void GuiInspectorTypeGuiProfile::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeGuiProfile)->setInspectorFieldType("GuiInspectorTypeGuiProfile");
}
static S32 QSORT_CALLBACK stringCompare(const void *a,const void *b)
{
StringTableEntry sa = *(StringTableEntry*)a;
StringTableEntry sb = *(StringTableEntry*)b;
return(dStricmp(sb, sa));
}
GuiControl* GuiInspectorTypeGuiProfile::constructEditControl()
{
GuiControl* retCtrl = new GuiPopUpMenuCtrl();
// If we couldn't construct the control, bail!
if( retCtrl == NULL )
return retCtrl;
GuiPopUpMenuCtrl *menu = dynamic_cast<GuiPopUpMenuCtrl*>(retCtrl);
// Let's make it look pretty.
retCtrl->setField( "profile", "InspectorTypeEnumProfile" );
menu->setField("text", getData());
registerEditControl( retCtrl );
// Configure it to update our value when the popup is closed
char szBuffer[512];
dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(),menu->getId() );
menu->setField("Command", szBuffer );
Vector<StringTableEntry> entries;
SimGroup * grp = Sim::getGuiDataGroup();
for(SimGroup::iterator i = grp->begin(); i != grp->end(); i++)
{
GuiControlProfile * profile = dynamic_cast<GuiControlProfile *>(*i);
if(profile)
entries.push_back(profile->getName());
}
// sort the entries
dQsort(entries.address(), entries.size(), sizeof(StringTableEntry), stringCompare);
for(U32 j = 0; j < entries.size(); j++)
menu->addEntry(entries[j], 0);
return retCtrl;
}
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeFileName
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeFileName);
void GuiInspectorTypeFileName::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeFilename)->setInspectorFieldType("GuiInspectorTypeFileName");
}
GuiControl* GuiInspectorTypeFileName::constructEditControl()
{
GuiControl* retCtrl = new GuiTextEditCtrl();
// If we couldn't construct the control, bail!
if( retCtrl == NULL )
return retCtrl;
// Let's make it look pretty.
retCtrl->setField( "profile", "GuiInspectorTextEditProfile" );
// Don't forget to register ourselves
registerEditControl( retCtrl );
char szBuffer[512];
dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(),retCtrl->getId() );
retCtrl->setField("AltCommand", szBuffer );
retCtrl->setField("Validate", szBuffer );
mBrowseButton = new GuiButtonCtrl();
if( mBrowseButton != NULL )
{
RectI browseRect( Point2I( ( mBounds.point.x + mBounds.extent.x) - 26, mBounds.point.y + 2), Point2I(20, mBounds.extent.y - 4) );
char szBuffer[512];
dSprintf( szBuffer, 512, "getLoadFilename(\"*.*\", \"%d.apply\", \"%s\");",getId(), getData());
mBrowseButton->setField( "Command", szBuffer );
mBrowseButton->setField( "text", "..." );
mBrowseButton->setField( "Profile", "GuiInspectorTypeFileNameProfile" );
mBrowseButton->registerObject();
addObject( mBrowseButton );
// Position
mBrowseButton->resize( browseRect.point, browseRect.extent );
}
return retCtrl;
}
void GuiInspectorTypeFileName::resize( const Point2I &newPosition, const Point2I &newExtent )
{
Parent::resize( newPosition, newExtent );
if( mEdit != NULL )
{
// Calculate Caption Rect
RectI captionRect( mBounds.point , Point2I( mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0 ) ) - 2, mBounds.extent.y ) );
// Calculate Edit Field Rect
RectI editFieldRect( Point2I( captionRect.extent.x + 1, 0 ) , Point2I( mBounds.extent.x - ( captionRect.extent.x + 25 ) , mBounds.extent.y ) );
mEdit->resize( editFieldRect.point, editFieldRect.extent );
if( mBrowseButton != NULL )
{
RectI browseRect( Point2I( ( mBounds.point.x + mBounds.extent.x) - 26, 2), Point2I(20, mBounds.extent.y - 4) );
mBrowseButton->resize( browseRect.point, browseRect.extent );
}
}
}
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeColor (Base for ColorI/ColorF)
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeColor);
GuiControl* GuiInspectorTypeColor::constructEditControl()
{
GuiControl* retCtrl = new GuiTextEditCtrl();
// If we couldn't construct the control, bail!
if( retCtrl == NULL )
return retCtrl;
// Let's make it look pretty.
retCtrl->setField( "profile", "GuiInspectorTextEditProfile" );
// Don't forget to register ourselves
registerEditControl( retCtrl );
char szBuffer[512];
dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() );
retCtrl->setField("AltCommand", szBuffer );
retCtrl->setField("Validate", szBuffer );
mBrowseButton = new GuiButtonCtrl();
if( mBrowseButton != NULL )
{
RectI browseRect( Point2I( ( mBounds.point.x + mBounds.extent.x) - 26, mBounds.point.y + 2), Point2I(20, mBounds.extent.y - 4) );
char szBuffer[512];
dSprintf( szBuffer, 512, "%s(\"%s\", \"%d.apply\");", mColorFunction, getData(), getId());
mBrowseButton->setField( "Command", szBuffer );
mBrowseButton->setField( "text", "..." );
mBrowseButton->setField( "Profile", "GuiInspectorTypeFileNameProfile" );
mBrowseButton->registerObject();
addObject( mBrowseButton );
// Position
mBrowseButton->resize( browseRect.point, browseRect.extent );
}
return retCtrl;
}
void GuiInspectorTypeColor::resize( const Point2I &newPosition, const Point2I &newExtent )
{
Parent::resize( newPosition, newExtent );
if( mEdit != NULL )
{
// Calculate Caption Rect
RectI captionRect( mBounds.point , Point2I( mFloor( mBounds.extent.x * (F32)( (F32)GuiInspectorField::smCaptionWidth / 100.0 ) ) - 2, mBounds.extent.y ) );
// Calculate Edit Field Rect
RectI editFieldRect( Point2I( captionRect.extent.x + 1, 0 ) , Point2I( mBounds.extent.x - ( captionRect.extent.x + 25 ) , mBounds.extent.y ) );
mEdit->resize( editFieldRect.point, editFieldRect.extent );
if( mBrowseButton != NULL )
{
RectI browseRect( Point2I( ( mBounds.point.x + mBounds.extent.x) - 26, 2), Point2I(20, mBounds.extent.y - 4) );
mBrowseButton->resize( browseRect.point, browseRect.extent );
}
}
}
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeColorI
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeColorI);
void GuiInspectorTypeColorI::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeColorI)->setInspectorFieldType("GuiInspectorTypeColorI");
}
GuiInspectorTypeColorI::GuiInspectorTypeColorI()
{
mColorFunction = StringTable->insert("getColorI");
}
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeColorF
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_CONOBJECT(GuiInspectorTypeColorF);
void GuiInspectorTypeColorF::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeColorF)->setInspectorFieldType("GuiInspectorTypeColorF");
}
GuiInspectorTypeColorF::GuiInspectorTypeColorF()
{
mColorFunction = StringTable->insert("getColorF");
}

View File

@ -0,0 +1,162 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUI_INSPECTOR_TYPES_H_
#define _GUI_INSPECTOR_TYPES_H_
#ifndef _GUI_INSPECTOR_H_
#include "gui/editor/guiInspector.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _H_GUIDEFAULTCONTROLRENDER_
#include "gui/core/guiDefaultControlRender.h"
#endif
#ifndef _GUISCROLLCTRL_H_
#include "gui/containers/guiScrollCtrl.h"
#endif
#ifndef _GUITEXTEDITCTRL_H_
#include "gui/controls/guiTextEditCtrl.h"
#endif
#ifndef _GUIPOPUPCTRL_H_
#include "gui/controls/guiPopUpCtrl.h"
#endif
#ifndef _GUICHECKBOXCTRL_H_
#include "gui/controls/guiCheckBoxCtrl.h"
#endif
//////////////////////////////////////////////////////////////////////////
// TypeEnum GuiInspectorField Class
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeEnum : public GuiInspectorField
{
private:
typedef GuiInspectorField Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeEnum);
static void consoleInit();
//////////////////////////////////////////////////////////////////////////
// Override able methods for custom edit fields
//////////////////////////////////////////////////////////////////////////
virtual GuiControl* constructEditControl();
virtual void setData( StringTableEntry data );
virtual StringTableEntry getData();
virtual void updateValue( StringTableEntry newValue );
};
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeCheckBox Class
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeCheckBox : public GuiInspectorField
{
private:
typedef GuiInspectorField Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeCheckBox);
static void consoleInit();
//////////////////////////////////////////////////////////////////////////
// Override able methods for custom edit fields (Both are REQUIRED)
//////////////////////////////////////////////////////////////////////////
virtual GuiControl* constructEditControl();
};
//////////////////////////////////////////////////////////////////////////
// GuiInspectorTypeGuiProfile Class
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeGuiProfile : public GuiInspectorTypeEnum
{
private:
typedef GuiInspectorTypeEnum Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeGuiProfile);
static void consoleInit();
//////////////////////////////////////////////////////////////////////////
// Override able methods for custom edit fields (Both are REQUIRED)
//////////////////////////////////////////////////////////////////////////
virtual GuiControl* constructEditControl();
};
//////////////////////////////////////////////////////////////////////////
// TypeFileName GuiInspectorField Class
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeFileName : public GuiInspectorField
{
private:
typedef GuiInspectorField Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeFileName);
static void consoleInit();
SimObjectPtr<GuiButtonCtrl> mBrowseButton;
//////////////////////////////////////////////////////////////////////////
// Override able methods for custom edit fields
//////////////////////////////////////////////////////////////////////////
virtual GuiControl* constructEditControl();
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
};
//////////////////////////////////////////////////////////////////////////
// TypeColor GuiInspectorField Class (Base for ColorI/ColorF)
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeColor : public GuiInspectorField
{
private:
typedef GuiInspectorField Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeColor);
StringTableEntry mColorFunction;
SimObjectPtr<GuiButtonCtrl> mBrowseButton;
//////////////////////////////////////////////////////////////////////////
// Override able methods for custom edit fields
//////////////////////////////////////////////////////////////////////////
virtual GuiControl* constructEditControl();
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
};
//////////////////////////////////////////////////////////////////////////
// TypeColorI GuiInspectorField Class
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeColorI : public GuiInspectorTypeColor
{
private:
typedef GuiInspectorTypeColor Parent;
public:
GuiInspectorTypeColorI();
DECLARE_CONOBJECT(GuiInspectorTypeColorI);
static void consoleInit();
};
//////////////////////////////////////////////////////////////////////////
// TypeColorF GuiInspectorField Class
//////////////////////////////////////////////////////////////////////////
class GuiInspectorTypeColorF : public GuiInspectorTypeColor
{
private:
typedef GuiInspectorTypeColor Parent;
public:
GuiInspectorTypeColorF();
DECLARE_CONOBJECT(GuiInspectorTypeColorF);
static void consoleInit();
};
#endif

851
engine/gui/editor/guiMenuBar.cc Executable file
View File

@ -0,0 +1,851 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiCanvas.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/controls/guiTextListCtrl.h"
#include "sim/actionMap.h"
#include "gui/editor/guiMenuBar.h"
// menu bar:
// basic idea - fixed height control bar at the top of a window, placed and sized in gui editor
// menu text for menus or menu items should not begin with a digit
// all menus can be removed via the clearMenus() console command
// each menu is added via the addMenu(menuText, menuId) console command
// each menu is added with a menu id
// menu items are added to menus via that addMenuItem(menu, menuItemText, menuItemId, accelerator, checkGroup) console command
// each menu item is added with a menu item id and an optional accelerator
// menu items are initially enabled, but can be disabled/re-enabled via the setMenuItemEnable(menu,menuItem,bool)
// menu text can be set via the setMenuText(menu, newMenuText) console method
// menu item text can be set via the setMenuItemText console method
// menu items can be removed via the removeMenuItem(menu, menuItem) console command
// menu items can be cleared via the clearMenuItems(menu) console command
// menus can be hidden or shown via the setMenuVisible(menu, bool) console command
// menu items can be hidden or shown via the setMenuItemVisible(menu, menuItem, bool) console command
// menu items can be check'd via the setMenuItemChecked(menu, menuItem, bool) console command
// if the bool is true, any other items in that menu item's check group become unchecked.
//
// menu items can have a bitmap set on them via the setMenuItemBitmap(menu, menuItem, bitmapIndex)
// passing -1 for the bitmap index will result in no bitmap being shown
// the index paramater is an index into the bitmap array of the associated profile
// this can be used, for example, to display a check next to a selected menu item
// bitmap indices are actually multiplied by 3 when indexing into the bitmap
// since bitmaps have normal, selected and disabled states.
//
// menus can be removed via the removeMenu console command
// specification arguments for menus and menu items can be either the id or the text of the menu or menu item
// adding the menu item "-" will add an un-selectable seperator to the menu
// callbacks:
// when a menu is clicked, before it is displayed, the menu calls its onMenuSelect(menuId, menuText) method -
// this allows the callback to enable/disable menu items, or add menu items in a context-sensitive way
// when a menu item is clicked, the menu removes itself from display, then calls onMenuItemSelect(menuId, menuText, menuItemId, menuItemText)
// the initial implementation does not support:
// hierarchal menus
// keyboard accelerators on menu text (i.e. via alt-key combos)
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiMenuBar);
//------------------------------------------------------------------------------
// console methods
//------------------------------------------------------------------------------
ConsoleMethod(GuiMenuBar, clearMenus, void, 2, 2, "() - clears all the menus from the menu bar.")
{
object->clearMenus();
}
ConsoleMethod(GuiMenuBar, addMenu, void, 4, 4, "(string menuText, int menuId) - adds a new menu to the menu bar.")
{
if(dIsdigit(argv[2][0]))
{
Con::errorf("Cannot add menu %s (id = %s). First character of a menu's text cannot be a digit.", argv[2], argv[3]);
return;
}
object->addMenu(argv[2], dAtoi(argv[3]));
}
ConsoleMethod(GuiMenuBar, addMenuItem, void, 5, 7, "(string menu, string menuItemText, int menuItemId, string accelerator = NULL, int checkGroup = -1) - adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.")
{
if(dIsdigit(argv[3][0]))
{
Con::errorf("Cannot add menu item %s (id = %s). First character of a menu item's text cannot be a digit.", argv[3], argv[4]);
return;
}
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for addMenuItem.", argv[2]);
return;
}
object->addMenuItem(menu, argv[3], dAtoi(argv[4]), argc == 5 ? "" : argv[5], argc < 7 ? -1 : dAtoi(argv[6]));
}
ConsoleMethod(GuiMenuBar, setMenuItemEnable, void, 5, 5, "(string menu, string menuItem, bool enabled) - sets the menu item to enabled or disabled based on the enable parameter. The specified menu and menu item can either be text or ids.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuItemEnable.", argv[2]);
return;
}
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
if(!menuItem)
{
Con::errorf("Cannot find menu item %s for setMenuItemEnable.", argv[3]);
return;
}
menuItem->enabled = dAtob(argv[4]);
}
ConsoleMethod(GuiMenuBar, setMenuItemChecked, void, 5, 5, "(string menu, string menuItem, bool checked) - sets the menu item bitmap to a check mark, which must be the first element in the bitmap array. Any other menu items in the menu with the same check group become unchecked if they are checked.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuItemChecked.", argv[2]);
return;
}
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
if(!menuItem)
{
Con::errorf("Cannot find menu item %s for setMenuItemChecked.", argv[3]);
return;
}
bool checked = dAtob(argv[4]);
if(checked && menuItem->checkGroup != -1)
{
// first, uncheck everything in the group:
for(GuiMenuBar::MenuItem *itemWalk = menu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem)
if(itemWalk->checkGroup == menuItem->checkGroup && itemWalk->bitmapIndex == 0)
itemWalk->bitmapIndex = -1;
}
menuItem->bitmapIndex = checked ? 0 : -1;
}
ConsoleMethod(GuiMenuBar, setMenuText, void, 4, 4, "(string menu, string newMenuText) - sets the text of the specified menu to the new string.")
{
if(dIsdigit(argv[3][0]))
{
Con::errorf("Cannot name menu %s to %s. First character of a menu's text cannot be a digit.", argv[2], argv[3]);
return;
}
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuText.", argv[2]);
return;
}
dFree(menu->text);
menu->text = dStrdup(argv[3]);
object->menuBarDirty = true;
}
ConsoleMethod(GuiMenuBar, setMenuVisible, void, 4, 4, "(string menu, bool visible) - sets the whether or not to display the specified menu.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuVisible.", argv[2]);
return;
}
menu->visible = dAtob(argv[3]);
object->menuBarDirty = true;
object->setUpdate();
}
ConsoleMethod(GuiMenuBar, setMenuItemText, void, 5, 5, "(string menu, string menuItem, string newMenuItemText) - sets the text of the specified menu item to the new string.")
{
if(dIsdigit(argv[4][0]))
{
Con::errorf("Cannot name menu item %s to %s. First character of a menu item's text cannot be a digit.", argv[3], argv[4]);
return;
}
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuItemText.", argv[2]);
return;
}
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
if(!menuItem)
{
Con::errorf("Cannot find menu item %s for setMenuItemText.", argv[3]);
return;
}
dFree(menuItem->text);
menuItem->text = dStrdup(argv[4]);
}
ConsoleMethod(GuiMenuBar, setMenuItemVisible, void, 5, 5, "(string menu, string menuItem, bool isVisible) - sets the specified menu item to be either visible or not.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuItemVisible.", argv[2]);
return;
}
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
if(!menuItem)
{
Con::errorf("Cannot find menu item %s for setMenuItemVisible.", argv[3]);
return;
}
menuItem->visible = dAtob(argv[4]);
}
ConsoleMethod(GuiMenuBar, setMenuItemBitmap, void, 5, 5, "(string menu, string menuItem, int bitmapIndex) - sets the specified menu item bitmap index in the bitmap array. Setting the item's index to -1 will remove any bitmap.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for setMenuItemBitmap.", argv[2]);
return;
}
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
if(!menuItem)
{
Con::errorf("Cannot find menu item %s for setMenuItemBitmap.", argv[3]);
return;
}
menuItem->bitmapIndex = dAtoi(argv[4]);
}
ConsoleMethod(GuiMenuBar, removeMenuItem, void, 4, 4, "(string menu, string menuItem) - removes the specified menu item from the menu.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for removeMenuItem.", argv[2]);
return;
}
GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, argv[3]);
if(!menuItem)
{
Con::errorf("Cannot find menu item %s for removeMenuItem.", argv[3]);
return;
}
object->removeMenuItem(menu, menuItem);
}
ConsoleMethod(GuiMenuBar, clearMenuItems, void, 3, 3, "(string menu) - removes all the menu items from the specified menu.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for clearMenuItems.", argv[2]);
return;
}
object->clearMenuItems(menu);
}
ConsoleMethod(GuiMenuBar, removeMenu, void, 3, 3, "(string menu) - removes the specified menu from the menu bar.")
{
GuiMenuBar::Menu *menu = object->findMenu(argv[2]);
if(!menu)
{
Con::errorf("Cannot find menu %s for removeMenu.", argv[2]);
return;
}
object->clearMenuItems(menu);
object->menuBarDirty = true;
}
//------------------------------------------------------------------------------
// menu management methods
//------------------------------------------------------------------------------
void GuiMenuBar::addMenu(const char *menuText, U32 menuId)
{
// allocate the menu
Menu *newMenu = new Menu;
newMenu->text = dStrdup(menuText);
newMenu->id = menuId;
newMenu->nextMenu = NULL;
newMenu->firstMenuItem = NULL;
newMenu->visible = true;
// add it to the menu list
menuBarDirty = true;
Menu **walk;
for(walk = &menuList; *walk; walk = &(*walk)->nextMenu)
;
*walk = newMenu;
}
GuiMenuBar::Menu *GuiMenuBar::findMenu(const char *menu)
{
if(dIsdigit(menu[0]))
{
U32 id = dAtoi(menu);
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
if(id == walk->id)
return walk;
return NULL;
}
else
{
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
if(!dStricmp(menu, walk->text))
return walk;
return NULL;
}
}
GuiMenuBar::MenuItem *GuiMenuBar::findMenuItem(Menu *menu, const char *menuItem)
{
if(dIsdigit(menuItem[0]))
{
U32 id = dAtoi(menuItem);
for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
if(id == walk->id)
return walk;
return NULL;
}
else
{
for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem)
if(!dStricmp(menuItem, walk->text))
return walk;
return NULL;
}
}
void GuiMenuBar::removeMenu(Menu *menu)
{
menuBarDirty = true;
clearMenuItems(menu);
for(Menu **walk = &menuList; *walk; walk = &(*walk)->nextMenu)
{
if(*walk == menu)
{
*walk = menu->nextMenu;
break;
}
}
dFree(menu->text);
delete menu;
}
void GuiMenuBar::removeMenuItem(Menu *menu, MenuItem *menuItem)
{
for(MenuItem **walk = &menu->firstMenuItem; *walk; walk = &(*walk)->nextMenuItem)
{
if(*walk == menuItem)
{
*walk = menuItem->nextMenuItem;
break;
}
}
dFree(menuItem->text);
dFree(menuItem->accelerator);
delete menuItem;
}
void GuiMenuBar::addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup)
{
// allocate the new menu item
MenuItem *newMenuItem = new MenuItem;
newMenuItem->text = dStrdup(text);
if(accelerator[0])
newMenuItem->accelerator = dStrdup(accelerator);
else
newMenuItem->accelerator = NULL;
newMenuItem->id = id;
newMenuItem->checkGroup = checkGroup;
newMenuItem->nextMenuItem = NULL;
newMenuItem->acceleratorIndex = 0;
newMenuItem->enabled = text[0] != '-';
newMenuItem->visible = true;
newMenuItem->bitmapIndex = -1;
// link it into the menu's menu item list
MenuItem **walk = &menu->firstMenuItem;
while(*walk)
walk = &(*walk)->nextMenuItem;
*walk = newMenuItem;
}
void GuiMenuBar::clearMenuItems(Menu *menu)
{
while(menu->firstMenuItem)
removeMenuItem(menu, menu->firstMenuItem);
}
void GuiMenuBar::clearMenus()
{
while(menuList)
removeMenu(menuList);
}
//------------------------------------------------------------------------------
// initialization, input and render methods
//------------------------------------------------------------------------------
GuiMenuBar::GuiMenuBar()
{
menuList = NULL;
menuBarDirty = true;
mouseDownMenu = NULL;
mouseOverMenu = NULL;
mCurAcceleratorIndex = 0;
mBackground = NULL;
}
bool GuiMenuBar::onWake()
{
if(!Parent::onWake())
return false;
mProfile->constructBitmapArray(); // if a bitmap was specified...
maxBitmapSize.set(0,0);
S32 numBitmaps = mProfile->mBitmapArrayRects.size();
if(numBitmaps)
{
RectI *bitmapBounds = mProfile->mBitmapArrayRects.address();
for(S32 i = 0; i < numBitmaps; i++)
{
if(bitmapBounds[i].extent.x > maxBitmapSize.x)
maxBitmapSize.x = bitmapBounds[i].extent.x;
if(bitmapBounds[i].extent.y > maxBitmapSize.y)
maxBitmapSize.y = bitmapBounds[i].extent.y;
}
}
return true;
}
GuiMenuBar::Menu *GuiMenuBar::findHitMenu(Point2I mousePoint)
{
Point2I pos = globalToLocalCoord(mousePoint);
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
if(walk->visible && walk->bounds.pointInRect(pos))
return walk;
return NULL;
}
void GuiMenuBar::onPreRender()
{
Parent::onPreRender();
if(menuBarDirty)
{
menuBarDirty = false;
U32 curX = 0;
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
{
if(!walk->visible)
continue;
walk->bounds.set(curX, 1, mProfile->mFont->getStrWidth((const UTF8 *)walk->text) + 12, mBounds.extent.y - 2);
curX += walk->bounds.extent.x;
}
mouseOverMenu = NULL;
mouseDownMenu = NULL;
}
}
void GuiMenuBar::checkMenuMouseMove(const GuiEvent &event)
{
Menu *hit = findHitMenu(event.mousePoint);
if(hit && hit != mouseDownMenu)
{
// gotta close out the current menu...
mTextList->setSelectedCell(Point2I(-1, -1));
closeMenu();
mouseOverMenu = mouseDownMenu = hit;
setUpdate();
onAction();
}
}
void GuiMenuBar::onMouseMove(const GuiEvent &event)
{
Menu *hit = findHitMenu(event.mousePoint);
if(hit != mouseOverMenu)
{
mouseOverMenu = hit;
setUpdate();
}
}
void GuiMenuBar::onMouseLeave(const GuiEvent &event)
{
if(mouseOverMenu)
setUpdate();
mouseOverMenu = NULL;
}
void GuiMenuBar::onMouseDragged(const GuiEvent &event)
{
Menu *hit = findHitMenu(event.mousePoint);
if(hit != mouseOverMenu)
{
mouseOverMenu = hit;
mouseDownMenu = hit;
setUpdate();
onAction();
}
}
void GuiMenuBar::onMouseDown(const GuiEvent &event)
{
mouseDownMenu = mouseOverMenu = findHitMenu(event.mousePoint);
setUpdate();
onAction();
}
void GuiMenuBar::onMouseUp(const GuiEvent &event)
{
mouseDownMenu = NULL;
setUpdate();
}
void GuiMenuBar::onRender(Point2I offset, const RectI &updateRect)
{
//if opaque, fill the update rect with the fill color
if (mProfile->mOpaque)
dglDrawRectFill(RectI(offset, mBounds.extent), mProfile->mFillColor);
for(Menu *walk = menuList; walk; walk = walk->nextMenu)
{
if(!walk->visible)
continue;
ColorI fontColor = mProfile->mFontColor;
RectI bounds = walk->bounds;
bounds.point += offset;
Point2I start;
start.x = walk->bounds.point.x + 6;
start.y = walk->bounds.point.y + ( walk->bounds.extent.y - ( mProfile->mFont->getHeight() - 2 ) ) / 2;
if(walk == mouseDownMenu)
renderSlightlyLoweredBox(bounds, mProfile);
else if(walk == mouseOverMenu && mouseDownMenu == NULL)
renderSlightlyRaisedBox(bounds, mProfile);
dglSetBitmapModulation( fontColor );
dglDrawText( mProfile->mFont, start + offset, walk->text, mProfile->mFontColors );
}
}
void GuiMenuBar::buildAcceleratorMap()
{
Parent::buildAcceleratorMap();
// ok, accelerator map is cleared...
// add all our keys:
mCurAcceleratorIndex = 1;
for(Menu *menu = menuList; menu; menu = menu->nextMenu)
{
for(MenuItem *item = menu->firstMenuItem; item; item = item->nextMenuItem)
{
if(!item->accelerator)
{
item->accelerator = 0;
continue;
}
EventDescriptor accelEvent;
ActionMap::createEventDescriptor(item->accelerator, &accelEvent);
//now we have a modifier, and a key, add them to the canvas
GuiCanvas *root = getRoot();
if (root)
root->addAcceleratorKey(this, mCurAcceleratorIndex, accelEvent.eventCode, accelEvent.flags);
item->acceleratorIndex = mCurAcceleratorIndex;
mCurAcceleratorIndex++;
}
}
}
void GuiMenuBar::acceleratorKeyPress(U32 index)
{
// loop through all the menus
// and find the item that corresponds to the accelerator index
for(Menu *menu = menuList; menu; menu = menu->nextMenu)
{
if(!menu->visible)
continue;
for(MenuItem *item = menu->firstMenuItem; item; item = item->nextMenuItem)
{
if(item->acceleratorIndex == index)
{
// first, call the script callback for menu selection:
Con::executef( this, 4, "onMenuSelect", Con::getIntArg(menu->id),
menu->text);
if(item->visible)
menuItemSelected(menu, item);
return;
}
}
}
}
//------------------------------------------------------------------------------
// Menu display class methods
//------------------------------------------------------------------------------
GuiMenuBackgroundCtrl::GuiMenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl *textList)
{
mMenuBarCtrl = ctrl;
mTextList = textList;
}
void GuiMenuBackgroundCtrl::onMouseDown(const GuiEvent &event)
{
mTextList->setSelectedCell(Point2I(-1,-1));
mMenuBarCtrl->closeMenu();
}
void GuiMenuBackgroundCtrl::onMouseMove(const GuiEvent &event)
{
GuiCanvas *root = getRoot();
GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1);
if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right...
mMenuBarCtrl->checkMenuMouseMove(event);
}
void GuiMenuBackgroundCtrl::onMouseDragged(const GuiEvent &event)
{
GuiCanvas *root = getRoot();
GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1);
if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right...
mMenuBarCtrl->checkMenuMouseMove(event);
}
GuiMenuTextListCtrl::GuiMenuTextListCtrl(GuiMenuBar *ctrl)
{
mMenuBarCtrl = ctrl;
}
void GuiMenuTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
{
if(dStrcmp(mList[cell.y].text + 2, "-\t"))
Parent::onRenderCell(offset, cell, selected, mouseOver);
else
{
S32 yp = offset.y + mCellSize.y / 2;
dglDrawLine(offset.x, yp, offset.x + mCellSize.x, yp, ColorI(128,128,128));
dglDrawLine(offset.x, yp+1, offset.x + mCellSize.x, yp+1, ColorI(255,255,255));
}
// now see if there's a bitmap...
U8 idx = mList[cell.y].text[0];
if(idx != 1)
{
// there's a bitmap...
U32 index = U32(idx - 2) * 3;
if(!mList[cell.y].active)
index += 2;
else if(selected || mouseOver)
index ++;
RectI rect = mProfile->mBitmapArrayRects[index];
Point2I off = mMenuBarCtrl->maxBitmapSize - rect.extent;
off /= 2;
dglClearBitmapModulation();
dglDrawBitmapSR(mProfile->mTextureHandle, offset + off, rect);
}
}
bool GuiMenuTextListCtrl::onKeyDown(const GuiEvent &event)
{
//if the control is a dead end, don't process the input:
if ( !mVisible || !mActive || !mAwake )
return false;
//see if the key down is a <return> or not
if ( event.modifier == 0 )
{
if ( event.keyCode == KEY_RETURN )
{
mMenuBarCtrl->closeMenu();
return true;
}
else if ( event.keyCode == KEY_ESCAPE )
{
mSelectedCell.set( -1, -1 );
mMenuBarCtrl->closeMenu();
return true;
}
}
//otherwise, pass the event to it's parent
return Parent::onKeyDown(event);
}
void GuiMenuTextListCtrl::onMouseDown(const GuiEvent &event)
{
Parent::onMouseDown(event);
mMenuBarCtrl->closeMenu();
}
void GuiMenuTextListCtrl::onMouseUp(const GuiEvent &event)
{
// ok, this is kind of strange... but!
// here's the deal: if we get a mouse up in this control
// it means the mouse was dragged from the initial menu mouse click
// so: activate the menu result as though this event were,
// instead, a mouse down.
onMouseDown(event);
}
//------------------------------------------------------------------------------
void GuiMenuBar::menuItemSelected(GuiMenuBar::Menu *menu, GuiMenuBar::MenuItem *item)
{
if(item->enabled)
Con::executef( this, 6, "onMenuItemSelect", Con::getIntArg(menu->id),
menu->text, Con::getIntArg(item->id), item->text);
}
void GuiMenuBar::onSleep()
{
if(mBackground) // a menu is up?
{
mTextList->setSelectedCell(Point2I(-1, -1));
closeMenu();
}
Parent::onSleep();
}
void GuiMenuBar::closeMenu()
{
// Get the selection from the text list:
S32 selectionIndex = mTextList->getSelectedCell().y;
// Pop the background:
getRoot()->popDialogControl(mBackground);
// Kill the popup:
mBackground->deleteObject();
mBackground = NULL;
// Now perform the popup action:
if ( selectionIndex != -1 )
{
MenuItem *list = mouseDownMenu->firstMenuItem;
while(selectionIndex && list)
{
list = list->nextMenuItem;
selectionIndex--;
}
if(list)
menuItemSelected(mouseDownMenu, list);
}
mouseDownMenu = NULL;
}
//------------------------------------------------------------------------------
void GuiMenuBar::onAction()
{
if(!mouseDownMenu)
return;
// first, call the script callback for menu selection:
Con::executef( this, 4, "onMenuSelect", Con::getIntArg(mouseDownMenu->id),
mouseDownMenu->text);
MenuItem *visWalk = mouseDownMenu->firstMenuItem;
while(visWalk)
{
if(visWalk->visible)
break;
visWalk = visWalk->nextMenuItem;
}
if(!visWalk)
{
mouseDownMenu = NULL;
return;
}
mTextList = new GuiMenuTextListCtrl(this);
mTextList->mProfile = mProfile;
mBackground = new GuiMenuBackgroundCtrl(this, mTextList);
GuiCanvas *root = getRoot();
Point2I windowExt = root->mBounds.extent;
mBackground->mBounds.point.set(0,0);
mBackground->mBounds.extent = root->mBounds.extent;
S32 textWidth = 0, width = 0;
S32 acceleratorWidth = 0;
GFont *font = mProfile->mFont;
for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem)
{
if(!walk->visible)
continue;
S32 iTextWidth = font->getStrWidth((const UTF8 *)walk->text);
S32 iAcceleratorWidth = walk->accelerator ? font->getStrWidth((const UTF8 *)walk->accelerator) : 0;
if(iTextWidth > textWidth)
textWidth = iTextWidth;
if(iAcceleratorWidth > acceleratorWidth)
acceleratorWidth = iAcceleratorWidth;
}
width = textWidth + acceleratorWidth + maxBitmapSize.x * 2 + 2 + 4;
mTextList->setCellSize(Point2I(width, font->getHeight()+3));
mTextList->clearColumnOffsets();
mTextList->addColumnOffset(-1); // add an empty column in for the bitmap index.
mTextList->addColumnOffset(maxBitmapSize.x + 1);
mTextList->addColumnOffset(maxBitmapSize.x + 1 + textWidth + 4);
U32 entryCount = 0;
for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem)
{
if(!walk->visible)
continue;
char buf[512];
char bitmapIndex = 1;
if(walk->bitmapIndex >= 0 && (walk->bitmapIndex * 3 <= mProfile->mBitmapArrayRects.size()))
bitmapIndex = walk->bitmapIndex + 2;
dSprintf(buf, sizeof(buf), "%c\t%s\t%s", bitmapIndex, walk->text, walk->accelerator ? walk->accelerator : "");
mTextList->addEntry(entryCount, buf);
if(!walk->enabled)
mTextList->setEntryActive(entryCount, false);
entryCount++;
}
Point2I menuPoint = localToGlobalCoord(mouseDownMenu->bounds.point);
menuPoint.y += mouseDownMenu->bounds.extent.y + 2;
GuiControl *ctrl = new GuiControl;
ctrl->mBounds.point = menuPoint;
ctrl->mBounds.extent = mTextList->mBounds.extent + Point2I(6, 6);
ctrl->mProfile = mProfile;
mTextList->mBounds.point += Point2I(3,3);
//mTextList->mBounds.point = Point2I(3,3);
mTextList->registerObject();
mBackground->registerObject();
ctrl->registerObject();
mBackground->addObject( ctrl );
ctrl->addObject( mTextList );
root->pushDialogControl(mBackground, mLayer + 1);
mTextList->setFirstResponder();
}

131
engine/gui/editor/guiMenuBar.h Executable file
View File

@ -0,0 +1,131 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIMENUBAR_H_
#define _GUIMENUBAR_H_
#ifndef _GUITEXTLISTCTRL_H_
#include "gui/guiTextListCtrl.h"
#endif
class GuiMenuBar;
class GuiMenuTextListCtrl;
class GuiMenuBackgroundCtrl : public GuiControl
{
protected:
GuiMenuBar *mMenuBarCtrl;
GuiMenuTextListCtrl *mTextList;
public:
GuiMenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl* textList);
void onMouseDown(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
};
//------------------------------------------------------------------------------
class GuiMenuTextListCtrl : public GuiTextListCtrl
{
private:
typedef GuiTextListCtrl Parent;
protected:
GuiMenuBar *mMenuBarCtrl;
public:
GuiMenuTextListCtrl(); // for inheritance
GuiMenuTextListCtrl(GuiMenuBar *ctrl);
// GuiControl overloads:
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
};
//------------------------------------------------------------------------------
class GuiMenuBar : public GuiControl
{
typedef GuiControl Parent;
public:
struct MenuItem // an individual item in a pull-down menu
{
char *text; // the text of the menu item
U32 id; // a script-assigned identifier
char *accelerator; // the keyboard accelerator shortcut for the menu item
U32 acceleratorIndex; // index of this accelerator
bool enabled; // true if the menu item is selectable
bool visible; // true if the menu item is visible
S32 bitmapIndex; // index of the bitmap in the bitmap array
S32 checkGroup; // the group index of the item visa vi check marks -
// only one item in the group can be checked.
MenuItem *nextMenuItem; // next menu item in the linked list
};
struct Menu
{
char *text;
U32 id;
RectI bounds;
bool visible;
Menu *nextMenu;
MenuItem *firstMenuItem;
};
GuiMenuBackgroundCtrl *mBackground;
GuiMenuTextListCtrl *mTextList;
Menu *menuList;
Menu *mouseDownMenu;
Menu *mouseOverMenu;
bool menuBarDirty;
U32 mCurAcceleratorIndex;
Point2I maxBitmapSize;
GuiMenuBar();
bool onWake();
void onSleep();
// internal menu handling functions
// these are used by the script manipulation functions to add/remove/change menu items
void addMenu(const char *menuText, U32 menuId);
Menu *findMenu(const char *menu); // takes either a menu text or a string id
MenuItem *findMenuItem(Menu *menu, const char *menuItem); // takes either a menu text or a string id
void removeMenu(Menu *menu);
void removeMenuItem(Menu *menu, MenuItem *menuItem);
void addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup);
void clearMenuItems(Menu *menu);
void clearMenus();
// display/mouse functions
Menu *findHitMenu(Point2I mousePoint);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void checkMenuMouseMove(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onAction();
void closeMenu();
void buildAcceleratorMap();
void acceleratorKeyPress(U32 index);
void menuItemSelected(Menu *menu, MenuItem *item);
DECLARE_CONOBJECT(GuiMenuBar);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIAVIBITMAPCTRL_H_
#define _GUIAVIBITMAPCTRL_H_
#if !ENABLE_AVI_GUI || !ENABLE_MPG_GUI
class GuiAviBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
bool mDone;
public:
DECLARE_CONOBJECT(GuiAviBitmapCtrl);
GuiAviBitmapCtrl();
~GuiAviBitmapCtrl();
static void initPersistFields();
};
#endif /* No movie control */
#if ENABLE_AVI_GUI
class GuiAviBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
StringTableEntry mAviFilename;
StringTableEntry mWavFilename;
U32 mNumTextures;
TextureHandle *mTextureHandles;
U32 mWidthCount;
U32 mHeightCount;
U32 mBitmapWidth;
U32 mBitmapAlignedWidth;
U32 mBitmapHeight;
PAVIFILE mPFile;
PAVISTREAM mPAviVideo; // video stream to play
AUDIOHANDLE mWavHandle; // music to play along with it
bool mBPlaying;
bool mDone;
bool mLetterBox;
F32 mFrate;
F32 mSpeed;
S32 mTimePlayStart;
S32 mTimePlayStartPos;
S16 mPlayFPrev;
S16 mPlayFSkipped;
S32 mVidsCurrent; // attempted frame to draw
S32 mVidsPrevious; // last successfully decoded frame
S32 mVidsPrevKey, mVidsNextKey;
S32 mVidsFirst, mVidsLast;
S32 mCBVBuf;
U8 *mPVBuf;
HIC mHic;
FOURCC mFccHandler;
BITMAPINFOHEADER *mPBiSrc;
BITMAPINFOHEADER *mPBiDst;
S32 mCBuf;
U8 *mPBuf;
bool mSwapRB;
ALint mAudioLatency;
S32 fileOpen();
S32 fileClose();
S32 movieOpen();
S32 movieClose();
S32 vidsVideoOpen();
S32 vidsVideoClose();
S32 vidsVideoStart();
S32 vidsVideoStop();
S32 vidsVideoDraw();
S32 vidsTimeToSample(S32 lTime);
bool vidsIsKey(S32 frame = -1);
void vidsResetDraw() { mVidsPrevious = -1; }
bool vidsSync();
void vidsCatchup();
S32 vcmOpen(FOURCC fccHandler, BITMAPINFOHEADER *pbiSrc);
S32 vcmClose();
S32 vcmBegin();
S32 vcmEnd();
S32 vcmDrawStart();
S32 vcmDrawStop();
S32 vcmDraw(U64 dwICflags = 0);
S32 vcmDrawIn(U64 dwICflags = 0);
bool sndOpen();
void sndStart();
void sndStop();
S32 getMilliseconds();
public:
DECLARE_CONOBJECT(GuiAviBitmapCtrl);
GuiAviBitmapCtrl();
~GuiAviBitmapCtrl();
static void initPersistFields();
void setFilename(const char *filename);
S32 movieStart();
S32 movieStop();
bool onWake();
void onSleep();
void onMouseDown(const GuiEvent&);
bool onKeyDown(const GuiEvent&);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif /* ENABLE_AVI_GUI */
#if ENABLE_MPG_GUI
class GuiAviBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
protected:
StringTableEntry mAviFilename;
StringTableEntry mWavFilename;
U32 mNumTextures;
TextureHandle *mTextureHandles;
U32 mWidthCount;
U32 mHeightCount;
U32 mBitmapWidth;
U32 mBitmapAlignedWidth;
U32 mBitmapHeight;
SDL_Surface *mSurface;
U8 *mPBuf;
SDL_mutex *mDecodeLock;
ALint mAudioLatency;
SMPEG *mMPEG; // video stream to play
AUDIOHANDLE mWavHandle; // music to play along with it
bool mBPlaying;
bool mDone;
bool mLetterBox;
SMPEG_Info mInfo;
S32 fileOpen();
S32 fileClose();
S32 movieOpen();
S32 movieClose();
bool sndOpen();
void sndStart();
void sndStop();
public:
DECLARE_CONOBJECT(GuiAviBitmapCtrl);
GuiAviBitmapCtrl();
~GuiAviBitmapCtrl();
static void initPersistFields();
void setFilename(const char *filename);
S32 movieStart();
S32 movieStop();
bool onWake();
void onSleep();
void onMouseDown(const GuiEvent&);
bool onKeyDown(const GuiEvent&);
void onRender(Point2I offset, const RectI &updateRect);
};
#endif /* ENABLE_MPG_GUI */
#endif /* _GUIAVIBITMAPCTRL_H_ */

View File

@ -0,0 +1,148 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "dgl/gBitmap.h"
#include "gui/core/guiControl.h"
#include "dgl/gTexManager.h"
#include "dgl/gChunkedTexManager.h"
class GuiChunkedBitmapCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
void renderRegion(const Point2I &offset, const Point2I &extent);
protected:
StringTableEntry mBitmapName;
ChunkedTextureHandle mTexHandle;
bool mUseVariable;
bool mTile;
public:
//creation methods
DECLARE_CONOBJECT(GuiChunkedBitmapCtrl);
GuiChunkedBitmapCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
void setBitmap(const char *name);
void onRender(Point2I offset, const RectI &updateRect);
};
IMPLEMENT_CONOBJECT(GuiChunkedBitmapCtrl);
void GuiChunkedBitmapCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("Misc");
addField( "bitmap", TypeFilename, Offset( mBitmapName, GuiChunkedBitmapCtrl ) );
addField( "useVariable", TypeBool, Offset( mUseVariable, GuiChunkedBitmapCtrl ) );
addField( "tile", TypeBool, Offset( mTile, GuiChunkedBitmapCtrl ) );
endGroup("Misc");
}
ConsoleMethod( GuiChunkedBitmapCtrl, setBitmap, void, 3, 3, "(string filename)"
"Set the bitmap contained in this control.")
{
object->setBitmap( argv[2] );
}
GuiChunkedBitmapCtrl::GuiChunkedBitmapCtrl()
{
mBitmapName = StringTable->insert("");
mUseVariable = false;
mTile = false;
}
void GuiChunkedBitmapCtrl::setBitmap(const char *name)
{
bool awake = mAwake;
if(awake)
onSleep();
mBitmapName = StringTable->insert(name);
if(awake)
onWake();
setUpdate();
}
bool GuiChunkedBitmapCtrl::onWake()
{
if(!Parent::onWake())
return false;
if ( mUseVariable )
mTexHandle = ChunkedTextureHandle( getVariable() );
else
mTexHandle = ChunkedTextureHandle( mBitmapName );
return true;
}
void GuiChunkedBitmapCtrl::onSleep()
{
mTexHandle = NULL;
Parent::onSleep();
}
void GuiChunkedBitmapCtrl::renderRegion(const Point2I &offset, const Point2I &extent)
{
U32 widthCount = mTexHandle.getTextureCountWidth();
U32 heightCount = mTexHandle.getTextureCountHeight();
if(!widthCount || !heightCount)
return;
F32 widthScale = F32(extent.x) / F32(mTexHandle.getWidth());
F32 heightScale = F32(extent.y) / F32(mTexHandle.getHeight());
dglSetBitmapModulation(ColorF(1,1,1));
for(U32 i = 0; i < widthCount; i++)
{
for(U32 j = 0; j < heightCount; j++)
{
TextureHandle t = mTexHandle.getSubTexture(i, j);
RectI stretchRegion;
stretchRegion.point.x = (S32)(i * 256 * widthScale + offset.x);
stretchRegion.point.y = (S32)(j * 256 * heightScale + offset.y);
if(i == widthCount - 1)
stretchRegion.extent.x = extent.x + offset.x - stretchRegion.point.x;
else
stretchRegion.extent.x = (S32)((i * 256 + t.getWidth() ) * widthScale + offset.x - stretchRegion.point.x);
if(j == heightCount - 1)
stretchRegion.extent.y = extent.y + offset.y - stretchRegion.point.y;
else
stretchRegion.extent.y = (S32)((j * 256 + t.getHeight()) * heightScale + offset.y - stretchRegion.point.y);
dglDrawBitmapStretch(t, stretchRegion);
}
}
}
void GuiChunkedBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if(mTexHandle)
{
if (mTile)
{
int stepy = 0;
for(int y = 0; offset.y + stepy < mBounds.extent.y; stepy += mTexHandle.getHeight())
{
int stepx = 0;
for(int x = 0; offset.x + stepx < mBounds.extent.x; stepx += mTexHandle.getWidth())
renderRegion(Point2I(offset.x+stepx, offset.y+stepy), Point2I(mTexHandle.getWidth(), mTexHandle.getHeight()) );
}
}
else
renderRegion(offset, mBounds.extent);
renderChildControls(offset, updateRect);
}
else
Parent::onRender(offset, updateRect);
}

View File

@ -0,0 +1,119 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
//
// Copyright (c) 2001 GarageGames.Com
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "gui/controls/guiBitmapCtrl.h"
class GuiFadeinBitmapCtrl : public GuiBitmapCtrl
{
typedef GuiBitmapCtrl Parent;
public:
U32 wakeTime;
bool done;
U32 fadeinTime;
U32 waitTime;
U32 fadeoutTime;
GuiFadeinBitmapCtrl()
{
wakeTime = 0;
fadeinTime = 1000;
waitTime = 2000;
fadeoutTime = 1000;
done = false;
}
void onPreRender()
{
Parent::onPreRender();
setUpdate();
}
void onMouseDown(const GuiEvent &)
{
Con::executef(this, 1, "click");
}
bool onKeyDown(const GuiEvent &)
{
Con::executef(this, 1, "click");
return true;
}
DECLARE_CONOBJECT(GuiFadeinBitmapCtrl);
bool onWake()
{
if(!Parent::onWake())
return false;
wakeTime = Platform::getRealMilliseconds();
return true;
}
void onRender(Point2I offset, const RectI &updateRect)
{
Parent::onRender(offset, updateRect);
U32 elapsed = Platform::getRealMilliseconds() - wakeTime;
U32 alpha;
if (elapsed < fadeinTime)
{
// fade-in
alpha = 255 - (255 * (F32(elapsed) / F32(fadeinTime)));
}
else if (elapsed < (fadeinTime+waitTime))
{
// wait
alpha = 0;
}
else if (elapsed < (fadeinTime+waitTime+fadeoutTime))
{
// fade out
elapsed -= (fadeinTime+waitTime);
alpha = 255 * F32(elapsed) / F32(fadeoutTime);
}
else
{
// done state
alpha = fadeoutTime ? 255 : 0;
done = true;
}
ColorI color(0,0,0,alpha);
dglDrawRectFill(offset, mBounds.extent + offset, color);
}
static void initPersistFields()
{
Parent::initPersistFields();
addField("fadeinTime", TypeS32, Offset(fadeinTime, GuiFadeinBitmapCtrl));
addField("waitTime", TypeS32, Offset(waitTime, GuiFadeinBitmapCtrl));
addField("fadeoutTime", TypeS32, Offset(fadeoutTime, GuiFadeinBitmapCtrl));
addField("done", TypeBool, Offset(done, GuiFadeinBitmapCtrl));
}
};
IMPLEMENT_CONOBJECT(GuiFadeinBitmapCtrl);

View File

@ -0,0 +1,817 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/game/guiMessageVectorCtrl.h"
#include "gui/utility/messageVector.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "gui/containers/guiScrollCtrl.h"
IMPLEMENT_CONOBJECT(GuiMessageVectorCtrl);
//-------------------------------------- Console functions
ConsoleMethod(GuiMessageVectorCtrl, attach, bool, 3, 3, "(MessageVector item)"
"Make this gui control display messages from the specified MessageVector")
{
MessageVector* pMV = NULL;
Sim::findObject(argv[2], pMV);
if (pMV == NULL) {
Con::errorf(ConsoleLogEntry::General, "Could not find MessageVector: %s", argv[2]);
return false;
}
return object->attach(pMV);
}
ConsoleMethod(GuiMessageVectorCtrl, detach, void, 2, 2, "()"
"Stop listing messages from the MessageVector previously attached to, if any.")
{
if (object->isAttached() == false) {
Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl: double detach");
return;
}
object->detach();
}
struct TempLineBreak
{
S32 start;
S32 end;
};
//--------------------------------------------------------------------------
// Callback for messageVector
void sMVCtrlCallback(void * spectatorKey,
const MessageVector::MessageCode code,
const U32 argument)
{
GuiMessageVectorCtrl* pMVC = reinterpret_cast<GuiMessageVectorCtrl*>(spectatorKey);
pMVC->callbackRouter(code, argument);
}
//--------------------------------------------------------------------------
GuiMessageVectorCtrl::GuiMessageVectorCtrl()
{
VECTOR_SET_ASSOCIATION(mLineWrappings);
VECTOR_SET_ASSOCIATION(mSpecialMarkers);
VECTOR_SET_ASSOCIATION(mLineElements);
mMessageVector = NULL;
mLineSpacingPixels = 0;
mLineContinuationIndent = 10;
mMouseDown = false;
mMouseSpecialLine = -1;
mMouseSpecialRef = -1;
for (U32 i = 0; i < 16; i++)
mAllowedMatches[i] = "";
mSpecialColor.set(0, 0, 255);
mMaxColorIndex = 9;
}
//--------------------------------------------------------------------------
GuiMessageVectorCtrl::~GuiMessageVectorCtrl()
{
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
AssertFatal(mSpecialMarkers.size() == 0, "Error, special markers not properly cleared!");
AssertFatal(mLineElements.size() == 0, "Error, line elements not properly cleared!");
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("lineSpacing", TypeS32, Offset(mLineSpacingPixels, GuiMessageVectorCtrl));
addField("lineContinuedIndex", TypeS32, Offset(mLineContinuationIndent, GuiMessageVectorCtrl));
addField("allowedMatches", TypeString, Offset(mAllowedMatches, GuiMessageVectorCtrl), 16);
addField("matchColor", TypeColorI, Offset(mSpecialColor, GuiMessageVectorCtrl));
addField("maxColorIndex", TypeS32, Offset(mMaxColorIndex, GuiMessageVectorCtrl));
}
bool GuiMessageVectorCtrl::onAdd()
{
return Parent::onAdd();
}
void GuiMessageVectorCtrl::onRemove()
{
Parent::onRemove();
}
//--------------------------------------------------------------------------
bool GuiMessageVectorCtrl::isAttached() const
{
return (mMessageVector != NULL);
}
//--------------------------------------------------------------------------
bool GuiMessageVectorCtrl::attach(MessageVector* newAttachment)
{
AssertFatal(newAttachment, "No attachment!");
if (newAttachment == NULL || !isAwake())
return false;
if (isAttached()) {
Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::attach: overriding attachment");
detach();
}
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
mMessageVector = newAttachment;
mMessageVector->registerSpectator(sMVCtrlCallback, this);
return true;
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::detach()
{
if (isAttached() == false) {
Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::detach: not attached!");
return;
}
mMessageVector->unregisterSpectator(this);
mMessageVector = NULL;
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::lineInserted(const U32 arg)
{
AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
GuiScrollCtrl* pScroll = dynamic_cast<GuiScrollCtrl*>(getParent());
bool fullyScrolled = pScroll->isScrolledToBottom();
mSpecialMarkers.insert(arg);
createSpecialMarkers(mSpecialMarkers[arg], mMessageVector->getLine(arg).message);
mLineWrappings.insert(arg);
createLineWrapping(mLineWrappings[arg], mMessageVector->getLine(arg).message);
mLineElements.insert(arg);
createLineElement(mLineElements[arg], mLineWrappings[arg], mSpecialMarkers[arg]);
U32 numLines = 0;
for (U32 i = 0; i < mLineWrappings.size(); i++) {
// We need to rebuild the physicalLineStart markers at the same time as
// we find out how many of them are left...
mLineElements[i].physicalLineStart = numLines;
numLines += mLineWrappings[i].numLines;
}
U32 newHeight = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1));
resize(mBounds.point, Point2I(mBounds.extent.x, newHeight));
if(fullyScrolled)
pScroll->scrollTo(0, 0x7FFFFFFF);
}
void GuiMessageVectorCtrl::lineDeleted(const U32 arg)
{
AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
AssertFatal(arg < mLineWrappings.size(), "Error, out of bounds line deleted!");
// It's a somewhat involved process to delete the lineelements...
LineElement& rElement = mLineElements[arg];
TextElement* walk = rElement.headLineElements;
while (walk != NULL) {
TextElement* lineWalk = walk->nextInLine;
while (lineWalk != NULL) {
TextElement* temp = lineWalk;
lineWalk = lineWalk->nextPhysicalLine;
delete temp;
}
TextElement* temp = walk;
walk = walk->nextPhysicalLine;
delete temp;
}
rElement.headLineElements = NULL;
mLineElements.erase(arg);
delete [] mLineWrappings[arg].startEndPairs;
mLineWrappings.erase(arg);
delete [] mSpecialMarkers[arg].specials;
mSpecialMarkers.erase(arg);
U32 numLines = 0;
for (U32 i = 0; i < mLineWrappings.size(); i++) {
// We need to rebuild the physicalLineStart markers at the same time as
// we find out how many of them are left...
mLineElements[i].physicalLineStart = numLines;
numLines += mLineWrappings[i].numLines;
}
U32 newHeight = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1));
resize(mBounds.point, Point2I(mBounds.extent.x, newHeight));
}
void GuiMessageVectorCtrl::vectorDeleted()
{
AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!");
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared out!");
mMessageVector = NULL;
U32 newHeight = mProfile->mFont->getHeight() + mLineSpacingPixels;
resize(mBounds.point, Point2I(mBounds.extent.x, newHeight));
}
void GuiMessageVectorCtrl::callbackRouter(const MessageVector::MessageCode code,
const U32 arg)
{
switch (code) {
case MessageVector::LineInserted:
lineInserted(arg);
break;
case MessageVector::LineDeleted:
lineDeleted(arg);
break;
case MessageVector::VectorDeletion:
vectorDeleted();
break;
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::createSpecialMarkers(SpecialMarkers& rSpecial, const char* string)
{
// The first thing we need to do is create a version of the string with no uppercase
// chars for matching...
char* pLCCopy = new char[dStrlen(string) + 1];
for (U32 i = 0; string[i] != '\0'; i++)
pLCCopy[i] = dTolower(string[i]);
pLCCopy[dStrlen(string)] = '\0';
Vector<TempLineBreak> tempSpecials(__FILE__, __LINE__);
Vector<S32> tempTypes(__FILE__, __LINE__);
const char* pCurr = pLCCopy;
while (pCurr[0] != '\0') {
const char* pMinMatch = &pLCCopy[dStrlen(string)];
U32 minMatchType = 0xFFFFFFFF;
AssertFatal(pMinMatch[0] == '\0', "Error, bad positioning of sentry...");
for (U32 i = 0; i < 16; i++) {
if (mAllowedMatches[i][0] == '\0')
continue;
// Not the most efficient...
char matchBuffer[512];
dStrncpy(matchBuffer, mAllowedMatches[i], 500);
matchBuffer[499] = '\0';
dStrcat(matchBuffer, "://");
const char* pMatch = dStrstr(pCurr, (const char*)matchBuffer);
if (pMatch != NULL && pMatch < pMinMatch) {
pMinMatch = pMatch;
minMatchType = i;
}
}
if (pMinMatch[0] != '\0') {
AssertFatal(minMatchType != 0xFFFFFFFF, "Hm, that's bad");
// Found a match...
U32 start = pMinMatch - pLCCopy;
U32 j;
for (j = 0; pLCCopy[start + j] != '\0'; j++) {
if (pLCCopy[start + j] == '\n' ||
pLCCopy[start + j] == ' ' ||
pLCCopy[start + j] == '\t')
break;
}
AssertFatal(j > 0, "Error, j must be > 0 at this point!");
U32 end = start + j - 1;
tempSpecials.increment();
tempSpecials.last().start = start;
tempSpecials.last().end = end;
tempTypes.push_back(minMatchType);
pCurr = &pLCCopy[end + 1];
} else {
// No match. This will cause the while loop to terminate...
pCurr = pMinMatch;
}
}
if ((rSpecial.numSpecials = tempSpecials.size()) != 0) {
rSpecial.specials = new SpecialMarkers::Special[tempSpecials.size()];
for (U32 i = 0; i < tempSpecials.size(); i++) {
rSpecial.specials[i].start = tempSpecials[i].start;
rSpecial.specials[i].end = tempSpecials[i].end;
rSpecial.specials[i].specialType = tempTypes[i];
}
} else {
rSpecial.specials = NULL;
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::createLineWrapping(LineWrapping& rWrapping, const char* string)
{
Vector<TempLineBreak> tempBreaks(__FILE__, __LINE__);
U32 i;
U32 currStart = 0;
if (dStrlen(string) != 0) {
for (i = 0; i < dStrlen(string); i++) {
if (string[i] == '\n') {
tempBreaks.increment();
tempBreaks.last().start = currStart;
tempBreaks.last().end = i-1;
currStart = i+1;
} else if (i == dStrlen(string) - 1) {
tempBreaks.increment();
tempBreaks.last().start = currStart;
tempBreaks.last().end = i;
currStart = i+1;
}
}
} else {
tempBreaks.increment();
tempBreaks.last().start = 0;
tempBreaks.last().end = -1;
}
bool used = false;
U32 splitWidth = mBounds.extent.x;
U32 currLine = 0;
while (currLine < tempBreaks.size()) {
TempLineBreak& rLine = tempBreaks[currLine];
if (rLine.start >= rLine.end) {
if (currLine == 0)
splitWidth -= mLineContinuationIndent;
currLine++;
continue;
}
// Ok, there's some actual text in this line. How long is it?
U32 baseLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], rLine.end-rLine.start+1);
if (baseLength > splitWidth) {
// DMMNOTE: Replace with bin search eventually
U32 currPos;
U32 breakPos;
for (currPos = 0; currPos < rLine.end-rLine.start+1; currPos++) {
U32 currLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], currPos+1);
if (currLength > splitWidth) {
// Make sure that the currPos has advanced, then set the breakPoint.
breakPos = currPos != 0 ? currPos - 1 : 0;
break;
}
}
if (currPos == rLine.end-rLine.start+1) {
AssertFatal(false, "Error, if the line must be broken, the position must be before this point!");
currLine++;
continue;
}
// Ok, the character at breakPos is the last valid char we can render. We
// want to scan back to the first whitespace character (which, in the bounds
// of the line, is guaranteed to be a space or a tab).
U32 originalBreak = breakPos;
while (true) {
if (string[rLine.start + breakPos] == ' ' || string[rLine.start + breakPos] == '\t') {
break;
} else {
AssertFatal(string[rLine.start + breakPos] != '\n',
"Bad characters in line range...");
if (breakPos == 0) {
breakPos = originalBreak;
break;
}
breakPos--;
}
}
// Ok, everything up to and including breakPos is in the currentLine. Insert
// a new line at this point, and put everything after in that line.
S32 oldStart = rLine.start;
S32 oldEnd = rLine.end;
rLine.end = rLine.start + breakPos;
// Note that rLine is NOTNOTNOTNOT valid after this point!
tempBreaks.insert(currLine+1);
tempBreaks[currLine+1].start = oldStart + breakPos + 1;
tempBreaks[currLine+1].end = oldEnd;
}
if (currLine == 0)
splitWidth -= mLineContinuationIndent;
currLine++;
}
rWrapping.numLines = tempBreaks.size();
rWrapping.startEndPairs = new LineWrapping::SEPair[tempBreaks.size()];
for (i = 0; i < tempBreaks.size(); i++) {
rWrapping.startEndPairs[i].start = tempBreaks[i].start;
rWrapping.startEndPairs[i].end = tempBreaks[i].end;
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::createLineElement(LineElement& rElement,
LineWrapping& rWrapping,
SpecialMarkers& rSpecial)
{
// First, do a straighforward translation of the wrapping...
TextElement** ppWalk = &rElement.headLineElements;
for (U32 i = 0; i < rWrapping.numLines; i++) {
*ppWalk = new TextElement;
(*ppWalk)->nextInLine = NULL;
(*ppWalk)->nextPhysicalLine = NULL;
(*ppWalk)->specialReference = -1;
(*ppWalk)->start = rWrapping.startEndPairs[i].start;
(*ppWalk)->end = rWrapping.startEndPairs[i].end;
ppWalk = &((*ppWalk)->nextPhysicalLine);
}
if (rSpecial.numSpecials != 0) {
// Ok. Now, walk down the lines, and split the elements into their contiuent parts...
//
TextElement* walk = rElement.headLineElements;
while (walk) {
TextElement* walkAcross = walk;
while (walkAcross) {
S32 specialMatch = -1;
for (U32 i = 0; i < rSpecial.numSpecials; i++) {
if (walkAcross->start <= rSpecial.specials[i].end &&
walkAcross->end >= rSpecial.specials[i].start) {
specialMatch = i;
break;
}
}
if (specialMatch != -1) {
// We have a match here. Break it into the appropriate number of segments.
// this will vary depending on how the overlap is occuring...
if (walkAcross->start >= rSpecial.specials[specialMatch].start) {
if (walkAcross->end <= rSpecial.specials[specialMatch].end) {
// The whole thing is part of the special
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->specialReference = specialMatch;
} else {
// The first part is in the special, the tail is out
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->specialReference = -1;
walkAcross->specialReference = specialMatch;
walkAcross->nextInLine->end = walkAcross->end;
walkAcross->end = rSpecial.specials[specialMatch].end;
walkAcross->nextInLine->start = rSpecial.specials[specialMatch].end + 1;
AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!");
}
walkAcross = walkAcross->nextInLine;
} else {
if (walkAcross->end <= rSpecial.specials[specialMatch].end) {
// The first part is out of the special, the second part in.
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->specialReference = specialMatch;
walkAcross->specialReference = -1;
walkAcross->nextInLine->end = walkAcross->end;
walkAcross->end = rSpecial.specials[specialMatch].start - 1;
walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start;
AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!");
walkAcross = walkAcross->nextInLine;
} else {
// First out, middle in, last out. Oy.
AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!");
walkAcross->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->specialReference = specialMatch;
walkAcross->nextInLine->nextInLine = new TextElement;
walkAcross->nextInLine->nextInLine->nextInLine = NULL;
walkAcross->nextInLine->nextInLine->nextPhysicalLine = NULL;
walkAcross->nextInLine->nextInLine->specialReference = -1;
walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start;
walkAcross->nextInLine->end = rSpecial.specials[specialMatch].end;
walkAcross->nextInLine->nextInLine->start = rSpecial.specials[specialMatch].end+1;
walkAcross->nextInLine->nextInLine->end = walkAcross->end;
walkAcross->end = walkAcross->nextInLine->start - 1;
AssertFatal((walkAcross->end >= walkAcross->start &&
walkAcross->nextInLine->end >= walkAcross->nextInLine->start &&
walkAcross->nextInLine->nextInLine->end >= walkAcross->nextInLine->nextInLine->start),
"Bad textelements generated!");
walkAcross = walkAcross->nextInLine->nextInLine;
}
}
} else {
walkAcross = walkAcross->nextInLine;
}
}
walk = walk->nextPhysicalLine;
}
}
}
//--------------------------------------------------------------------------
bool GuiMessageVectorCtrl::onWake()
{
if (Parent::onWake() == false)
return false;
if (bool(mProfile->mFont) == false)
return false;
mMinSensibleWidth = 1;
for (U32 i = 0; i < 256; i++) {
if (mProfile->mFont->isValidChar(U8(i))) {
if (mProfile->mFont->getCharWidth(U8(i)) > mMinSensibleWidth)
mMinSensibleWidth = mProfile->mFont->getCharWidth(U8(i));
}
}
AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!");
return true;
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::onSleep()
{
if (isAttached())
detach();
Parent::onSleep();
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::onRender(Point2I offset,
const RectI& updateRect)
{
Parent::onRender(offset, updateRect);
if (isAttached()) {
U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels;
U32 currLine = 0;
for (U32 i = 0; i < mMessageVector->getNumLines(); i++) {
TextElement* pElement = mLineElements[i].headLineElements;
ColorI lastColor = mProfile->mFontColor;
while (pElement != NULL) {
Point2I localStart(pElement == mLineElements[i].headLineElements ? 0 : mLineContinuationIndent, currLine * linePixels);
Point2I globalCheck = localToGlobalCoord(localStart);
U32 globalRangeStart = globalCheck.y;
U32 globalRangeEnd = globalCheck.y + mProfile->mFont->getHeight();
if (globalRangeStart > updateRect.point.y + updateRect.extent.y ||
globalRangeEnd < updateRect.point.y) {
currLine++;
pElement = pElement->nextPhysicalLine;
continue;
}
TextElement* walkAcross = pElement;
while (walkAcross) {
if (walkAcross->start > walkAcross->end)
break;
Point2I globalStart = localToGlobalCoord(localStart);
U32 strWidth;
if (walkAcross->specialReference == -1) {
dglSetBitmapModulation(lastColor);
dglSetTextAnchorColor(mProfile->mFontColor);
strWidth = dglDrawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start],
walkAcross->end - walkAcross->start + 1, mProfile->mFontColors, mMaxColorIndex);
dglGetBitmapModulation(&lastColor);
} else {
dglGetBitmapModulation(&lastColor);
dglSetBitmapModulation(mSpecialColor);
dglSetTextAnchorColor(mProfile->mFontColor);
strWidth = dglDrawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start],
walkAcross->end - walkAcross->start + 1);
// in case we have 2 in a row...
dglSetBitmapModulation(lastColor);
}
if (walkAcross->specialReference != -1) {
Point2I lineStart = localStart;
Point2I lineEnd = localStart;
lineStart.y += mProfile->mFont->getBaseline() + 1;
lineEnd.x += strWidth;
lineEnd.y += mProfile->mFont->getBaseline() + 1;
dglDrawLine(localToGlobalCoord(lineStart),
localToGlobalCoord(lineEnd),
mSpecialColor);
}
localStart.x += strWidth;
walkAcross = walkAcross->nextInLine;
}
currLine++;
pElement = pElement->nextPhysicalLine;
}
}
dglClearBitmapModulation();
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::inspectPostApply()
{
Parent::inspectPostApply();
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::parentResized(const Point2I& oldSize,
const Point2I& newSize)
{
Parent::parentResized(oldSize, newSize);
// If we have a MesssageVector, detach/reattach so we can reflow the text.
if (mMessageVector)
{
MessageVector *reflowme = mMessageVector;
detach();
attach(reflowme);
}
}
//--------------------------------------------------------------------------
void GuiMessageVectorCtrl::findSpecialFromCoord(const Point2I& point, S32* specialLine, S32* specialRef)
{
if (mLineElements.size() == 0) {
*specialLine = -1;
*specialRef = -1;
return;
}
U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels;
if ((point.x < 0 || point.x >= mBounds.extent.x) ||
(point.y < 0 || point.y >= mBounds.extent.y)) {
*specialLine = -1;
*specialRef = -1;
return;
}
// Ok, we have real work to do here. Let's determine the physical line that it's on...
U32 physLine = point.y / linePixels;
AssertFatal(physLine >= 0, "Bad physical line!");
// And now we find the LineElement that contains that physicalLine...
U32 elemIndex;
for (elemIndex = 0; elemIndex < mLineElements.size(); elemIndex++) {
if (mLineElements[elemIndex].physicalLineStart > physLine) {
// We've passed it.
AssertFatal(elemIndex != 0, "Error, bad elemIndex, check assumptions.");
elemIndex--;
break;
}
}
if (elemIndex == mLineElements.size()) {
// On the last line...
elemIndex = mLineElements.size() - 1;
}
TextElement* line = mLineElements[elemIndex].headLineElements;
for (U32 i = 0; i < physLine - mLineElements[elemIndex].physicalLineStart; i++)
{
if (line->nextPhysicalLine == NULL)
{
*specialLine = -1;
*specialRef = -1;
return;
}
line = line->nextPhysicalLine;
AssertFatal(line != NULL, "Error, moved too far!");
}
// Ok, line represents the current line. We now need to find out which textelement
// the points x coord falls in.
U32 currX = 0;
if ((physLine - mLineElements[elemIndex].physicalLineStart) != 0) {
currX = mLineContinuationIndent;
// First, if this isn't the first line in this wrapping, we have to make sure
// that the coord isn't in the margin...
if (point.x < mLineContinuationIndent) {
*specialLine = -1;
*specialRef = -1;
return;
}
}
if (line->start > line->end) {
// Empty line special case...
*specialLine = -1;
*specialRef = -1;
return;
}
U32 currElem = 0;
while (line) {
U32 newX = currX + mProfile->mFont->getStrNWidth((const UTF8 *)mMessageVector->getLine(elemIndex).message,
line->end - line->start + 1);
if (point.x < newX) {
// That's the one!
*specialLine = elemIndex;
*specialRef = line->specialReference;
return;
}
currX = newX;
line = line->nextInLine;
}
// Off to the right. Aw...
*specialLine = -1;
*specialRef = -1;
}
void GuiMessageVectorCtrl::onMouseDown(const GuiEvent& event)
{
Parent::onMouseDown(event);
mouseUnlock();
mMouseDown = true;
// Find the special we are in, if any...
findSpecialFromCoord(globalToLocalCoord(event.mousePoint),
&mMouseSpecialLine, &mMouseSpecialRef);
}
void GuiMessageVectorCtrl::onMouseUp(const GuiEvent& event)
{
Parent::onMouseUp(event);
mouseUnlock();
// Is this an up from a dragged click?
if (mMouseDown == false)
return;
// Find the special we are in, if any...
S32 currSpecialLine;
S32 currSpecialRef;
findSpecialFromCoord(globalToLocalCoord(event.mousePoint), &currSpecialLine, &currSpecialRef);
if (currSpecialRef != -1 &&
(currSpecialLine == mMouseSpecialLine &&
currSpecialRef == mMouseSpecialRef)) {
// Execute the callback
SpecialMarkers& rSpecial = mSpecialMarkers[currSpecialLine];
S32 specialStart = rSpecial.specials[currSpecialRef].start;
S32 specialEnd = rSpecial.specials[currSpecialRef].end;
char* copyURL = new char[specialEnd - specialStart + 2];
dStrncpy(copyURL, &mMessageVector->getLine(currSpecialLine).message[specialStart], specialEnd - specialStart + 1);
copyURL[specialEnd - specialStart + 1] = '\0';
Con::executef(this, 2, "urlClickCallback", copyURL);
delete [] copyURL;
}
mMouseDown = false;
mMouseSpecialLine = -1;
mMouseSpecialRef = -1;
}

View File

@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIMESSAGEVECTORCTRL_H_
#define _GUIMESSAGEVECTORCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _MESSAGEVECTOR_H_
#include "gui/utility/messageVector.h"
#endif
/// Render a MessageVector (chat HUD)
///
/// This renders messages from a MessageVector; the important thing
/// here is that the MessageVector holds all the messages we care
/// about, while we can destroy and create these GUI controls as
/// needed. (For instance, Tribes 2 used seperate GuiMessageVectorCtrl
/// controls in the different HUD modes.)
class GuiMessageVectorCtrl : public GuiControl
{
typedef GuiControl Parent;
//-------------------------------------- Public interfaces...
public:
struct SpecialMarkers {
struct Special {
S32 specialType;
S32 start;
S32 end;
};
U32 numSpecials;
Special* specials;
};
GuiMessageVectorCtrl();
~GuiMessageVectorCtrl();
bool isAttached() const;
bool attach(MessageVector*);
void detach();
// Gui control overrides
protected:
bool onAdd();
void onRemove();
bool onWake();
void onSleep();
void onRender(Point2I offset, const RectI &updateRect);
void inspectPostApply();
void parentResized(const Point2I &oldParentExtent, const Point2I &newParentExtent);
void onMouseUp(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
// void onMouseMove(const GuiEvent &event);
// Overrideables
protected:
virtual void lineInserted(const U32);
virtual void lineDeleted(const U32);
virtual void vectorDeleted();
MessageVector* mMessageVector;
// Font resource
protected:
U32 mMinSensibleWidth;
U32 mLineSpacingPixels;
U32 mLineContinuationIndent;
StringTableEntry mAllowedMatches[16];
ColorI mSpecialColor;
U32 mMaxColorIndex;
bool mMouseDown;
S32 mMouseSpecialLine;
S32 mMouseSpecialRef;
/// Derived classes must keep this set of variables consistent if they
/// use this classes onRender. Note that this has the fairly large
/// disadvantage of requiring lots of ugly, tiny mem allocs. A rewrite
/// to a more memory friendly version might be a good idea...
struct LineWrapping {
struct SEPair {
S32 start;
S32 end;
};
U32 numLines;
SEPair* startEndPairs; /// start[linex] = startEndPairs[linex*2+0]
/// end[linex] = startEndPairs[linex*2+1]
/// if end < start, line is empty...
};
struct TextElement {
TextElement* nextInLine;
TextElement* nextPhysicalLine;
S32 specialReference; /// if (!= -1), indicates a special reference
S32 start;
S32 end;
};
struct LineElement {
U32 physicalLineStart;
TextElement* headLineElements;
};
Vector<LineWrapping> mLineWrappings;
Vector<SpecialMarkers> mSpecialMarkers;
Vector<LineElement> mLineElements;
void createLineWrapping(LineWrapping&, const char*);
void createSpecialMarkers(SpecialMarkers&, const char*);
void createLineElement(LineElement&, LineWrapping&, SpecialMarkers&);
void findSpecialFromCoord(const Point2I&, S32*, S32*);
public:
void callbackRouter(const MessageVector::MessageCode, const U32);
public:
DECLARE_CONOBJECT(GuiMessageVectorCtrl);
static void initPersistFields();
};
#endif // _H_GUIMESSAGEVECTORCTRL_

View File

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "gui/game/guiProgressCtrl.h"
IMPLEMENT_CONOBJECT(GuiProgressCtrl);
GuiProgressCtrl::GuiProgressCtrl()
{
mProgress = 0.0f;
}
const char* GuiProgressCtrl::getScriptValue()
{
char * ret = Con::getReturnBuffer(64);
dSprintf(ret, 64, "%g", mProgress);
return ret;
}
void GuiProgressCtrl::setScriptValue(const char *value)
{
//set the value
if (! value)
mProgress = 0.0f;
else
mProgress = dAtof(value);
//validate the value
mProgress = mClampF(mProgress, 0.f, 1.f);
setUpdate();
}
void GuiProgressCtrl::onPreRender()
{
const char * var = getVariable();
if(var)
{
F32 value = mClampF(dAtof(var), 0.f, 1.f);
if(value != mProgress)
{
mProgress = value;
setUpdate();
}
}
}
void GuiProgressCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI ctrlRect(offset, mBounds.extent);
//draw the progress
S32 width = (S32)((F32)mBounds.extent.x * mProgress);
if (width > 0)
{
RectI progressRect = ctrlRect;
progressRect.extent.x = width;
dglDrawRectFill(progressRect, mProfile->mFillColor);
}
//now draw the border
if (mProfile->mBorder)
{
dglDrawRect(ctrlRect, mProfile->mBorderColor);
RectI copyRect = ctrlRect;
copyRect.point.x += 1;
copyRect.point.y += 1;
copyRect.extent.x -= 2;
copyRect.extent.y -= 2;
ColorI color = mProfile->mBorderColor*0.75;
color.alpha = 255;
dglDrawRect(copyRect, color);
}
//render the children
renderChildControls(offset, updateRect);
}

View File

@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _GUIPROGRESSCTRL_H_
#define _GUIPROGRESSCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiProgressCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
F32 mProgress;
public:
//creation methods
DECLARE_CONOBJECT(GuiProgressCtrl);
GuiProgressCtrl();
//console related methods
virtual const char *getScriptValue();
virtual void setScriptValue(const char *value);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif

View File

@ -0,0 +1,192 @@
#include "gui/shiny/guiEffectCanvas.h"
#include "dgl/dgl.h"
#include "util/safeDelete.h"
IMPLEMENT_CONOBJECT(GuiEffectCanvas);
//------------------------------------------------------------------------------
// An effect
namespace EffectCanvasEffects
{
void FN_CDECL spiralInitFn( const int x, const int y, const Point2I &resolution,
const F32 maxX, const F32 maxY, Point2F *outVec )
{
outVec->x += 0.01;
//outVec->x -= 0.5f;
//outVec->y -= 0.5f;
//F32 d = mSqrt( ( x - resolution.x / 2.f ) * ( x - resolution.x / 2.f ) +
// ( y - resolution.y / 2.f ) * ( y - resolution.y / 2.f ) );
//F32 c = mCos( d * M_PI / 64.f ) * 0.5f;
//F32 s = mSin( d * M_PI / 64.f ) * 0.5f;
//outVec->x = c * outVec->x - s * outVec->y;
//outVec->y = s * outVec->x + c * outVec->y;
//outVec->x += 0.5f;
//outVec->y += 0.5f;
}
};
//------------------------------------------------------------------------------
ConsoleFunction( createEffectCanvas, bool, 2, 2, "(string windowTitle)"
"Create the game window/canvas, with the specified window title.")
{
AssertISV(!Canvas, "createEffectCanvas: canvas has already been instantiated");
#if !defined(TORQUE_OS_MAC) // macs can only run one instance in general.
#if !defined(TORQUE_DEBUG) && !defined(INTERNAL_RELEASE)
if(!Platform::excludeOtherInstances("TorqueTest"))
return false;
#endif
#endif
Platform::initWindow(Point2I(800, 600), argv[1]);
// create the canvas, and add it to the manager
Canvas = new GuiEffectCanvas();
Canvas->registerObject("Canvas"); // automatically adds to GuiGroup
return true;
}
//------------------------------------------------------------------------------
GuiEffectCanvas::GuiEffectCanvas()
{
mStartEffect = false;
mEffectInProgress = false;
mVisualizeField = false;
mUpdateFeedbackTexture = false;
mVectorField = new VectorField( Point2I( 16, 12 ) );
mClearColor.set( 1.f, 0.f, 0.f, 1.0f );
mLastSize = mBounds.extent;
}
//------------------------------------------------------------------------------
GuiEffectCanvas::~GuiEffectCanvas()
{
SAFE_DELETE( mVectorField );
}
//------------------------------------------------------------------------------
void GuiEffectCanvas::renderFrame( bool preRenderOnly, bool bufferSwap /* = true */ )
{
// With this canvas, always re-draw the whole thing if an effect is in progress
if( mEffectInProgress )
{
resetUpdateRegions();
}
// Render normally.
Parent::renderFrame( preRenderOnly, false );
// Check for resize (renderFrame does this)
if( Platform::getWindowSize() != mLastSize )
{
canvasResized();
mLastSize = Platform::getWindowSize();
}
// Check to see if the effect should be started
if( mStartEffect )
{
// Sweet, don't do this again until next time
mStartEffect = false;
// Grab the frame
mFeedbackTexture.update();
// And we are in business!
mEffectInProgress = true;
}
// Check to see if we are going
if( mEffectInProgress )
{
// Do some vooodooo!
glDisable( GL_LIGHTING );
glEnable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
// Bind the feedback texture
glBindTexture( GL_TEXTURE_2D, mFeedbackTexture.getTextureHandle().getGLName() );
// Set up some tex parameters
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (F32 *)&mClearColor );
// Set up the blend parameters
glBlendColorEXT( mClearColor.red, mClearColor.green, mClearColor.blue, mClearColor.alpha );
glBlendFunc( GL_CONSTANT_COLOR_EXT, GL_ZERO );
glGetFloatv( GL_BLEND_COLOR_EXT, (F32 *)&mClearColor );
// Render the current field
mVectorField->renderField( true );
glDisable( GL_TEXTURE_2D );
}
// Check to see if we should update the feedback texture
if( mUpdateFeedbackTexture )
{
mFeedbackTexture.update();
mUpdateFeedbackTexture = false;
}
// Debug visualization
if( mVisualizeField )
mVectorField->visualizeField();
// Now swap the buffers
swapBuffers();
}
//------------------------------------------------------------------------------
void GuiEffectCanvas::processTick()
{
if( mEffectInProgress )
{
// Check to see if we are done
if( mEffectTickCount++ > 100 )
mEffectInProgress = false;
else
mUpdateFeedbackTexture = true;
}
}
//------------------------------------------------------------------------------
void GuiEffectCanvas::canvasResized()
{
// Do NOT call parent here
mFeedbackTexture.setUpdateRect( mBounds );
TextureObject *obj = (TextureObject *)mFeedbackTexture.getTextureHandle();
F32 maxX = F32(obj->bitmapWidth) / F32(obj->texWidth);
F32 maxY = F32(obj->bitmapHeight) / F32(obj->texHeight);
mVectorField->initVectorField( maxX, maxY, VectorField::Flip_None, &EffectCanvasEffects::spiralInitFn );
}
//------------------------------------------------------------------------------
void GuiEffectCanvas::setContentControl(GuiControl *gui)
{
Parent::setContentControl( gui );
//mStartEffect = true;
mEffectTickCount = 0;
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,49 @@
#ifndef _GUIEFFECTCANVAS_H_
#define _GUIEFFECTCANVAS_H_
#include "gui/core/guiCanvas.h"
#include "core/iTickable.h"
#include "dgl/gVectorField.h"
#include "dgl/gDynamicTexture.h"
class GuiEffectCanvas : public GuiCanvas, public virtual ITickable
{
typedef GuiCanvas Parent;
protected:
bool mEffectInProgress;
bool mVisualizeField;
bool mUpdateFeedbackTexture;
bool mStartEffect;
U32 mEffectTickCount;
VectorField *mVectorField;
DynamicTexture mFeedbackTexture;
ColorF mClearColor;
Point2I mLastSize;
public:
DECLARE_CONOBJECT(GuiEffectCanvas);
GuiEffectCanvas();
~GuiEffectCanvas();
// Change rendering to support the effects
virtual void renderFrame( bool preRenderOnly, bool bufferSwap = true );
// To adjust the vector field
virtual void canvasResized();
// This will start the effect!
virtual void setContentControl(GuiControl *gui);
// ITickable stuff
virtual void processTick();
virtual void interpolateTick( F32 delta ) {};
virtual void advanceTime( F32 timeDelta ) {};
};
#endif

Some files were not shown because too many files have changed in this diff Show More