Initial commit

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

View File

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

View File

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

View File

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

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

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

View File

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

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

File diff suppressed because it is too large Load Diff

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

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

View File

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

View File

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

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

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

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

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