Initial commit

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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