Initial commit
This commit is contained in:
12
engine/gui/README.txt
Executable file
12
engine/gui/README.txt
Executable 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.
|
||||
91
engine/gui/containers/guiCtrlArrayCtrl.cc
Executable file
91
engine/gui/containers/guiCtrlArrayCtrl.cc
Executable 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
engine/gui/containers/guiCtrlArrayCtrl.h
Executable file
36
engine/gui/containers/guiCtrlArrayCtrl.h
Executable 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
|
||||
1005
engine/gui/containers/guiFrameCtrl.cc
Executable file
1005
engine/gui/containers/guiFrameCtrl.cc
Executable file
File diff suppressed because it is too large
Load Diff
176
engine/gui/containers/guiFrameCtrl.h
Executable file
176
engine/gui/containers/guiFrameCtrl.h
Executable 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
|
||||
310
engine/gui/containers/guiPaneCtrl.cc
Executable file
310
engine/gui/containers/guiPaneCtrl.cc
Executable 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);
|
||||
}
|
||||
65
engine/gui/containers/guiPaneCtrl.h
Executable file
65
engine/gui/containers/guiPaneCtrl.h
Executable 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
|
||||
887
engine/gui/containers/guiScrollCtrl.cc
Executable file
887
engine/gui/containers/guiScrollCtrl.cc
Executable 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);
|
||||
}
|
||||
201
engine/gui/containers/guiScrollCtrl.h
Executable file
201
engine/gui/containers/guiScrollCtrl.h
Executable 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
|
||||
165
engine/gui/containers/guiStackCtrl.cc
Executable file
165
engine/gui/containers/guiStackCtrl.cc
Executable 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();
|
||||
}
|
||||
49
engine/gui/containers/guiStackCtrl.h
Executable file
49
engine/gui/containers/guiStackCtrl.h
Executable 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
|
||||
638
engine/gui/containers/guiTabBookCtrl.cc
Executable file
638
engine/gui/containers/guiTabBookCtrl.cc
Executable 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();
|
||||
|
||||
}
|
||||
|
||||
198
engine/gui/containers/guiTabBookCtrl.h
Executable file
198
engine/gui/containers/guiTabBookCtrl.h
Executable 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_
|
||||
743
engine/gui/containers/guiWindowCtrl.cc
Executable file
743
engine/gui/containers/guiWindowCtrl.cc
Executable 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
132
engine/gui/containers/guiWindowCtrl.h
Executable file
132
engine/gui/containers/guiWindowCtrl.h
Executable 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
|
||||
26
engine/gui/controls/guiBackgroundCtrl.cc
Executable file
26
engine/gui/controls/guiBackgroundCtrl.cc
Executable 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);
|
||||
}
|
||||
|
||||
|
||||
29
engine/gui/controls/guiBackgroundCtrl.h
Executable file
29
engine/gui/controls/guiBackgroundCtrl.h
Executable 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
|
||||
120
engine/gui/controls/guiBitmapBorderCtrl.cc
Executable file
120
engine/gui/controls/guiBitmapBorderCtrl.cc
Executable 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);
|
||||
}
|
||||
234
engine/gui/controls/guiBitmapButtonCtrl.cc
Executable file
234
engine/gui/controls/guiBitmapButtonCtrl.cc
Executable 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);
|
||||
}
|
||||
66
engine/gui/controls/guiBitmapButtonCtrl.h
Executable file
66
engine/gui/controls/guiBitmapButtonCtrl.h
Executable 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
|
||||
166
engine/gui/controls/guiBitmapCtrl.cc
Executable file
166
engine/gui/controls/guiBitmapCtrl.cc
Executable 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;
|
||||
}
|
||||
49
engine/gui/controls/guiBitmapCtrl.h
Executable file
49
engine/gui/controls/guiBitmapCtrl.h
Executable 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
|
||||
41
engine/gui/controls/guiBorderButton.cc
Executable file
41
engine/gui/controls/guiBorderButton.cc
Executable 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);
|
||||
}
|
||||
|
||||
311
engine/gui/controls/guiButtonBaseCtrl.cc
Executable file
311
engine/gui/controls/guiButtonBaseCtrl.cc
Executable 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 );
|
||||
}
|
||||
}
|
||||
|
||||
69
engine/gui/controls/guiButtonBaseCtrl.h
Executable file
69
engine/gui/controls/guiButtonBaseCtrl.h
Executable 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
|
||||
54
engine/gui/controls/guiButtonCtrl.cc
Executable file
54
engine/gui/controls/guiButtonCtrl.cc
Executable 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);
|
||||
}
|
||||
|
||||
24
engine/gui/controls/guiButtonCtrl.h
Executable file
24
engine/gui/controls/guiButtonCtrl.h
Executable 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
|
||||
73
engine/gui/controls/guiCheckBoxCtrl.cc
Executable file
73
engine/gui/controls/guiCheckBoxCtrl.cc
Executable 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 //
|
||||
|
||||
|
||||
27
engine/gui/controls/guiCheckBoxCtrl.h
Executable file
27
engine/gui/controls/guiCheckBoxCtrl.h
Executable 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
|
||||
390
engine/gui/controls/guiColorPicker.cc
Executable file
390
engine/gui/controls/guiColorPicker.cc
Executable 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();
|
||||
}
|
||||
121
engine/gui/controls/guiColorPicker.h
Executable file
121
engine/gui/controls/guiColorPicker.h
Executable 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
106
engine/gui/controls/guiConsole.cc
Executable 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();
|
||||
}
|
||||
32
engine/gui/controls/guiConsole.h
Executable file
32
engine/gui/controls/guiConsole.h
Executable 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
|
||||
89
engine/gui/controls/guiConsoleEditCtrl.cc
Executable file
89
engine/gui/controls/guiConsoleEditCtrl.cc
Executable 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);
|
||||
}
|
||||
|
||||
37
engine/gui/controls/guiConsoleEditCtrl.h
Executable file
37
engine/gui/controls/guiConsoleEditCtrl.h
Executable 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
|
||||
158
engine/gui/controls/guiConsoleTextCtrl.cc
Executable file
158
engine/gui/controls/guiConsoleTextCtrl.cc
Executable 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 //
|
||||
58
engine/gui/controls/guiConsoleTextCtrl.h
Executable file
58
engine/gui/controls/guiConsoleTextCtrl.h
Executable 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_
|
||||
108
engine/gui/controls/guiDirectoryFileListCtrl.cc
Executable file
108
engine/gui/controls/guiDirectoryFileListCtrl.cc
Executable 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();
|
||||
}
|
||||
|
||||
40
engine/gui/controls/guiDirectoryFileListCtrl.h
Executable file
40
engine/gui/controls/guiDirectoryFileListCtrl.h
Executable 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
|
||||
382
engine/gui/controls/guiDirectoryTreeCtrl.cc
Executable file
382
engine/gui/controls/guiDirectoryTreeCtrl.cc
Executable 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;
|
||||
}
|
||||
58
engine/gui/controls/guiDirectoryTreeCtrl.h
Executable file
58
engine/gui/controls/guiDirectoryTreeCtrl.h
Executable 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
|
||||
2148
engine/gui/controls/guiMLTextCtrl.cc
Executable file
2148
engine/gui/controls/guiMLTextCtrl.cc
Executable file
File diff suppressed because it is too large
Load Diff
275
engine/gui/controls/guiMLTextCtrl.h
Executable file
275
engine/gui/controls/guiMLTextCtrl.h
Executable 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_
|
||||
376
engine/gui/controls/guiMLTextEditCtrl.cc
Executable file
376
engine/gui/controls/guiMLTextEditCtrl.cc
Executable 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);
|
||||
}
|
||||
}
|
||||
|
||||
41
engine/gui/controls/guiMLTextEditCtrl.h
Executable file
41
engine/gui/controls/guiMLTextEditCtrl.h
Executable 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_
|
||||
811
engine/gui/controls/guiPopUpCtrl.cc
Executable file
811
engine/gui/controls/guiPopUpCtrl.cc
Executable 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 //
|
||||
131
engine/gui/controls/guiPopUpCtrl.h
Executable file
131
engine/gui/controls/guiPopUpCtrl.h
Executable 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
|
||||
18
engine/gui/controls/guiRadioCtrl.cc
Executable file
18
engine/gui/controls/guiRadioCtrl.cc
Executable 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;
|
||||
}
|
||||
26
engine/gui/controls/guiRadioCtrl.h
Executable file
26
engine/gui/controls/guiRadioCtrl.h
Executable 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
|
||||
250
engine/gui/controls/guiSliderCtrl.cc
Executable file
250
engine/gui/controls/guiSliderCtrl.cc
Executable 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);
|
||||
}
|
||||
|
||||
48
engine/gui/controls/guiSliderCtrl.h
Executable file
48
engine/gui/controls/guiSliderCtrl.h
Executable 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
|
||||
227
engine/gui/controls/guiTabPageCtrl.cc
Executable file
227
engine/gui/controls/guiTabPageCtrl.cc
Executable 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 );
|
||||
|
||||
}
|
||||
47
engine/gui/controls/guiTabPageCtrl.h
Executable file
47
engine/gui/controls/guiTabPageCtrl.h
Executable 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
|
||||
197
engine/gui/controls/guiTextCtrl.cc
Executable file
197
engine/gui/controls/guiTextCtrl.cc
Executable 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 //
|
||||
64
engine/gui/controls/guiTextCtrl.h
Executable file
64
engine/gui/controls/guiTextCtrl.h
Executable 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_
|
||||
1239
engine/gui/controls/guiTextEditCtrl.cc
Executable file
1239
engine/gui/controls/guiTextEditCtrl.cc
Executable file
File diff suppressed because it is too large
Load Diff
118
engine/gui/controls/guiTextEditCtrl.h
Executable file
118
engine/gui/controls/guiTextEditCtrl.h
Executable 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
|
||||
271
engine/gui/controls/guiTextEditSliderCtrl.cc
Executable file
271
engine/gui/controls/guiTextEditSliderCtrl.cc
Executable 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
67
engine/gui/controls/guiTextEditSliderCtrl.h
Executable file
67
engine/gui/controls/guiTextEditSliderCtrl.h
Executable 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
|
||||
580
engine/gui/controls/guiTextListCtrl.cc
Executable file
580
engine/gui/controls/guiTextListCtrl.cc
Executable 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 );
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
92
engine/gui/controls/guiTextListCtrl.h
Executable file
92
engine/gui/controls/guiTextListCtrl.h
Executable 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
|
||||
3502
engine/gui/controls/guiTreeViewCtrl.cc
Executable file
3502
engine/gui/controls/guiTreeViewCtrl.cc
Executable file
File diff suppressed because it is too large
Load Diff
389
engine/gui/controls/guiTreeViewCtrl.h
Executable file
389
engine/gui/controls/guiTreeViewCtrl.h
Executable 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
420
engine/gui/core/guiArrayCtrl.cc
Executable 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
78
engine/gui/core/guiArrayCtrl.h
Executable 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
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
334
engine/gui/core/guiCanvas.h
Executable 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
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
569
engine/gui/core/guiControl.h
Executable 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
|
||||
134
engine/gui/core/guiDefaultControlRender.cc
Executable file
134
engine/gui/core/guiDefaultControlRender.cc
Executable 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 );
|
||||
}
|
||||
|
||||
19
engine/gui/core/guiDefaultControlRender.h
Executable file
19
engine/gui/core/guiDefaultControlRender.h
Executable 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
160
engine/gui/core/guiTSControl.cc
Executable 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
73
engine/gui/core/guiTSControl.h
Executable 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
371
engine/gui/core/guiTypes.cc
Executable 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
169
engine/gui/core/guiTypes.h
Executable 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
|
||||
37
engine/gui/editor/guiControlListPopup.cc
Executable file
37
engine/gui/editor/guiControlListPopup.cc
Executable 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
702
engine/gui/editor/guiDebugger.cc
Executable 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
82
engine/gui/editor/guiDebugger.h
Executable 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
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
98
engine/gui/editor/guiEditCtrl.h
Executable 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
|
||||
241
engine/gui/editor/guiFilterCtrl.cc
Executable file
241
engine/gui/editor/guiFilterCtrl.cc
Executable 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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
74
engine/gui/editor/guiFilterCtrl.h
Executable file
74
engine/gui/editor/guiFilterCtrl.h
Executable 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
350
engine/gui/editor/guiGraphCtrl.cc
Executable 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;
|
||||
}
|
||||
65
engine/gui/editor/guiGraphCtrl.h
Executable file
65
engine/gui/editor/guiGraphCtrl.h
Executable 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
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
258
engine/gui/editor/guiInspector.h
Executable 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
|
||||
350
engine/gui/editor/guiInspectorTypes.cc
Executable file
350
engine/gui/editor/guiInspectorTypes.cc
Executable 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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
162
engine/gui/editor/guiInspectorTypes.h
Executable file
162
engine/gui/editor/guiInspectorTypes.h
Executable 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
851
engine/gui/editor/guiMenuBar.cc
Executable 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
131
engine/gui/editor/guiMenuBar.h
Executable 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
|
||||
1379
engine/gui/game/guiAviBitmapCtrl.cc
Executable file
1379
engine/gui/game/guiAviBitmapCtrl.cc
Executable file
File diff suppressed because it is too large
Load Diff
186
engine/gui/game/guiAviBitmapCtrl.h
Executable file
186
engine/gui/game/guiAviBitmapCtrl.h
Executable 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_ */
|
||||
148
engine/gui/game/guiChunkedBitmapCtrl.cc
Executable file
148
engine/gui/game/guiChunkedBitmapCtrl.cc
Executable 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);
|
||||
}
|
||||
119
engine/gui/game/guiFadeinBitmapCtrl.cc
Executable file
119
engine/gui/game/guiFadeinBitmapCtrl.cc
Executable 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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
817
engine/gui/game/guiMessageVectorCtrl.cc
Executable file
817
engine/gui/game/guiMessageVectorCtrl.cc
Executable 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;
|
||||
}
|
||||
|
||||
135
engine/gui/game/guiMessageVectorCtrl.h
Executable file
135
engine/gui/game/guiMessageVectorCtrl.h
Executable 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_
|
||||
84
engine/gui/game/guiProgressCtrl.cc
Executable file
84
engine/gui/game/guiProgressCtrl.cc
Executable 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);
|
||||
}
|
||||
|
||||
33
engine/gui/game/guiProgressCtrl.h
Executable file
33
engine/gui/game/guiProgressCtrl.h
Executable 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
|
||||
192
engine/gui/shiny/guiEffectCanvas.cc
Executable file
192
engine/gui/shiny/guiEffectCanvas.cc
Executable 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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
49
engine/gui/shiny/guiEffectCanvas.h
Executable file
49
engine/gui/shiny/guiEffectCanvas.h
Executable 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
Reference in New Issue
Block a user