added everything

This commit is contained in:
Metario
2017-04-17 06:17:10 -06:00
commit 9c6ff74f19
6121 changed files with 1625704 additions and 0 deletions

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -0,0 +1,915 @@
//-----------------------------------------------------------------------------
// 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;
mScrollBarDragTolerance = 130;
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 );
}
ConsoleMethod(GuiScrollCtrl, setScrollPosition, void, 4, 4, "(x, y) - scrolls the scroll control to the specified position.")
{
object->scrollTo(dAtoi(argv[2]), dAtoi(argv[3]));
}
ConsoleMethod(GuiScrollCtrl, getScrollPositionX, S32, 2, 2, "() - get the current x scroll position of the scroll control.")
{
return object->getChildRelPos().x;
}
ConsoleMethod(GuiScrollCtrl, getScrollPositionY, S32, 2, 2, "() - get the current y scroll position of the scroll control.")
{
return object->getChildRelPos().y;
}
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;
// If Extent X Changed, check Horiz Scrollbar.
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();
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 - mScrollBarDragTolerance &&
curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
curMousePos.y >= mVTrackRect.point.y - mScrollBarDragTolerance &&
curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarDragTolerance)
{
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 - mScrollBarDragTolerance &&
curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarDragTolerance &&
curMousePos.y >= mHTrackRect.point.y - mScrollBarDragTolerance &&
curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarDragTolerance)
{
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 );
Point2I previousPos = mChildPos;
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 );
}
// If no scrolling happened (already at the top), pass it on to the parent.
GuiControl* parent = getParent();
if (parent && (previousPos == mChildPos))
return parent->onMouseWheelUp(event);
return true;
}
bool GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event)
{
if ( !mAwake || !mVisible )
return( false );
Point2I previousPos = mChildPos;
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 );
}
// If no scrolling happened (already at the bottom), pass it on to the parent.
GuiControl* parent = getParent();
if (parent && (previousPos == mChildPos))
return parent->onMouseWheelDown(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, -(S32)pageHeight);
break;
case DownPage:
scrollDelta(0, pageHeight);
break;
case UpArrow:
scrollDelta(0, -(S32)rowHeight);
break;
case DownArrow:
scrollDelta(0, rowHeight);
break;
}
}
if (mHBarEnabled)
{
switch(reg)
{
case LeftPage:
scrollDelta(-(S32)pageWidth, 0);
break;
case RightPage:
scrollDelta(pageWidth, 0);
break;
case LeftArrow:
scrollDelta(-(S32)columnWidth, 0);
break;
case RightArrow:
scrollDelta(columnWidth, 0);
break;
}
}
}
void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI r(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
if (mProfile->mOpaque)
dglDrawRectFill(r, mProfile->mFillColor);
if (mProfile->mBorder)
renderFilledBorder(r, mProfile);
// draw scroll bars
if (mHasVScrollBar)
drawVScrollBar(offset);
if (mHasHScrollBar)
drawHScrollBar(offset);
//draw the scroll corner
if (mHasVScrollBar && mHasHScrollBar)
drawScrollCorner(offset);
// draw content controls
// create a rect to intersect with the updateRect
RectI contentRect(mContentPos.x + offset.x, mContentPos.y + offset.y, mContentExt.x, mContentExt.y);
if(contentRect.intersect(updateRect))
renderChildControls(offset, contentRect);
// Finally draw the last vis rect (debug aid, BJG)
//RectI renderRect = lastVisRect;
//renderRect.point += mContentPos + offset;
//dglSetClipRect(r);
//dglDrawRect(renderRect, ColorI(0, 255, 0));
}
void GuiScrollCtrl::drawBorder( const Point2I &offset, bool /*isFirstResponder*/ )
{
}
void GuiScrollCtrl::drawVScrollBar(const Point2I &offset)
{
Point2I pos = offset + mUpArrowRect.point;
S32 bitmap = (mVBarEnabled ? ((curHitRegion == UpArrow && stateDepressed) ?
BmpStates * BmpUp + BmpHilite : BmpStates * BmpUp) : BmpStates * BmpUp + BmpDisabled);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
pos.y += mScrollBarArrowBtnLength;
S32 end;
if (mVBarEnabled)
end = mVThumbPos + offset.y;
else
end = mDownArrowRect.point.y + offset.y;
bitmap = (mVBarEnabled ? ((curHitRegion == DownPage && stateDepressed) ?
BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage) : BmpStates * BmpVPage + BmpDisabled);
if (end > pos.y)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
}
pos.y = end;
if (mVBarEnabled)
{
bool thumbSelected = (curHitRegion == VertThumb && stateDepressed);
S32 ttop = (thumbSelected ? BmpStates * BmpVThumbTopCap + BmpHilite : BmpStates * BmpVThumbTopCap);
S32 tmid = (thumbSelected ? BmpStates * BmpVThumb + BmpHilite : BmpStates * BmpVThumb);
S32 tbot = (thumbSelected ? BmpStates * BmpVThumbBottomCap + BmpHilite : BmpStates * BmpVThumbBottomCap);
// draw the thumb
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
pos.y += mBitmapBounds[ttop].extent.y;
end = mVThumbPos + mVThumbSize - mBitmapBounds[tbot].extent.y + offset.y;
if (end > pos.y)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[tmid].extent.x, end - pos.y)), mBitmapBounds[tmid]);
}
pos.y = end;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
pos.y += mBitmapBounds[tbot].extent.y;
end = mVTrackRect.point.y + mVTrackRect.extent.y - 1 + offset.y;
bitmap = (curHitRegion == DownPage && stateDepressed) ? BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage;
if (end > pos.y)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
}
pos.y = end;
}
bitmap = (mVBarEnabled ? ((curHitRegion == DownArrow && stateDepressed ) ?
BmpStates * BmpDown + BmpHilite : BmpStates * BmpDown) : BmpStates * BmpDown + BmpDisabled);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
}
void GuiScrollCtrl::drawHScrollBar(const Point2I &offset)
{
S32 bitmap;
//draw the left arrow
bitmap = (mHBarEnabled ? ((curHitRegion == LeftArrow && stateDepressed) ?
BmpStates * BmpLeft + BmpHilite : BmpStates * BmpLeft) : BmpStates * BmpLeft + BmpDisabled);
Point2I pos = offset;
pos += mLeftArrowRect.point;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
pos.x += mLeftArrowRect.extent.x;
//draw the left page
S32 end;
if (mHBarEnabled)
end = mHThumbPos + offset.x;
else
end = mRightArrowRect.point.x + offset.x;
bitmap = (mHBarEnabled ? ((curHitRegion == LeftPage && stateDepressed) ?
BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage) : BmpStates * BmpHPage + BmpDisabled);
if (end > pos.x)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
}
pos.x = end;
//draw the thumb and the rightPage
if (mHBarEnabled)
{
bool thumbSelected = (curHitRegion == HorizThumb && stateDepressed);
S32 ttop = (thumbSelected ? BmpStates * BmpHThumbLeftCap + BmpHilite : BmpStates * BmpHThumbLeftCap );
S32 tmid = (thumbSelected ? BmpStates * BmpHThumb + BmpHilite : BmpStates * BmpHThumb);
S32 tbot = (thumbSelected ? BmpStates * BmpHThumbRightCap + BmpHilite : BmpStates * BmpHThumbRightCap);
// draw the thumb
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
pos.x += mBitmapBounds[ttop].extent.x;
end = mHThumbPos + mHThumbSize - mBitmapBounds[tbot].extent.x + offset.x;
if (end > pos.x)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[tmid].extent.y)), mBitmapBounds[tmid]);
}
pos.x = end;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
pos.x += mBitmapBounds[tbot].extent.x;
end = mHTrackRect.point.x + mHTrackRect.extent.x - 1 + offset.x;
bitmap = ((curHitRegion == RightPage && stateDepressed) ? BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage);
if (end > pos.x)
{
dglClearBitmapModulation();
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
}
pos.x = end;
}
bitmap = (mHBarEnabled ? ((curHitRegion == RightArrow && stateDepressed) ?
BmpStates * BmpRight + BmpHilite : BmpStates * BmpRight) : BmpStates * BmpRight + BmpDisabled);
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
}
void GuiScrollCtrl::drawScrollCorner(const Point2I &offset)
{
Point2I pos = offset;
pos.x += mRightArrowRect.point.x + mRightArrowRect.extent.x;
pos.y += mRightArrowRect.point.y;
dglClearBitmapModulation();
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[BmpStates * BmpResize]);
}
void GuiScrollCtrl::autoScroll(Region reg)
{
scrollByRegion(reg);
}

View File

@ -0,0 +1,204 @@
//-----------------------------------------------------------------------------
// 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
S32 mScrollBarDragTolerance; // maximal distance from scrollbar at which a scrollbar drag is still valid
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 getChildRelPos() { return mChildRelPos; };
Point2I getChildExtent() { return mChildExt; }
Point2I getContentExtent() { return mContentExt; }
Point2I getChildMargin() { return mChildMargin; } // DAW: Added to aid in sizing calculations
S32 getBorderThickness(void) { return mBorderThickness; }
S32 scrollBarThickness() const { return(mScrollBarThickness); }
S32 scrollBarArrowBtnLength() const { return(mScrollBarArrowBtnLength); }
bool hasHScrollBar() const { return(mHasHScrollBar); }
bool hasVScrollBar() const { return(mHasVScrollBar); }
bool enabledHScrollBar() const { return(mHBarEnabled); }
bool enabledVScrollBar() const { return(mVBarEnabled); }
bool isScrolledToBottom() { return mChildPos.y + mChildExt.y <= mContentPos.y + mContentExt.y; }
bool wantsTabListMembership();
bool becomeFirstResponder();
bool loseFirstResponder();
Region getCurHitRegion(void) { return curHitRegion; }
bool onKeyDown(const GuiEvent &event);
void onMouseDown(const GuiEvent &event);
void onMouseRepeat(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
bool onMouseWheelUp(const GuiEvent &event);
bool onMouseWheelDown(const GuiEvent &event);
bool onWake();
void onSleep();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
virtual void drawBorder(const Point2I &offset, bool isFirstResponder);
virtual void drawVScrollBar(const Point2I &offset);
virtual void drawHScrollBar(const Point2I &offset);
virtual void drawScrollCorner(const Point2I &offset);
virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
};
#endif //_GUI_SCROLL_CTRL_H

View File

@ -0,0 +1,302 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "gui/containers/guiStackCtrl.h"
IMPLEMENT_CONOBJECT(GuiStackControl);
static EnumTable::Enums stackTypeEnum[] =
{
{ GuiStackControl::stackingTypeVert, "Vertical" },
{ GuiStackControl::stackingTypeHoriz,"Horizontal" },
{ GuiStackControl::stackingTypeDyn,"Dynamic" }
};
static EnumTable gStackTypeTable(3, &stackTypeEnum[0]);
static EnumTable::Enums stackHorizEnum[] =
{
{ GuiStackControl::horizStackLeft, "Left to Right" },
{ GuiStackControl::horizStackRight,"Right to Left" }
};
static EnumTable gStackHorizSizingTable(2, &stackHorizEnum[0]);
static EnumTable::Enums stackVertEnum[] =
{
{ GuiStackControl::vertStackTop, "Top to Bottom" },
{ GuiStackControl::vertStackBottom,"Bottom to Top" }
};
static EnumTable gStackVertSizingTable(2, &stackVertEnum[0]);
GuiStackControl::GuiStackControl()
{
mMinExtent.set(24, 24);
mResizing = false;
mStackingType = stackingTypeVert;
mStackVertSizing = vertStackTop;
mStackHorizSizing = horizStackLeft;
mPadding = 0;
}
void GuiStackControl::initPersistFields()
{
addGroup( "Stacking" );
addField( "StackingType", TypeEnum, Offset(mStackingType, GuiStackControl), 1, &gStackTypeTable);
addField( "HorizStacking", TypeEnum, Offset(mStackHorizSizing, GuiStackControl), 1, &gStackHorizSizingTable);
addField( "VertStacking", TypeEnum, Offset(mStackVertSizing, GuiStackControl), 1, &gStackVertSizingTable);
addField( "Padding", TypeS32, Offset(mPadding, GuiStackControl));
endGroup( "Stacking" );
Parent::initPersistFields();
}
ConsoleMethod( GuiStackControl, updateStack, void, 2, 2, "%stackCtrl.updateStack() - Restacks controls it owns")
{
object->updatePanes();
}
bool GuiStackControl::onWake()
{
if ( !Parent::onWake() )
return false;
updatePanes();
return true;
}
void GuiStackControl::onSleep()
{
Parent::onSleep();
}
void GuiStackControl::updatePanes()
{
// Prevent recursion
if(mResizing)
return;
// Set Resizing.
mResizing = true;
Point2I extent = getExtent();
// Do we need to stack horizontally?
if( ( extent.x > extent.y && mStackingType == stackingTypeDyn ) || mStackingType == stackingTypeHoriz )
{
switch( mStackHorizSizing )
{
case horizStackLeft:
stackFromLeft();
break;
case horizStackRight:
stackFromRight();
break;
}
}
// Or, vertically?
else if( ( extent.y > extent.x && mStackingType == stackingTypeDyn ) || mStackingType == stackingTypeVert)
{
switch( mStackVertSizing )
{
case vertStackTop:
stackFromTop();
break;
case vertStackBottom:
stackFromBottom();
break;
}
}
// Clear Sizing Flag.
mResizing = false;
}
void GuiStackControl::stackFromBottom()
{
// Store the sum of the heights of our controls.
S32 totalHeight=0;
Point2I curPos;
// 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 && gc->isVisible() )
{
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));
}
}
}
void GuiStackControl::stackFromTop()
{
// Store the sum of the heights of our controls.
S32 totalHeight=0;
Point2I curPos;
// 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 && gc->isVisible() )
{
// 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.
if( totalHeight > getExtent().y )
{
curPos.x = getExtent().x;
curPos.y = totalHeight;
resize(getPosition(), curPos);
}
else if( totalHeight < getExtent().y )
{
curPos.x = getExtent().x;
curPos.y = getMax( totalHeight , mMinExtent.y );
resize(getPosition(), curPos);
}
}
void GuiStackControl::stackFromLeft()
{
// Store the sum of the heights of our controls.
S32 totalWidth=0;
Point2I curPos;
// 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 && gc->isVisible() )
{
// We must place the child...
// Make it have our width but keep its height
Point2I childExtent = gc->getExtent();
gc->resize(curPos - getPosition(), Point2I( childExtent.x,getExtent().y));
// Update our state...
curPos.x += childExtent.x + mPadding;
totalWidth += childExtent.x + mPadding;
}
}
// Conform our size to the sum of the child sizes.
if( totalWidth > getExtent().x )
{
curPos.x = totalWidth;
curPos.y = getExtent().y;
resize(getPosition(), curPos);
}
}
void GuiStackControl::stackFromRight()
{
}
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::removeObject(SimObject *obj)
{
Parent::removeObject(obj);
updatePanes();
}
bool GuiStackControl::reOrder(SimObject* obj, SimObject* target)
{
bool ret = Parent::reOrder(obj, target);
if (ret)
updatePanes();
return ret;
}
void GuiStackControl::childResized(GuiControl *child)
{
updatePanes();
}

View File

@ -0,0 +1,69 @@
#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 stack of GUI controls.
///
/// This maintains a horizontal or 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).
///
///
/// @todo Make this support horizontal right to left stacks.
class GuiStackControl : public GuiControl
{
protected:
typedef GuiControl Parent;
bool mResizing;
S32 mPadding;
S32 mStackHorizSizing; ///< Set from horizSizingOptions.
S32 mStackVertSizing; ///< Set from vertSizingOptions.
S32 mStackingType;
public:
GuiStackControl();
enum stackingOptions
{
horizStackLeft = 0,///< Stack from left to right when horizontal
horizStackRight, ///< Stack from right to left when horizontal
vertStackTop, ///< Stack from top to bottom when vertical
vertStackBottom, ///< Stack from bottom to top when vertical
stackingTypeVert, ///< Always stack vertically
stackingTypeHoriz, ///< Always stack horizontally
stackingTypeDyn ///< Dynamically switch based on width/height
};
void resize(const Point2I &newPosition, const Point2I &newExtent);
void childResized(GuiControl *child);
bool onWake();
void onSleep();
void updatePanes();
void stackFromLeft();
void stackFromRight();
void stackFromTop();
void stackFromBottom();
S32 getCount() { return size(); }; /// Returns the number of children in the stack
void addObject(SimObject *obj);
void removeObject(SimObject *obj);
bool reOrder(SimObject* obj, SimObject* target = 0);
static void initPersistFields();
DECLARE_CONOBJECT(GuiStackControl);
};
#endif

View File

@ -0,0 +1,895 @@
//-----------------------------------------------------------------------------
// 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"
#include "gui/core/guiDefaultControlRender.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,&tabAlignEnums[0]);
IMPLEMENT_CONOBJECT(GuiTabBookCtrl);
GuiTabBookCtrl::GuiTabBookCtrl()
{
VECTOR_SET_ASSOCIATION(mPages);
mTabHeight = 24;
mLastTabHeight = mTabHeight;
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);
mPages.reserve(12);
mTabMargin = 7;
mMinTabWidth = 64;
}
void GuiTabBookCtrl::initPersistFields()
{
Parent::initPersistFields();
addField("TabPosition", TypeEnum, Offset(mTabPosition,GuiTabBookCtrl), 1, &gTabAlignEnums );
addField("TabHeight", TypeS32, Offset(mTabHeight,GuiTabBookCtrl) );
addField("TabMargin", TypeS32, Offset(mTabMargin,GuiTabBookCtrl));
addField("MinTabWidth", TypeS32, Offset(mMinTabWidth,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();
return true;
}
void GuiTabBookCtrl::onRemove()
{
Parent::onRemove();
}
void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
{
for (S32 i = 0; i < mPages.size(); i++ )
{
GuiTabPageCtrl* tab = mPages[i].Page;
if( tab == child )
{
if( tab == mActivePage )
mActivePage = NULL;
mPages.erase( i );
break;
}
}
if( mPages.empty() )
mActivePage = NULL;
else if (mActivePage == NULL )
mActivePage = static_cast<GuiTabPageCtrl*>(mPages[0].Page);
}
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;
}
TabHeaderInfo newPage;
newPage.Page = page;
newPage.TabRow = -1;
newPage.TabColumn = -1;
mPages.push_back( newPage );
// Calculate Page Information
calculatePageTabs();
child->resize( mPageRect.point, mPageRect.extent );
// Select this 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 );
calculatePageTabs();
// Resize Children
SimSet::iterator i;
for(i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
ctrl->resize( mPageRect.point, mPageRect.extent );
}
}
void GuiTabBookCtrl::childResized(GuiControl *child)
{
//Parent::childResized( child );
child->resize( mPageRect.point, mPageRect.extent );
}
void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
{
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mTabRect.pointInRect( localMouse ) )
{
GuiTabPageCtrl *tab = findHitTab( localMouse );
if( tab != NULL )
selectPage( tab );
}
}
void GuiTabBookCtrl::onMouseMove(const GuiEvent &event)
{
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mTabRect.pointInRect( localMouse ) )
{
GuiTabPageCtrl *tab = findHitTab( localMouse );
if( tab != NULL && mHoverTab != tab )
mHoverTab = tab;
else if ( !tab )
mHoverTab = NULL;
}
Parent::onMouseMove( event );
}
void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event )
{
mHoverTab = NULL;
}
bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
{
bool handled = false;
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mTabRect.pointInRect( localMouse ) )
{
GuiTabPageCtrl *tab = findHitTab( localMouse );
if( tab != NULL )
{
selectPage( tab );
handled = true;
}
}
// 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 );
}
// Return whether we handled this or not.
return handled;
}
void GuiTabBookCtrl::onPreRender()
{
// sometimes we need to resize because of a changed persistent field
// that's what this does
solveDirty();
}
void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
{
RectI tabRect = mTabRect;
tabRect.point += offset;
RectI pageRect = mPageRect;
pageRect.point += 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 background
renderBackground( offset, updateRect );
// Render our tabs
renderTabs( offset );
// Render Children
renderChildControls( offset, updateRect );
// Restore old modulation
dglSetBitmapModulation( oldModulation );
}
void GuiTabBookCtrl::renderBackground( Point2I offset, const RectI& updateRect )
{
RectI winRect;
winRect.point = offset;
winRect.extent = mBounds.extent;
if( mHasTexture && mProfile->mBitmapArrayRects.size() >= NumBitmaps)
renderSizableBitmapBordersFilledIndex( winRect, TabBackground, mProfile );
else
dglDrawRectFill(winRect, mProfile->mFillColor);
}
void GuiTabBookCtrl::renderTabs( const Point2I &offset )
{
// If the tab size is zero, don't render tabs,
// and assume it's a tab-less tab-book - JDD
if( mPages.empty() || mTabHeight <= 0 )
return;
for( S32 i = 0; i < mPages.size(); i++ )
{
RectI tabBounds = mPages[i].TabRect;
tabBounds.point += offset;
GuiTabPageCtrl *tab = mPages[i].Page;
if( tab != NULL )
renderTab( tabBounds, tab );
}
}
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 indexMultiplier = 1;
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
if ( mActivePage == tab )
indexMultiplier += TabSelected;
else if( mHoverTab == tab )
indexMultiplier += TabHover;
else
indexMultiplier += TabNormal;
//dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, ( mTabPosition == AlignBottom ) ? GFlip_Y : 0 );
break;
case AlignLeft:
case AlignRight:
if ( mActivePage == tab )
indexMultiplier += TabSelectedVertical;
else if( mHoverTab == tab )
indexMultiplier += TabHoverVertical;
else
indexMultiplier += TabNormalVertical;
//dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, ( mTabPosition == AlignRight ) ? GFlip_X : 0 );
break;
}
renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile );
}
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);
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
renderJustifiedTextRot( tabRect.point, tabRect.extent, text, 0);
break;
case AlignLeft:
renderJustifiedTextRot( tabRect.point, tabRect.extent, text, -90 );
break;
case AlignRight:
renderJustifiedTextRot( tabRect.point, tabRect.extent, text, -90 );
break;
}
dglSetBitmapModulation( oldColor);
}
void GuiTabBookCtrl::renderJustifiedTextRot(Point2I offset, Point2I extent, const char *text, F32 rot )
{
GFont *font = mProfile->mFont;
S32 textWidth = font->getStrWidth(text);
Point2I start;
offset += mProfile->mTextOffset;
if( mTabPosition == AlignLeft || mTabPosition == AlignRight )
{
switch( mProfile->mAlignment )
{
case GuiControlProfile::RightJustify:
start.set( 0, extent.y - textWidth);
break;
case GuiControlProfile::CenterJustify:
start.set( 0, ( extent.y - textWidth) / 2);
break;
default:
// GuiControlProfile::LeftJustify
start.set( 0, 0 );
break;
}
if( textWidth > extent.y )
start.set( 0, 0 );
start.x = ( ( extent.x - font->getHeight() ) / 2 ) + font->getHeight();
}
else
{
// align the horizontal
switch( mProfile->mAlignment )
{
case GuiControlProfile::RightJustify:
start.set( extent.x - textWidth, 0 );
break;
case GuiControlProfile::CenterJustify:
start.set( ( extent.x - textWidth) / 2, 0 );
break;
default:
// GuiControlProfile::LeftJustify
start.set( 0, 0 );
break;
}
if( textWidth > extent.x )
start.set( 0, 0 );
start.y = ( extent.y - font->getHeight() ) / 2;
}
dglDrawText( font, start + offset, text, mProfile->mFontColors,9,rot );
}
// This is nothing but a clever hack to allow the tab page children
// to cast this to a GuiControl* so that the file doesn't need to have circular
// includes. generic method overriding for the win!
void GuiTabBookCtrl::setUpdate()
{
Parent::setUpdate();
setUpdateRegion(Point2I(0,0), mBounds.extent);
calculatePageTabs();
}
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 )
{
calculatePageTabs();
resize( mBounds.point, mBounds.extent );
}
}
S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page )
{
if( !page )
return mTabWidth;
StringTableEntry text = page->getText();
if( !text || dStrlen(text) == 0 || mProfile->mFont == NULL )
return mTabWidth;
GFont *font = mProfile->mFont;
return font->getStrNWidth( text, dStrlen(text) );
}
void GuiTabBookCtrl::calculatePageTabs()
{
// Short Circuit.
//
// If the tab size is zero, don't render tabs,
// and assume it's a tab-less tab-book - JDD
if( mPages.empty() || mTabHeight <= 0 )
return;
S32 currRow = 0;
S32 currColumn = 0;
S32 maxRow = 0;
S32 maxColumn = 0;
S32 currX = 0;
S32 currY = 0;
S32 maxWidth = 0;
S32 maxHeight = 0;
for( S32 i = 0; i < mPages.size(); i++ )
{
// Fetch Tab Width
S32 tabWidth = calculatePageTabWidth( mPages[i].Page ) + ( mTabMargin * 2 );
tabWidth = getMax( tabWidth, mMinTabWidth );
TabHeaderInfo &info = mPages[i];
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
// If we're going to go outside our bounds
// with this tab move it down a row
if( currX + tabWidth > mBounds.extent.x )
{
// Calculate and Advance State.
maxWidth = getMax( tabWidth, maxWidth );
balanceRow( currRow, currX );
info.TabRow = ++currRow;
// Reset Necessaries
info.TabColumn = currColumn = maxWidth = currX = 0;
}
else
{
info.TabRow = currRow;
info.TabColumn = currColumn++;
}
// Calculate Tabs Bounding Rect
info.TabRect.point.x = currX;
info.TabRect.extent.x = tabWidth;
info.TabRect.extent.y = mTabHeight;
// Adjust Y Point based on alignment
if( mTabPosition == AlignTop )
info.TabRect.point.y = ( info.TabRow * mTabHeight );
else
info.TabRect.point.y = mBounds.extent.y - ( ( 1 + info.TabRow ) * mTabHeight );
currX += tabWidth;
break;
case AlignLeft:
case AlignRight:
// If we're going to go outside our bounds
// with this tab move it down a row
if( currY + tabWidth > mBounds.extent.y )
{
// Balance Tab Column.
balanceColumn( currColumn, currY );
// Calculate and Advance State.
info.TabColumn = ++currColumn;
info.TabRow = currRow = currY = 0;
}
else
{
info.TabColumn = currColumn;
info.TabRow = currRow++;
}
// Calculate Tabs Bounding Rect
info.TabRect.point.y = currY;
info.TabRect.extent.y = tabWidth;
info.TabRect.extent.x = mTabHeight;
// Adjust Y Point based on alignment
if( mTabPosition == AlignLeft )
info.TabRect.point.x = ( info.TabColumn * mTabHeight );
else
info.TabRect.point.x = mBounds.extent.x - ( (1 + info.TabColumn) * mTabHeight );
currY += tabWidth;
break;
};
}
currRow++;
currColumn++;
Point2I localPoint = mBounds.extent;
// Calculate
switch( mTabPosition )
{
case AlignTop:
localPoint.y -= mBounds.point.y;
mTabRect.point.x = 0;
mTabRect.extent.x = localPoint.x;
mTabRect.point.y = 0;
mTabRect.extent.y = currRow * mTabHeight;
mPageRect.point.x = 0;
mPageRect.point.y = mTabRect.extent.y;
mPageRect.extent.x = mTabRect.extent.x;
mPageRect.extent.y = mBounds.extent.y - mTabRect.extent.y;
break;
case AlignBottom:
mTabRect.point.x = 0;
mTabRect.extent.x = localPoint.x;
mTabRect.extent.y = currRow * mTabHeight;
mTabRect.point.y = mBounds.extent.y - mTabRect.extent.y;
mPageRect.point.x = 0;
mPageRect.point.y = 0;
mPageRect.extent.x = mTabRect.extent.x;
mPageRect.extent.y = localPoint.y - mTabRect.extent.y;
break;
case AlignLeft:
mTabRect.point.y = 0;
mTabRect.extent.y = mBounds.extent.y;
mTabRect.point.x = 0;
mTabRect.extent.x = currColumn * mTabHeight;
mPageRect.point.y = 0;
mPageRect.point.x = mTabRect.extent.x;
mPageRect.extent.y = localPoint.y;
mPageRect.extent.x = localPoint.x - mTabRect.extent.x;
break;
case AlignRight:
mTabRect.extent.x = currColumn * mTabHeight;
mTabRect.point.y = 0;
mTabRect.extent.y = localPoint.y;
mTabRect.point.x = localPoint.x - mTabRect.extent.x;
mPageRect.point.y = 0;
mPageRect.point.x = 0;
mPageRect.extent.y = localPoint.y;
mPageRect.extent.x = localPoint.x - mTabRect.extent.x;
break;
};
}
void GuiTabBookCtrl::balanceColumn( S32 column , S32 totalTabWidth )
{
// Short Circuit.
//
// If the tab size is zero, don't render tabs,
// and assume it's a tab-less tab-book - JDD
if( mPages.empty() || mTabHeight <= 0 )
return;
Vector<TabHeaderInfo*> rowTemp;
rowTemp.clear();
for( S32 i = 0; i < mPages.size(); i++ )
{
TabHeaderInfo &info = mPages[i];
if(info.TabColumn == column )
rowTemp.push_back( &mPages[i] );
}
if( rowTemp.empty() )
return;
// Balance the tabs across the remaining space
S32 spaceToDivide = mBounds.extent.y - totalTabWidth;
S32 pointDelta = 0;
for( S32 i = 0; i < rowTemp.size(); i++ )
{
TabHeaderInfo &info = *rowTemp[i];
S32 extraSpace = (S32)( spaceToDivide / rowTemp.size() );
info.TabRect.extent.y += extraSpace;
info.TabRect.point.y += pointDelta;
pointDelta += extraSpace;
}
}
void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth )
{
// Short Circuit.
//
// If the tab size is zero, don't render tabs,
// and assume it's a tab-less tab-book - JDD
if( mPages.empty() || mTabHeight <= 0 )
return;
Vector<TabHeaderInfo*> rowTemp;
rowTemp.clear();
for( S32 i = 0; i < mPages.size(); i++ )
{
TabHeaderInfo &info = mPages[i];
if(info.TabRow == row )
rowTemp.push_back( &mPages[i] );
}
if( rowTemp.empty() )
return;
// Balance the tabs across the remaining space
S32 spaceToDivide = mBounds.extent.x - totalTabWidth;
S32 pointDelta = 0;
for( S32 i = 0; i < rowTemp.size(); i++ )
{
TabHeaderInfo &info = *rowTemp[i];
S32 extraSpace = (S32)spaceToDivide / ( rowTemp.size() );
info.TabRect.extent.x += extraSpace;
info.TabRect.point.x += pointDelta;
pointDelta += extraSpace;
}
}
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
{
return findHitTab( event.mousePoint );
}
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
{
// Short Circuit.
//
// If the tab size is zero, don't render tabs,
// and assume it's a tab-less tab-book - JDD
if( mPages.empty() || mTabHeight <= 0 )
return NULL;
for( S32 i = 0; i < mPages.size(); i++ )
{
if( mPages[i].TabRect.pointInRect( hitPoint ) )
return mPages[i].Page;
}
return NULL;
}
ConsoleMethod( GuiTabBookCtrl, selectPage, void, 3, 3, "(int pageIndex)")
{
S32 pageIndex = dAtoi(argv[2]);
object->selectPage(pageIndex);
}
void GuiTabBookCtrl::selectPage( S32 index )
{
if( mPages.size() < index )
return;
// Select the page
selectPage( mPages[ index ].Page );
}
void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
{
Vector<TabHeaderInfo>::iterator i = mPages.begin();
for( ; i != mPages.end() ; i++ )
{
GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>((*i).Page);
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 );
}
}
bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event)
{
// Tab = Next Page
// Ctrl-Tab = Previous Page
if( 0 && event.keyCode == KEY_TAB )
{
if( event.modifier & SI_CTRL )
selectPrevPage();
else
selectNextPage();
return true;
}
return Parent::onKeyDown( event );
}
void GuiTabBookCtrl::selectNextPage()
{
if( mPages.empty() )
return;
if( mActivePage == NULL )
mActivePage = mPages[0].Page;
S32 nI = 0;
for( ; nI < mPages.size(); nI++ )
{
GuiTabPageCtrl *tab = mPages[ nI ].Page;
if( tab == mActivePage )
{
if( nI == ( mPages.size() - 1 ) )
selectPage( 0 );
else if ( nI + 1 <= ( mPages.size() - 1 ) )
selectPage( nI + 1 );
else
selectPage( 0 );
// Notify User
if( isMethod( "onTabSelected" ) )
{
char *retBuffer = Con::getReturnBuffer( 512 );
dStrcpy( retBuffer, tab->getText() );
Con::executef( this, 2, "onTabSelected", retBuffer );
}
return;
}
}
}
void GuiTabBookCtrl::selectPrevPage()
{
if( mPages.empty() )
return;
if( mActivePage == NULL )
mActivePage = mPages[0].Page;
S32 nI = 0;
for( ; nI < mPages.size(); nI++ )
{
GuiTabPageCtrl *tab = mPages[ nI ].Page;
if( tab == mActivePage )
{
if( nI == 0 )
selectPage( mPages.size() - 1 );
else
selectPage( nI - 1 );
// Notify User
if( isMethod( "onTabSelected" ) )
{
char *retBuffer = Con::getReturnBuffer( 512 );
dStrcpy( retBuffer, tab->getText() );
Con::executef( this, 2, "onTabSelected", retBuffer );
}
return;
}
}
}

View File

@ -0,0 +1,230 @@
//-----------------------------------------------------------------------------
// 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, ///< Align the tabs on the left of the tab book control
AlignRight ///< Align the tabs on the right of the tab book control
};
struct TabHeaderInfo
{
GuiTabPageCtrl *Page;
S32 TextWidth;
S32 TabRow;
S32 TabColumn;
RectI TabRect;
};
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<TabHeaderInfo> 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
S32 mMinTabWidth; ///< Minimum Width a tab will display as.
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
S32 mTabMargin; ///< Margin left/right of tab text in tab
enum
{
TabSelected = 0, ///< Index of selected tab texture
TabNormal, ///< Index of normal tab texture
TabHover, ///< Index of hover tab texture
TabSelectedVertical, ///< Index of selected tab texture
TabNormalVertical, ///< Index of normal tab texture
TabHoverVertical, ///< Index of hover tab texture
TabBackground = 19, ///< Index of background texture (tiled)
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 offset the render offset to accomodate global coords
void renderTabs( const Point2I &offset );
/// 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 );
/// Page Rendering Routine
void renderBackground( Point2I offset, const RectI &updateRect );
void renderJustifiedTextRot(Point2I offset, Point2I extent, const char *text, F32 rot );
/// @}
/// @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 );
/// Select the Next page in the tab book
void selectNextPage();
/// Select the Previous page in the tab book
void selectPrevPage();
/// @}
/// @name Internal Utility Functions
/// @{
/// Update ourselves by hooking common GuiControl functionality.
void setUpdate();
/// Balance a top/bottom tab row
void balanceRow( S32 row, S32 totalTabWidth );
/// Balance a left/right tab column
void balanceColumn( S32 row, S32 totalTabWidth );
/// Checks to see if a tab option has changed and we need to resize children, resizes if necessary
void solveDirty();
/// Calculate the tab width of a page, given it's caption
S32 calculatePageTabWidth( GuiTabPageCtrl *page );
/// Calculate Page Header Information
void calculatePageTabs();
/// 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);
/// @}
virtual bool onKeyDown(const GuiEvent &event);
/// @name Mouse Events
/// @{
void onMouseDown(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
virtual bool onMouseDownEditor(const GuiEvent &event, Point2I offset);
/// @}
};
#endif //_GUI_TABBOOKCTRL_H_

View File

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

View File

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