Initial commit
This commit is contained in:
91
engine/gui/containers/guiCtrlArrayCtrl.cc
Executable file
91
engine/gui/containers/guiCtrlArrayCtrl.cc
Executable file
@ -0,0 +1,91 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "gui/containers/guiCtrlArrayCtrl.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiControlArrayControl);
|
||||
|
||||
GuiControlArrayControl::GuiControlArrayControl()
|
||||
{
|
||||
mCols = 0;
|
||||
mRowSize = 30;
|
||||
mRowSpacing = 2;
|
||||
mColSpacing = 0;
|
||||
}
|
||||
|
||||
void GuiControlArrayControl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("colCount", TypeS32, Offset(mCols, GuiControlArrayControl));
|
||||
addField("colSizes", TypeS32Vector, Offset(mColumnSizes, GuiControlArrayControl));
|
||||
addField("rowSize", TypeS32, Offset(mRowSize, GuiControlArrayControl));
|
||||
addField("rowSpacing", TypeS32, Offset(mRowSpacing, GuiControlArrayControl));
|
||||
addField("colSpacing", TypeS32, Offset(mColSpacing, GuiControlArrayControl));
|
||||
}
|
||||
|
||||
bool GuiControlArrayControl::onWake()
|
||||
{
|
||||
if ( !Parent::onWake() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiControlArrayControl::onSleep()
|
||||
{
|
||||
Parent::onSleep();
|
||||
}
|
||||
|
||||
void GuiControlArrayControl::inspectPostApply()
|
||||
{
|
||||
resize(getPosition(), getExtent());
|
||||
}
|
||||
|
||||
void GuiControlArrayControl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
||||
{
|
||||
Parent::resize(newPosition, newExtent);
|
||||
|
||||
if(mCols < 1 || size() < 1)
|
||||
return;
|
||||
|
||||
S32 *sizes = new S32[mCols];
|
||||
S32 *offsets = new S32[mCols];
|
||||
S32 totalSize = 0;
|
||||
|
||||
// Calculate the column sizes
|
||||
for(S32 i=0; i<mCols; i++)
|
||||
{
|
||||
sizes[i] = mColumnSizes[i];
|
||||
offsets[i] = totalSize;
|
||||
|
||||
// If it's an auto-size one, then... auto-size...
|
||||
if(sizes[i] == -1)
|
||||
{
|
||||
sizes[i] = newExtent.x - totalSize;
|
||||
break;
|
||||
}
|
||||
|
||||
totalSize += sizes[i] + mColSpacing;
|
||||
}
|
||||
|
||||
// Now iterate through the children and resize them to fit the grid...
|
||||
for(S32 i=0; i<size(); i++)
|
||||
{
|
||||
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](i));
|
||||
|
||||
// Get the current column and row...
|
||||
S32 curCol = i % mCols;
|
||||
S32 curRow = i / mCols;
|
||||
|
||||
if(gc)
|
||||
{
|
||||
Point2I newPos(offsets[curCol], curRow * (mRowSize + mRowSpacing));
|
||||
Point2I newExtents(sizes[curCol], mRowSize);
|
||||
|
||||
gc->resize(newPos, newExtents);
|
||||
}
|
||||
}
|
||||
}
|
36
engine/gui/containers/guiCtrlArrayCtrl.h
Executable file
36
engine/gui/containers/guiCtrlArrayCtrl.h
Executable file
@ -0,0 +1,36 @@
|
||||
#ifndef _GUICTRLARRAYCTRL_H_
|
||||
#define _GUICTRLARRAYCTRL_H_
|
||||
|
||||
#ifndef _GUICONTROL_H_
|
||||
#include "gui/core/guiControl.h"
|
||||
#endif
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
class GuiControlArrayControl : public GuiControl
|
||||
{
|
||||
private:
|
||||
typedef GuiControl Parent;
|
||||
|
||||
S32 mCols;
|
||||
Vector<S32> mColumnSizes;
|
||||
S32 mRowSize;
|
||||
S32 mRowSpacing;
|
||||
S32 mColSpacing;
|
||||
|
||||
public:
|
||||
GuiControlArrayControl();
|
||||
|
||||
void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
|
||||
bool onWake();
|
||||
void onSleep();
|
||||
void inspectPostApply();
|
||||
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT(GuiControlArrayControl);
|
||||
};
|
||||
|
||||
#endif
|
1005
engine/gui/containers/guiFrameCtrl.cc
Executable file
1005
engine/gui/containers/guiFrameCtrl.cc
Executable file
File diff suppressed because it is too large
Load Diff
176
engine/gui/containers/guiFrameCtrl.h
Executable file
176
engine/gui/containers/guiFrameCtrl.h
Executable file
@ -0,0 +1,176 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A gui control allowing a window to be subdivided into panes,
|
||||
// each of which displays a gui control child of the
|
||||
// GuiFrameSetCtrl. Each gui control child will have an associated
|
||||
// FrameDetail through which frame-specific details can be
|
||||
// assigned. Frame-specific values override the values specified
|
||||
// for the entire frameset.
|
||||
//
|
||||
// Note that it is possible to have more children than frames,
|
||||
// or more frames than children. In the former case, the extra
|
||||
// children will not be visible (they are moved beyond the
|
||||
// visible extent of the frameset). In the latter case, frames
|
||||
// will be empty.
|
||||
//
|
||||
// If a frameset had two columns and two rows but only three
|
||||
// gui control children they would be assigned to frames as
|
||||
// follows:
|
||||
// 1 | 2
|
||||
// -----
|
||||
// 3 |
|
||||
//
|
||||
// The last frame would be blank.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GUIFRAMECTRL_H_
|
||||
#define _GUIFRAMECTRL_H_
|
||||
|
||||
#define DISABLE_COPY_CTOR(className) \
|
||||
className(const className &)
|
||||
|
||||
#define DISABLE_ASSIGNMENT(className) \
|
||||
className& operator=(const className &)
|
||||
|
||||
#ifndef _GUICONTROL_H_
|
||||
#include "gui/core/guiControl.h"
|
||||
#endif
|
||||
|
||||
// for debugging porpoises...
|
||||
#define GUI_FRAME_DEBUG
|
||||
// ...save the porpoises
|
||||
|
||||
class GuiFrameSetCtrl : public GuiControl
|
||||
{
|
||||
private:
|
||||
typedef GuiControl Parent;
|
||||
public:
|
||||
enum
|
||||
{
|
||||
FRAME_STATE_ON, // ON overrides OFF
|
||||
FRAME_STATE_OFF, // OFF overrides AUTO
|
||||
FRAME_STATE_AUTO, // AUTO == ON, unless overridden
|
||||
|
||||
NO_HIT = -1,
|
||||
|
||||
DEFAULT_BORDER_WIDTH = 4,
|
||||
DEFAULT_COLUMNS = 1,
|
||||
DEFAULT_ROWS = 1,
|
||||
DEFAULT_MIN_FRAME_EXTENT = 20
|
||||
};
|
||||
enum Region
|
||||
{
|
||||
VERTICAL_DIVIDER,
|
||||
HORIZONTAL_DIVIDER,
|
||||
DIVIDER_INTERSECTION,
|
||||
NONE
|
||||
};
|
||||
struct FrameDetail
|
||||
{
|
||||
U32 mBorderWidth;
|
||||
ColorI mBorderColor;
|
||||
S32 mBorderEnable;
|
||||
S32 mBorderMovable;
|
||||
Point2I mMinExtent;
|
||||
FrameDetail() { mBorderWidth = DEFAULT_BORDER_WIDTH; mBorderEnable = FRAME_STATE_AUTO; mBorderMovable = FRAME_STATE_AUTO; mMinExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT); }
|
||||
};
|
||||
DECLARE_CONOBJECT(GuiFrameSetCtrl);
|
||||
static void initPersistFields();
|
||||
|
||||
GuiFrameSetCtrl();
|
||||
GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[] = NULL, const U32 rowOffsets[] = NULL);
|
||||
virtual ~GuiFrameSetCtrl();
|
||||
|
||||
void addObject(SimObject *obj);
|
||||
void removeObject(SimObject *obj);
|
||||
|
||||
virtual void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
|
||||
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
|
||||
|
||||
virtual void onMouseDown(const GuiEvent &event);
|
||||
virtual void onMouseUp(const GuiEvent &event);
|
||||
virtual void onMouseDragged(const GuiEvent &event);
|
||||
virtual void onMouseEnter(const GuiEvent &event);
|
||||
virtual void onMouseLeave(const GuiEvent &event);
|
||||
|
||||
bool onAdd();
|
||||
void onRender(Point2I offset, const RectI &updateRect );
|
||||
protected:
|
||||
/* member variables */
|
||||
Vector<S32> mColumnOffsets;
|
||||
Vector<S32> mRowOffsets;
|
||||
GuiCursor *mMoveCursor;
|
||||
GuiCursor *mUpDownCursor;
|
||||
GuiCursor *mLeftRightCursor;
|
||||
GuiCursor *mDefaultCursor;
|
||||
FrameDetail mFramesetDetails;
|
||||
VectorPtr<FrameDetail *> mFrameDetails;
|
||||
bool mAutoBalance;
|
||||
S32 mFudgeFactor;
|
||||
|
||||
/* divider activation member variables */
|
||||
Region mCurHitRegion;
|
||||
Point2I mLocOnDivider;
|
||||
S32 mCurVerticalHit;
|
||||
S32 mCurHorizontalHit;
|
||||
|
||||
bool init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]);
|
||||
bool initCursors();
|
||||
|
||||
Region findHitRegion(const Point2I &point);
|
||||
Region pointInAnyRegion(const Point2I &point);
|
||||
S32 findResizableFrames(S32 indexes[]);
|
||||
bool hitVerticalDivider(S32 x, const Point2I &point);
|
||||
bool hitHorizontalDivider(S32 y, const Point2I &point);
|
||||
|
||||
void rebalance(const Point2I &newExtent);
|
||||
|
||||
void computeSizes(bool balanceFrames = false);
|
||||
void computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[]);
|
||||
|
||||
void drawDividers(const Point2I &offset);
|
||||
public:
|
||||
U32 columns() const { return(mColumnOffsets.size()); }
|
||||
U32 rows() const { return(mRowOffsets.size()); }
|
||||
U32 borderWidth() const { return(mFramesetDetails.mBorderWidth); }
|
||||
Vector<S32>* columnOffsets() { return(&mColumnOffsets); }
|
||||
Vector<S32>* rowOffsets() { return(&mRowOffsets); }
|
||||
FrameDetail* framesetDetails() { return(&mFramesetDetails); }
|
||||
|
||||
bool findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd);
|
||||
|
||||
void frameBorderEnable(S32 index, const char *state = NULL);
|
||||
void frameBorderMovable(S32 index, const char *state = NULL);
|
||||
void frameMinExtent(S32 index, const Point2I &extent);
|
||||
|
||||
void balanceFrames() { computeSizes(true); }
|
||||
void updateSizes() { computeSizes(); }
|
||||
|
||||
bool onWake();
|
||||
|
||||
private:
|
||||
DISABLE_COPY_CTOR(GuiFrameSetCtrl);
|
||||
DISABLE_ASSIGNMENT(GuiFrameSetCtrl);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// x is the first value inside the next column, so the divider x-coords
|
||||
// precede x.
|
||||
inline bool GuiFrameSetCtrl::hitVerticalDivider(S32 x, const Point2I &point)
|
||||
{
|
||||
return((point.x >= S32(x - mFramesetDetails.mBorderWidth)) && (point.x < x) && (point.y >= 0) && (point.y < S32(mBounds.extent.y)));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// y is the first value inside the next row, so the divider y-coords precede y.
|
||||
inline bool GuiFrameSetCtrl::hitHorizontalDivider(S32 y, const Point2I &point)
|
||||
{
|
||||
return((point.x >= 0) && (point.x < S32(mBounds.extent.x)) && (point.y >= S32(y - mFramesetDetails.mBorderWidth)) && (point.y < y));
|
||||
}
|
||||
|
||||
#endif // _GUI_FRAME_CTRL_H
|
310
engine/gui/containers/guiPaneCtrl.cc
Executable file
310
engine/gui/containers/guiPaneCtrl.cc
Executable file
@ -0,0 +1,310 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "gui/containers/guiPaneCtrl.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiPaneControl);
|
||||
|
||||
GuiPaneControl::GuiPaneControl()
|
||||
{
|
||||
mMinExtent.set(10, 10);
|
||||
mActive = true;
|
||||
mCollapsable = true;
|
||||
mCollapsed = false;
|
||||
mBarBehindText = true;
|
||||
mMouseOver = false;
|
||||
mDepressed = false;
|
||||
mOriginalExtents.set(10,10);
|
||||
mCaption = StringTable->insert("A Pane");
|
||||
mCaptionID = StringTable->insert("");
|
||||
}
|
||||
|
||||
void GuiPaneControl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("caption", TypeString, Offset(mCaption, GuiPaneControl));
|
||||
addField("captionID", TypeString, Offset(mCaptionID, GuiPaneControl));
|
||||
addField("collapsable", TypeBool, Offset(mCollapsable, GuiPaneControl));
|
||||
addField("barBehindText", TypeBool, Offset(mBarBehindText, GuiPaneControl));
|
||||
}
|
||||
|
||||
bool GuiPaneControl::onWake()
|
||||
{
|
||||
if ( !Parent::onWake() )
|
||||
return false;
|
||||
|
||||
mFont = mProfile->mFont;
|
||||
AssertFatal(mFont, "GuiPaneControl::onWake: invalid font in profile" );
|
||||
if(mCaptionID && *mCaptionID != 0)
|
||||
{
|
||||
setCaptionID(mCaptionID);
|
||||
}
|
||||
|
||||
mProfile->constructBitmapArray();
|
||||
|
||||
if(mProfile->mBitmapArrayRects.size())
|
||||
{
|
||||
mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
|
||||
mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
|
||||
|
||||
if(mFont->getHeight() > mThumbSize.y)
|
||||
mThumbSize.y = mFont->getHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
mThumbSize.set(20, 20);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiPaneControl::onSleep()
|
||||
{
|
||||
Parent::onSleep();
|
||||
mFont = NULL;
|
||||
}
|
||||
|
||||
void GuiPaneControl::setCaptionID(const char *id)
|
||||
{
|
||||
S32 n = Con::getIntVariable(id, -1);
|
||||
if(n != -1)
|
||||
{
|
||||
mCaptionID = StringTable->insert(id);
|
||||
setCaptionID(n);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiPaneControl::setCaptionID(S32 id)
|
||||
{
|
||||
const UTF8 *str = getGUIString(id);
|
||||
if(str)
|
||||
mCaption = StringTable->insert((const char*)str);
|
||||
}
|
||||
|
||||
void GuiPaneControl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
||||
{
|
||||
|
||||
//call set update both before and after
|
||||
setUpdate();
|
||||
Point2I actualNewExtent = Point2I(getMax(mMinExtent.x, newExtent.x),
|
||||
getMax(mMinExtent.y, newExtent.y));
|
||||
|
||||
mBounds.set(newPosition, actualNewExtent);
|
||||
mOriginalExtents.x = actualNewExtent.x;
|
||||
|
||||
GuiControl *parent = getParent();
|
||||
if (parent)
|
||||
parent->childResized(this);
|
||||
setUpdate();
|
||||
|
||||
// Resize the child control if we're not collapsed
|
||||
if(size() && !mCollapsed)
|
||||
{
|
||||
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
|
||||
|
||||
if(gc)
|
||||
{
|
||||
Point2I offset(0, mThumbSize.y);
|
||||
|
||||
gc->resize(offset, newExtent - offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiPaneControl::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
// Render our awesome little doogong
|
||||
if(mProfile->mBitmapArrayRects.size() >= 2 && mCollapsable)
|
||||
{
|
||||
S32 idx = mCollapsed ? 0 : 1;
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(
|
||||
mProfile->mTextureHandle,
|
||||
RectI(offset, mProfile->mBitmapArrayRects[idx].extent),
|
||||
mProfile->mBitmapArrayRects[idx]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
S32 textWidth = 0;
|
||||
|
||||
if(!mBarBehindText)
|
||||
{
|
||||
dglSetBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
|
||||
textWidth = dglDrawText(
|
||||
mFont,
|
||||
Point2I(mThumbSize.x, 0) + offset,
|
||||
mCaption,
|
||||
mProfile->mFontColors
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Draw our little bar, too
|
||||
if(mProfile->mBitmapArrayRects.size() >= 5)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
|
||||
S32 barStart = mThumbSize.x + offset.x + textWidth;
|
||||
S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2;
|
||||
|
||||
Point2I barOffset(barStart, barTop);
|
||||
|
||||
// Draw the start of the bar...
|
||||
dglDrawBitmapStretchSR(
|
||||
mProfile->mTextureHandle,
|
||||
RectI(barOffset, mProfile->mBitmapArrayRects[2].extent),
|
||||
mProfile->mBitmapArrayRects[2]
|
||||
);
|
||||
|
||||
// Now draw the middle...
|
||||
barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
|
||||
|
||||
S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x;
|
||||
|
||||
if(barMiddleSize>0)
|
||||
{
|
||||
// We have to do this inset to prevent nasty stretching artifacts
|
||||
RectI foo = mProfile->mBitmapArrayRects[3];
|
||||
foo.inset(1,0);
|
||||
|
||||
dglDrawBitmapStretchSR(
|
||||
mProfile->mTextureHandle,
|
||||
RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
|
||||
foo
|
||||
);
|
||||
}
|
||||
|
||||
// And the end
|
||||
barOffset.x += barMiddleSize;
|
||||
|
||||
dglDrawBitmapStretchSR(
|
||||
mProfile->mTextureHandle,
|
||||
RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
|
||||
mProfile->mBitmapArrayRects[4]
|
||||
);
|
||||
}
|
||||
|
||||
if(mBarBehindText)
|
||||
{
|
||||
dglSetBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
|
||||
dglDrawText(
|
||||
mFont,
|
||||
Point2I(mThumbSize.x, 0) + offset,
|
||||
mCaption,
|
||||
mProfile->mFontColors
|
||||
);
|
||||
}
|
||||
|
||||
// Draw child controls if appropriate
|
||||
if(!mCollapsed)
|
||||
renderChildControls(offset, updateRect);
|
||||
}
|
||||
|
||||
ConsoleMethod(GuiPaneControl, setCollapsed, void, 3, 3, "(bool)")
|
||||
{
|
||||
object->setCollapsed(dAtob(argv[2]));
|
||||
}
|
||||
|
||||
void GuiPaneControl::setCollapsed(bool isCollapsed)
|
||||
{
|
||||
// Get the child
|
||||
if(size() == 0 || !mCollapsable) return;
|
||||
|
||||
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
|
||||
|
||||
if(mCollapsed && !isCollapsed)
|
||||
{
|
||||
mCollapsed = false;
|
||||
|
||||
resize(getPosition(), mOriginalExtents);
|
||||
|
||||
if(gc)
|
||||
gc->setVisible(true);
|
||||
}
|
||||
else if(!mCollapsed && isCollapsed)
|
||||
{
|
||||
mCollapsed = true;
|
||||
|
||||
mOriginalExtents = getExtent();
|
||||
resize(getPosition(), Point2I(getExtent().x, mThumbSize.y));
|
||||
|
||||
if(gc)
|
||||
gc->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiPaneControl::onMouseMove(const GuiEvent &event)
|
||||
{
|
||||
Point2I localMove = globalToLocalCoord(event.mousePoint);
|
||||
|
||||
// If we're clicking in the header then resize
|
||||
mMouseOver = (localMove.y < mThumbSize.y);
|
||||
if(isMouseLocked())
|
||||
mDepressed = mMouseOver;
|
||||
|
||||
}
|
||||
|
||||
void GuiPaneControl::onMouseEnter(const GuiEvent &event)
|
||||
{
|
||||
setUpdate();
|
||||
if(isMouseLocked())
|
||||
{
|
||||
mDepressed = true;
|
||||
mMouseOver = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMouseOver = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GuiPaneControl::onMouseLeave(const GuiEvent &event)
|
||||
{
|
||||
setUpdate();
|
||||
if(isMouseLocked())
|
||||
mDepressed = false;
|
||||
mMouseOver = false;
|
||||
}
|
||||
|
||||
void GuiPaneControl::onMouseDown(const GuiEvent &event)
|
||||
{
|
||||
if(!mCollapsable)
|
||||
return;
|
||||
|
||||
Point2I localClick = globalToLocalCoord(event.mousePoint);
|
||||
|
||||
// If we're clicking in the header then resize
|
||||
if(localClick.y < mThumbSize.y)
|
||||
{
|
||||
mouseLock();
|
||||
mDepressed = true;
|
||||
|
||||
//update
|
||||
setUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GuiPaneControl::onMouseUp(const GuiEvent &event)
|
||||
{
|
||||
// Make sure we only get events we ought to be getting...
|
||||
if (! mActive)
|
||||
return;
|
||||
|
||||
if(!mCollapsable)
|
||||
return;
|
||||
|
||||
mouseUnlock();
|
||||
setUpdate();
|
||||
|
||||
Point2I localClick = globalToLocalCoord(event.mousePoint);
|
||||
|
||||
// If we're clicking in the header then resize
|
||||
if(localClick.y < mThumbSize.y && mDepressed)
|
||||
setCollapsed(!mCollapsed);
|
||||
}
|
65
engine/gui/containers/guiPaneCtrl.h
Executable file
65
engine/gui/containers/guiPaneCtrl.h
Executable file
@ -0,0 +1,65 @@
|
||||
|
||||
#ifndef _GUIPANECTRL_H_
|
||||
#define _GUIPANECTRL_H_
|
||||
|
||||
#ifndef _GUICONTROL_H_
|
||||
#include "gui/core/guiControl.h"
|
||||
#endif
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
/// Collapsable pane control.
|
||||
///
|
||||
/// This class wraps a single child control and displays a header with caption
|
||||
/// above it. If you click the header it will collapse or expand. The control
|
||||
/// resizes itself based on its collapsed/expanded size.
|
||||
///
|
||||
/// In the GUI editor, if you just want the header you can make collapsable
|
||||
/// false. The caption field lets you set the caption. It expects a bitmap
|
||||
/// (from the GuiControlProfile) that contains two images - the first is
|
||||
/// displayed when the control is expanded and the second is displayed when
|
||||
/// it is collapsed. The header is sized based off of the first image.
|
||||
class GuiPaneControl : public GuiControl
|
||||
{
|
||||
private:
|
||||
typedef GuiControl Parent;
|
||||
|
||||
Resource<GFont> mFont;
|
||||
bool mCollapsable;
|
||||
bool mCollapsed;
|
||||
bool mBarBehindText;
|
||||
Point2I mOriginalExtents;
|
||||
StringTableEntry mCaption;
|
||||
StringTableEntry mCaptionID;
|
||||
Point2I mThumbSize;
|
||||
|
||||
bool mMouseOver;
|
||||
bool mDepressed;
|
||||
|
||||
public:
|
||||
GuiPaneControl();
|
||||
|
||||
void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
void onRender(Point2I offset, const RectI &updateRect);
|
||||
|
||||
bool getCollapsed() { return mCollapsed; };
|
||||
void setCollapsed(bool isCollapsed);
|
||||
|
||||
bool onWake();
|
||||
void onSleep();
|
||||
|
||||
virtual void setCaptionID(S32 id);
|
||||
virtual void setCaptionID(const char *id);
|
||||
void onMouseDown(const GuiEvent &event);
|
||||
void onMouseUp(const GuiEvent &event);
|
||||
void onMouseMove(const GuiEvent &event);
|
||||
void onMouseLeave(const GuiEvent &event);
|
||||
void onMouseEnter(const GuiEvent &event);
|
||||
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT(GuiPaneControl);
|
||||
};
|
||||
|
||||
#endif
|
887
engine/gui/containers/guiScrollCtrl.cc
Executable file
887
engine/gui/containers/guiScrollCtrl.cc
Executable file
@ -0,0 +1,887 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/console.h"
|
||||
#include "dgl/gBitmap.h"
|
||||
#include "dgl/gTexManager.h"
|
||||
#include "core/resManager.h"
|
||||
#include "platform/event.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "gui/core/guiArrayCtrl.h"
|
||||
#include "gui/containers/guiScrollCtrl.h"
|
||||
#include "gui/core/guiDefaultControlRender.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiScrollCtrl);
|
||||
|
||||
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
|
||||
|
||||
GuiScrollCtrl::GuiScrollCtrl()
|
||||
{
|
||||
mBounds.extent.set(200,200);
|
||||
mChildMargin.set(0,0);
|
||||
mBorderThickness = 1;
|
||||
mScrollBarThickness = 16;
|
||||
mScrollBarArrowBtnLength = 16;
|
||||
stateDepressed = false;
|
||||
curHitRegion = None;
|
||||
|
||||
mWillFirstRespond = true;
|
||||
mUseConstantHeightThumb = false;
|
||||
|
||||
mForceVScrollBar = ScrollBarAlwaysOn;
|
||||
mForceHScrollBar = ScrollBarAlwaysOn;
|
||||
}
|
||||
|
||||
static EnumTable::Enums scrollBarEnums[] =
|
||||
{
|
||||
{ GuiScrollCtrl::ScrollBarAlwaysOn, "alwaysOn" },
|
||||
{ GuiScrollCtrl::ScrollBarAlwaysOff, "alwaysOff" },
|
||||
{ GuiScrollCtrl::ScrollBarDynamic, "dynamic" },
|
||||
};
|
||||
static EnumTable gScrollBarTable(3, &scrollBarEnums[0]);
|
||||
|
||||
ConsoleMethod(GuiScrollCtrl, scrollToTop, void, 2, 2, "() - scrolls the scroll control to the top of the child content area.")
|
||||
{
|
||||
argc; argv;
|
||||
object->scrollTo( 0, 0 );
|
||||
}
|
||||
|
||||
ConsoleMethod(GuiScrollCtrl, scrollToBottom, void, 2, 2, "() - scrolls the scroll control to the bottom of the child content area.")
|
||||
{
|
||||
argc; argv;
|
||||
object->scrollTo( 0, 0x7FFFFFFF );
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("willFirstRespond", TypeBool, Offset(mWillFirstRespond, GuiScrollCtrl));
|
||||
addField("hScrollBar", TypeEnum, Offset(mForceHScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
|
||||
addField("vScrollBar", TypeEnum, Offset(mForceVScrollBar, GuiScrollCtrl), 1, &gScrollBarTable);
|
||||
addField("constantThumbHeight", TypeBool, Offset(mUseConstantHeightThumb, GuiScrollCtrl));
|
||||
addField("childMargin", TypePoint2I, Offset(mChildMargin, GuiScrollCtrl));
|
||||
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt)
|
||||
{
|
||||
Parent::resize(newPos, newExt);
|
||||
computeSizes();
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::childResized(GuiControl *child)
|
||||
{
|
||||
Parent::childResized(child);
|
||||
computeSizes();
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::onWake()
|
||||
{
|
||||
if (! Parent::onWake())
|
||||
return false;
|
||||
|
||||
mTextureHandle = mProfile->mTextureHandle;
|
||||
mTextureHandle.setFilterNearest();
|
||||
|
||||
bool result = mProfile->constructBitmapArray() >= BmpStates * BmpCount;
|
||||
|
||||
AssertFatal(result, "Failed to create the bitmap array");
|
||||
|
||||
mBitmapBounds = mProfile->mBitmapArrayRects.address();
|
||||
|
||||
//init
|
||||
mBaseThumbSize = mBitmapBounds[BmpStates * BmpVThumbTopCap].extent.y +
|
||||
mBitmapBounds[BmpStates * BmpVThumbBottomCap].extent.y;
|
||||
|
||||
mScrollBarThickness = mBitmapBounds[BmpStates * BmpVPage].extent.x;
|
||||
mScrollBarArrowBtnLength = mBitmapBounds[BmpStates * BmpUp].extent.y;
|
||||
computeSizes();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::onSleep()
|
||||
{
|
||||
Parent::onSleep();
|
||||
mTextureHandle = NULL;
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::calcChildExtents()
|
||||
{
|
||||
// right now the scroll control really only deals well with a single
|
||||
// child control for its content
|
||||
if (!size())
|
||||
return false;
|
||||
|
||||
GuiControl *ctrl = (GuiControl *) front();
|
||||
mChildExt = ctrl->mBounds.extent;
|
||||
mChildPos = ctrl->mBounds.point;
|
||||
return true;
|
||||
}
|
||||
|
||||
RectI lastVisRect;
|
||||
|
||||
void GuiScrollCtrl::scrollRectVisible(RectI rect)
|
||||
{
|
||||
// rect is passed in virtual client space
|
||||
if(rect.extent.x > mContentExt.x)
|
||||
rect.extent.x = mContentExt.x;
|
||||
if(rect.extent.y > mContentExt.y)
|
||||
rect.extent.y = mContentExt.y;
|
||||
|
||||
// Determine the points bounding the requested rectangle
|
||||
Point2I rectUpperLeft = rect.point;
|
||||
Point2I rectLowerRight = rect.point + rect.extent;
|
||||
|
||||
lastVisRect = rect;
|
||||
|
||||
// Determine the points bounding the actual visible area...
|
||||
Point2I visUpperLeft = mChildRelPos;
|
||||
Point2I visLowerRight = mChildRelPos + mContentExt;
|
||||
Point2I delta(0,0);
|
||||
|
||||
// We basically try to make sure that first the top left of the given
|
||||
// rect is visible, and if it is, then that the bottom right is visible.
|
||||
|
||||
// Make sure the rectangle is visible along the X axis...
|
||||
if(rectUpperLeft.x < visUpperLeft.x)
|
||||
delta.x = rectUpperLeft.x - visUpperLeft.x;
|
||||
else if(rectLowerRight.x > visLowerRight.x)
|
||||
delta.x = rectLowerRight.x - visLowerRight.x;
|
||||
|
||||
// Make sure the rectangle is visible along the Y axis...
|
||||
if(rectUpperLeft.y < visUpperLeft.y)
|
||||
delta.y = rectUpperLeft.y - visUpperLeft.y;
|
||||
else if(rectLowerRight.y > visLowerRight.y)
|
||||
delta.y = rectLowerRight.y - visLowerRight.y;
|
||||
|
||||
// If we had any changes, scroll, otherwise don't.
|
||||
if(delta.x || delta.y)
|
||||
scrollDelta(delta.x, delta.y);
|
||||
}
|
||||
|
||||
|
||||
void GuiScrollCtrl::addObject(SimObject *object)
|
||||
{
|
||||
Parent::addObject(object);
|
||||
computeSizes();
|
||||
}
|
||||
|
||||
GuiControl* GuiScrollCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
|
||||
{
|
||||
if(pt.x < mProfile->mBorderThickness || pt.y < mProfile->mBorderThickness)
|
||||
return this;
|
||||
if(pt.x >= mBounds.extent.x - mProfile->mBorderThickness - (mHasVScrollBar ? mScrollBarThickness : 0) ||
|
||||
pt.y >= mBounds.extent.y - mProfile->mBorderThickness - (mHasHScrollBar ? mScrollBarThickness : 0))
|
||||
return this;
|
||||
return Parent::findHitControl(pt, initialLayer);
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::computeSizes()
|
||||
{
|
||||
S32 thickness = (mProfile ? mProfile->mBorderThickness : 1);
|
||||
Point2I borderExtent(thickness, thickness);
|
||||
mContentPos = borderExtent + mChildMargin;
|
||||
mContentExt = mBounds.extent - (mChildMargin * 2)
|
||||
- (borderExtent * 2);
|
||||
|
||||
Point2I childLowerRight;
|
||||
|
||||
mHBarEnabled = false;
|
||||
mVBarEnabled = false;
|
||||
mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn);
|
||||
mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn);
|
||||
|
||||
setUpdate();
|
||||
|
||||
if (calcChildExtents())
|
||||
{
|
||||
childLowerRight = mChildPos + mChildExt;
|
||||
|
||||
if (mHasVScrollBar)
|
||||
mContentExt.x -= mScrollBarThickness;
|
||||
if (mHasHScrollBar)
|
||||
mContentExt.y -= mScrollBarThickness;
|
||||
if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic))
|
||||
{
|
||||
mHasHScrollBar = true;
|
||||
mContentExt.y -= mScrollBarThickness;
|
||||
}
|
||||
if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic))
|
||||
{
|
||||
mHasVScrollBar = true;
|
||||
mContentExt.x -= mScrollBarThickness;
|
||||
// doh! ext.x changed, so check hscrollbar again
|
||||
if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic))
|
||||
{
|
||||
mHasHScrollBar = true;
|
||||
mContentExt.y -= mScrollBarThickness;
|
||||
}
|
||||
}
|
||||
Point2I contentLowerRight = mContentPos + mContentExt;
|
||||
|
||||
// see if the child controls need to be repositioned (null space in control)
|
||||
Point2I delta(0,0);
|
||||
|
||||
if (mChildPos.x > mContentPos.x)
|
||||
delta.x = mContentPos.x - mChildPos.x;
|
||||
else if (contentLowerRight.x > childLowerRight.x)
|
||||
{
|
||||
S32 diff = contentLowerRight.x - childLowerRight.x;
|
||||
delta.x = getMin(mContentPos.x - mChildPos.x, diff);
|
||||
}
|
||||
|
||||
//reposition the children if the child extent > the scroll content extent
|
||||
if (mChildPos.y > mContentPos.y)
|
||||
delta.y = mContentPos.y - mChildPos.y;
|
||||
else if (contentLowerRight.y > childLowerRight.y)
|
||||
{
|
||||
S32 diff = contentLowerRight.y - childLowerRight.y;
|
||||
delta.y = getMin(mContentPos.y - mChildPos.y, diff);
|
||||
}
|
||||
|
||||
// apply the deltas to the children...
|
||||
if (delta.x || delta.y)
|
||||
{
|
||||
SimGroup::iterator i;
|
||||
for(i = begin(); i != end();i++)
|
||||
{
|
||||
GuiControl *ctrl = (GuiControl *) (*i);
|
||||
ctrl->mBounds.point += delta;
|
||||
}
|
||||
mChildPos += delta;
|
||||
childLowerRight += delta;
|
||||
}
|
||||
// enable needed scroll bars
|
||||
if (mChildExt.x > mContentExt.x)
|
||||
mHBarEnabled = true;
|
||||
if (mChildExt.y > mContentExt.y)
|
||||
mVBarEnabled = true;
|
||||
mChildRelPos = mContentPos - mChildPos;
|
||||
}
|
||||
// build all the rectangles and such...
|
||||
calcScrollRects();
|
||||
calcThumbs();
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::calcScrollRects(void)
|
||||
{
|
||||
S32 thickness = ( mProfile ? mProfile->mBorderThickness : 1 );
|
||||
if (mHasHScrollBar)
|
||||
{
|
||||
mLeftArrowRect.set(thickness,
|
||||
mBounds.extent.y - thickness - mScrollBarThickness - 1,
|
||||
mScrollBarArrowBtnLength,
|
||||
mScrollBarThickness);
|
||||
|
||||
mRightArrowRect.set(mBounds.extent.x - thickness - (mHasVScrollBar ? mScrollBarThickness : 0) - mScrollBarArrowBtnLength,
|
||||
mBounds.extent.y - thickness - mScrollBarThickness - 1,
|
||||
mScrollBarArrowBtnLength,
|
||||
mScrollBarThickness);
|
||||
mHTrackRect.set(mLeftArrowRect.point.x + mLeftArrowRect.extent.x,
|
||||
mLeftArrowRect.point.y,
|
||||
mRightArrowRect.point.x - (mLeftArrowRect.point.x + mLeftArrowRect.extent.x),
|
||||
mScrollBarThickness);
|
||||
}
|
||||
if (mHasVScrollBar)
|
||||
{
|
||||
mUpArrowRect.set(mBounds.extent.x - thickness - mScrollBarThickness,
|
||||
thickness,
|
||||
mScrollBarThickness,
|
||||
mScrollBarArrowBtnLength);
|
||||
mDownArrowRect.set(mBounds.extent.x - thickness - mScrollBarThickness,
|
||||
mBounds.extent.y - thickness - mScrollBarArrowBtnLength - (mHasHScrollBar ? ( mScrollBarThickness + 1 ) : 0),
|
||||
mScrollBarThickness,
|
||||
mScrollBarArrowBtnLength);
|
||||
mVTrackRect.set(mUpArrowRect.point.x,
|
||||
mUpArrowRect.point.y + mUpArrowRect.extent.y,
|
||||
mScrollBarThickness,
|
||||
mDownArrowRect.point.y - (mUpArrowRect.point.y + mUpArrowRect.extent.y) );
|
||||
}
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::calcThumbs()
|
||||
{
|
||||
if (mHBarEnabled)
|
||||
{
|
||||
U32 trackSize = mHTrackRect.len_x();
|
||||
|
||||
if (mUseConstantHeightThumb)
|
||||
mHThumbSize = mBaseThumbSize;
|
||||
else
|
||||
mHThumbSize = getMax(mBaseThumbSize, S32((mContentExt.x * trackSize) / mChildExt.x));
|
||||
|
||||
mHThumbPos = mHTrackRect.point.x + (mChildRelPos.x * (trackSize - mHThumbSize)) / (mChildExt.x - mContentExt.x);
|
||||
}
|
||||
if (mVBarEnabled)
|
||||
{
|
||||
U32 trackSize = mVTrackRect.len_y();
|
||||
|
||||
if (mUseConstantHeightThumb)
|
||||
mVThumbSize = mBaseThumbSize;
|
||||
else
|
||||
mVThumbSize = getMax(mBaseThumbSize, S32((mContentExt.y * trackSize) / mChildExt.y));
|
||||
|
||||
mVThumbPos = mVTrackRect.point.y + (mChildRelPos.y * (trackSize - mVThumbSize)) / (mChildExt.y - mContentExt.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GuiScrollCtrl::scrollDelta(S32 deltaX, S32 deltaY)
|
||||
{
|
||||
scrollTo(mChildRelPos.x + deltaX, mChildRelPos.y + deltaY);
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::scrollTo(S32 x, S32 y)
|
||||
{
|
||||
if(!size())
|
||||
return;
|
||||
|
||||
setUpdate();
|
||||
if (x > mChildExt.x - mContentExt.x)
|
||||
x = mChildExt.x - mContentExt.x;
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
|
||||
if (y > mChildExt.y - mContentExt.y)
|
||||
y = mChildExt.y - mContentExt.y;
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
|
||||
Point2I delta(x - mChildRelPos.x, y - mChildRelPos.y);
|
||||
mChildRelPos += delta;
|
||||
mChildPos -= delta;
|
||||
|
||||
for(SimSet::iterator i = begin(); i != end();i++)
|
||||
{
|
||||
GuiControl *ctrl = (GuiControl *) (*i);
|
||||
ctrl->mBounds.point -= delta;
|
||||
}
|
||||
calcThumbs();
|
||||
}
|
||||
|
||||
GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I &pt)
|
||||
{
|
||||
if (mVBarEnabled && mHasVScrollBar)
|
||||
{
|
||||
if (mUpArrowRect.pointInRect(pt))
|
||||
return UpArrow;
|
||||
else if (mDownArrowRect.pointInRect(pt))
|
||||
return DownArrow;
|
||||
else if (mVTrackRect.pointInRect(pt))
|
||||
{
|
||||
if (pt.y < mVThumbPos)
|
||||
return UpPage;
|
||||
else if (pt.y < mVThumbPos + mVThumbSize)
|
||||
return VertThumb;
|
||||
else
|
||||
return DownPage;
|
||||
}
|
||||
}
|
||||
if (mHBarEnabled && mHasHScrollBar)
|
||||
{
|
||||
if (mLeftArrowRect.pointInRect(pt))
|
||||
return LeftArrow;
|
||||
else if (mRightArrowRect.pointInRect(pt))
|
||||
return RightArrow;
|
||||
else if (mHTrackRect.pointInRect(pt))
|
||||
{
|
||||
if (pt.x < mHThumbPos)
|
||||
return LeftPage;
|
||||
else if (pt.x < mHThumbPos + mHThumbSize)
|
||||
return HorizThumb;
|
||||
else
|
||||
return RightPage;
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::wantsTabListMembership()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::loseFirstResponder()
|
||||
{
|
||||
setUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::becomeFirstResponder()
|
||||
{
|
||||
setUpdate();
|
||||
return mWillFirstRespond;
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::onKeyDown(const GuiEvent &event)
|
||||
{
|
||||
if (mWillFirstRespond)
|
||||
{
|
||||
switch (event.keyCode)
|
||||
{
|
||||
case KEY_RIGHT:
|
||||
scrollByRegion(RightArrow);
|
||||
return true;
|
||||
|
||||
case KEY_LEFT:
|
||||
scrollByRegion(LeftArrow);
|
||||
return true;
|
||||
|
||||
case KEY_DOWN:
|
||||
scrollByRegion(DownArrow);
|
||||
return true;
|
||||
|
||||
case KEY_UP:
|
||||
scrollByRegion(UpArrow);
|
||||
return true;
|
||||
|
||||
case KEY_PAGE_UP:
|
||||
scrollByRegion(UpPage);
|
||||
return true;
|
||||
|
||||
case KEY_PAGE_DOWN:
|
||||
scrollByRegion(DownPage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return Parent::onKeyDown(event);
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::onMouseDown(const GuiEvent &event)
|
||||
{
|
||||
mouseLock();
|
||||
setFirstResponder();
|
||||
|
||||
setUpdate();
|
||||
|
||||
Point2I curMousePos = globalToLocalCoord(event.mousePoint);
|
||||
curHitRegion = findHitRegion(curMousePos);
|
||||
stateDepressed = true;
|
||||
|
||||
// Set a 0.5 second delay before we start scrolling
|
||||
mLastUpdated = Platform::getVirtualMilliseconds() + 500;
|
||||
|
||||
scrollByRegion(curHitRegion);
|
||||
|
||||
if (curHitRegion == VertThumb)
|
||||
{
|
||||
mChildRelPosAnchor = mChildRelPos;
|
||||
mThumbMouseDelta = curMousePos.y - mVThumbPos;
|
||||
}
|
||||
else if (curHitRegion == HorizThumb)
|
||||
{
|
||||
mChildRelPosAnchor = mChildRelPos;
|
||||
mThumbMouseDelta = curMousePos.x - mHThumbPos;
|
||||
}
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::onMouseUp(const GuiEvent &)
|
||||
{
|
||||
mouseUnlock();
|
||||
|
||||
setUpdate();
|
||||
|
||||
curHitRegion = None;
|
||||
stateDepressed = false;
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::onMouseDragged(const GuiEvent &event)
|
||||
{
|
||||
Point2I curMousePos = globalToLocalCoord(event.mousePoint);
|
||||
setUpdate();
|
||||
|
||||
if ( (curHitRegion != VertThumb) && (curHitRegion != HorizThumb) )
|
||||
{
|
||||
Region hit = findHitRegion(curMousePos);
|
||||
if (hit != curHitRegion)
|
||||
stateDepressed = false;
|
||||
else
|
||||
stateDepressed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// ok... if the mouse is 'near' the scroll bar, scroll with it
|
||||
// otherwise, snap back to the previous position.
|
||||
|
||||
if (curHitRegion == VertThumb)
|
||||
{
|
||||
if (curMousePos.x >= mVTrackRect.point.x - mScrollBarThickness &&
|
||||
curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarThickness &&
|
||||
curMousePos.y >= mVTrackRect.point.y - mScrollBarThickness &&
|
||||
curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarThickness)
|
||||
{
|
||||
S32 newVThumbPos = curMousePos.y - mThumbMouseDelta;
|
||||
if(newVThumbPos != mVThumbPos)
|
||||
{
|
||||
S32 newVPos = (newVThumbPos - mVTrackRect.point.y) *
|
||||
(mChildExt.y - mContentExt.y) /
|
||||
(mVTrackRect.extent.y - mVThumbSize);
|
||||
|
||||
scrollTo(mChildRelPosAnchor.x, newVPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
|
||||
}
|
||||
else if (curHitRegion == HorizThumb)
|
||||
{
|
||||
if (curMousePos.x >= mHTrackRect.point.x - mScrollBarThickness &&
|
||||
curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarThickness &&
|
||||
curMousePos.y >= mHTrackRect.point.y - mScrollBarThickness &&
|
||||
curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarThickness)
|
||||
{
|
||||
S32 newHThumbPos = curMousePos.x - mThumbMouseDelta;
|
||||
if(newHThumbPos != mHThumbPos)
|
||||
{
|
||||
S32 newHPos = (newHThumbPos - mHTrackRect.point.x) *
|
||||
(mChildExt.x - mContentExt.x) /
|
||||
(mHTrackRect.extent.x - mHThumbSize);
|
||||
|
||||
scrollTo(newHPos, mChildRelPosAnchor.y);
|
||||
}
|
||||
}
|
||||
else
|
||||
scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y);
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::onMouseWheelUp(const GuiEvent &event)
|
||||
{
|
||||
if ( !mAwake || !mVisible )
|
||||
return( false );
|
||||
|
||||
scrollByRegion((event.modifier & SI_CTRL) ? UpPage : UpArrow);
|
||||
|
||||
// Tell the kids that the mouse moved (relatively):
|
||||
iterator itr;
|
||||
for ( itr = begin(); itr != end(); itr++ )
|
||||
{
|
||||
GuiControl* grandKid = static_cast<GuiControl*>( *itr );
|
||||
grandKid->onMouseMove( event );
|
||||
}
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event)
|
||||
{
|
||||
if ( !mAwake || !mVisible )
|
||||
return( false );
|
||||
|
||||
scrollByRegion((event.modifier & SI_CTRL) ? DownPage : DownArrow);
|
||||
|
||||
// Tell the kids that the mouse moved (relatively):
|
||||
iterator itr;
|
||||
for ( itr = begin(); itr != end(); itr++ )
|
||||
{
|
||||
GuiControl* grandKid = static_cast<GuiControl *>( *itr );
|
||||
grandKid->onMouseMove( event );
|
||||
}
|
||||
|
||||
return( true );
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::onPreRender()
|
||||
{
|
||||
Parent::onPreRender();
|
||||
|
||||
// Short circuit if not depressed to save cycles
|
||||
if( stateDepressed != true )
|
||||
return;
|
||||
|
||||
//default to one second, though it shouldn't be necessary
|
||||
U32 timeThreshold = 1000;
|
||||
|
||||
// We don't want to scroll by pages at an interval the same as when we're scrolling
|
||||
// using the arrow buttons, so adjust accordingly.
|
||||
switch( curHitRegion )
|
||||
{
|
||||
case UpPage:
|
||||
case DownPage:
|
||||
case LeftPage:
|
||||
case RightPage:
|
||||
timeThreshold = 200;
|
||||
break;
|
||||
case UpArrow:
|
||||
case DownArrow:
|
||||
case LeftArrow:
|
||||
case RightArrow:
|
||||
timeThreshold = 20;
|
||||
break;
|
||||
default:
|
||||
// Neither a button or a page, don't scroll (shouldn't get here)
|
||||
return;
|
||||
break;
|
||||
};
|
||||
|
||||
S32 timeElapsed = Platform::getVirtualMilliseconds() - mLastUpdated;
|
||||
|
||||
if ( ( timeElapsed > 0 ) && ( timeElapsed > timeThreshold ) )
|
||||
{
|
||||
|
||||
mLastUpdated = Platform::getVirtualMilliseconds();
|
||||
scrollByRegion(curHitRegion);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::scrollByRegion(Region reg)
|
||||
{
|
||||
setUpdate();
|
||||
if(!size())
|
||||
return;
|
||||
GuiControl *content = (GuiControl *) front();
|
||||
U32 rowHeight, columnWidth;
|
||||
U32 pageHeight, pageWidth;
|
||||
|
||||
content->getScrollLineSizes(&rowHeight, &columnWidth);
|
||||
|
||||
if(rowHeight >= mContentExt.y)
|
||||
pageHeight = 1;
|
||||
else
|
||||
pageHeight = mContentExt.y - rowHeight;
|
||||
|
||||
if(columnWidth >= mContentExt.x)
|
||||
pageWidth = 1;
|
||||
else
|
||||
pageWidth = mContentExt.x - columnWidth;
|
||||
|
||||
if (mVBarEnabled)
|
||||
{
|
||||
switch(reg)
|
||||
{
|
||||
case UpPage:
|
||||
scrollDelta(0, -pageHeight);
|
||||
break;
|
||||
case DownPage:
|
||||
scrollDelta(0, pageHeight);
|
||||
break;
|
||||
case UpArrow:
|
||||
scrollDelta(0, -rowHeight);
|
||||
break;
|
||||
case DownArrow:
|
||||
scrollDelta(0, rowHeight);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mHBarEnabled)
|
||||
{
|
||||
switch(reg)
|
||||
{
|
||||
case LeftPage:
|
||||
scrollDelta(-pageWidth, 0);
|
||||
break;
|
||||
case RightPage:
|
||||
scrollDelta(pageWidth, 0);
|
||||
break;
|
||||
case LeftArrow:
|
||||
scrollDelta(-columnWidth, 0);
|
||||
break;
|
||||
case RightArrow:
|
||||
scrollDelta(columnWidth, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
RectI r(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y);
|
||||
|
||||
if (mProfile->mOpaque)
|
||||
dglDrawRectFill(r, mProfile->mFillColor);
|
||||
|
||||
if (mProfile->mBorder)
|
||||
renderFilledBorder(r, mProfile);
|
||||
|
||||
// draw scroll bars
|
||||
if (mHasVScrollBar)
|
||||
drawVScrollBar(offset);
|
||||
|
||||
if (mHasHScrollBar)
|
||||
drawHScrollBar(offset);
|
||||
|
||||
//draw the scroll corner
|
||||
if (mHasVScrollBar && mHasHScrollBar)
|
||||
drawScrollCorner(offset);
|
||||
|
||||
// draw content controls
|
||||
// create a rect to intersect with the updateRect
|
||||
RectI contentRect(mContentPos.x + offset.x, mContentPos.y + offset.y, mContentExt.x, mContentExt.y);
|
||||
if(contentRect.intersect(updateRect))
|
||||
renderChildControls(offset, contentRect);
|
||||
|
||||
// Finally draw the last vis rect (debug aid, BJG)
|
||||
//RectI renderRect = lastVisRect;
|
||||
//renderRect.point += mContentPos + offset;
|
||||
|
||||
//dglSetClipRect(r);
|
||||
//dglDrawRect(renderRect, ColorI(0, 255, 0));
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::drawBorder( const Point2I &offset, bool /*isFirstResponder*/ )
|
||||
{
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::drawVScrollBar(const Point2I &offset)
|
||||
{
|
||||
Point2I pos = offset + mUpArrowRect.point;
|
||||
S32 bitmap = (mVBarEnabled ? ((curHitRegion == UpArrow && stateDepressed) ?
|
||||
BmpStates * BmpUp + BmpHilite : BmpStates * BmpUp) : BmpStates * BmpUp + BmpDisabled);
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
|
||||
|
||||
pos.y += mScrollBarArrowBtnLength;
|
||||
S32 end;
|
||||
if (mVBarEnabled)
|
||||
end = mVThumbPos + offset.y;
|
||||
else
|
||||
end = mDownArrowRect.point.y + offset.y;
|
||||
|
||||
bitmap = (mVBarEnabled ? ((curHitRegion == DownPage && stateDepressed) ?
|
||||
BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage) : BmpStates * BmpVPage + BmpDisabled);
|
||||
|
||||
if (end > pos.y)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
|
||||
}
|
||||
|
||||
pos.y = end;
|
||||
if (mVBarEnabled)
|
||||
{
|
||||
bool thumbSelected = (curHitRegion == VertThumb && stateDepressed);
|
||||
S32 ttop = (thumbSelected ? BmpStates * BmpVThumbTopCap + BmpHilite : BmpStates * BmpVThumbTopCap);
|
||||
S32 tmid = (thumbSelected ? BmpStates * BmpVThumb + BmpHilite : BmpStates * BmpVThumb);
|
||||
S32 tbot = (thumbSelected ? BmpStates * BmpVThumbBottomCap + BmpHilite : BmpStates * BmpVThumbBottomCap);
|
||||
|
||||
// draw the thumb
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
|
||||
pos.y += mBitmapBounds[ttop].extent.y;
|
||||
end = mVThumbPos + mVThumbSize - mBitmapBounds[tbot].extent.y + offset.y;
|
||||
|
||||
if (end > pos.y)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[tmid].extent.x, end - pos.y)), mBitmapBounds[tmid]);
|
||||
}
|
||||
|
||||
pos.y = end;
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
|
||||
pos.y += mBitmapBounds[tbot].extent.y;
|
||||
end = mVTrackRect.point.y + mVTrackRect.extent.y - 1 + offset.y;
|
||||
|
||||
bitmap = (curHitRegion == DownPage && stateDepressed) ? BmpStates * BmpVPage + BmpHilite : BmpStates * BmpVPage;
|
||||
if (end > pos.y)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(mBitmapBounds[bitmap].extent.x, end - pos.y)), mBitmapBounds[bitmap]);
|
||||
}
|
||||
|
||||
pos.y = end;
|
||||
}
|
||||
|
||||
bitmap = (mVBarEnabled ? ((curHitRegion == DownArrow && stateDepressed ) ?
|
||||
BmpStates * BmpDown + BmpHilite : BmpStates * BmpDown) : BmpStates * BmpDown + BmpDisabled);
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::drawHScrollBar(const Point2I &offset)
|
||||
{
|
||||
S32 bitmap;
|
||||
|
||||
//draw the left arrow
|
||||
bitmap = (mHBarEnabled ? ((curHitRegion == LeftArrow && stateDepressed) ?
|
||||
BmpStates * BmpLeft + BmpHilite : BmpStates * BmpLeft) : BmpStates * BmpLeft + BmpDisabled);
|
||||
|
||||
Point2I pos = offset;
|
||||
pos += mLeftArrowRect.point;
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
|
||||
|
||||
pos.x += mLeftArrowRect.extent.x;
|
||||
|
||||
//draw the left page
|
||||
S32 end;
|
||||
if (mHBarEnabled)
|
||||
end = mHThumbPos + offset.x;
|
||||
else
|
||||
end = mRightArrowRect.point.x + offset.x;
|
||||
|
||||
bitmap = (mHBarEnabled ? ((curHitRegion == LeftPage && stateDepressed) ?
|
||||
BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage) : BmpStates * BmpHPage + BmpDisabled);
|
||||
|
||||
if (end > pos.x)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
|
||||
}
|
||||
pos.x = end;
|
||||
|
||||
//draw the thumb and the rightPage
|
||||
if (mHBarEnabled)
|
||||
{
|
||||
bool thumbSelected = (curHitRegion == HorizThumb && stateDepressed);
|
||||
S32 ttop = (thumbSelected ? BmpStates * BmpHThumbLeftCap + BmpHilite : BmpStates * BmpHThumbLeftCap );
|
||||
S32 tmid = (thumbSelected ? BmpStates * BmpHThumb + BmpHilite : BmpStates * BmpHThumb);
|
||||
S32 tbot = (thumbSelected ? BmpStates * BmpHThumbRightCap + BmpHilite : BmpStates * BmpHThumbRightCap);
|
||||
|
||||
// draw the thumb
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[ttop]);
|
||||
pos.x += mBitmapBounds[ttop].extent.x;
|
||||
end = mHThumbPos + mHThumbSize - mBitmapBounds[tbot].extent.x + offset.x;
|
||||
if (end > pos.x)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[tmid].extent.y)), mBitmapBounds[tmid]);
|
||||
}
|
||||
|
||||
pos.x = end;
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[tbot]);
|
||||
pos.x += mBitmapBounds[tbot].extent.x;
|
||||
end = mHTrackRect.point.x + mHTrackRect.extent.x - 1 + offset.x;
|
||||
|
||||
bitmap = ((curHitRegion == RightPage && stateDepressed) ? BmpStates * BmpHPage + BmpHilite : BmpStates * BmpHPage);
|
||||
|
||||
if (end > pos.x)
|
||||
{
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapStretchSR(mTextureHandle, RectI(pos, Point2I(end - pos.x, mBitmapBounds[bitmap].extent.y)), mBitmapBounds[bitmap]);
|
||||
}
|
||||
|
||||
pos.x = end;
|
||||
}
|
||||
bitmap = (mHBarEnabled ? ((curHitRegion == RightArrow && stateDepressed) ?
|
||||
BmpStates * BmpRight + BmpHilite : BmpStates * BmpRight) : BmpStates * BmpRight + BmpDisabled);
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[bitmap]);
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::drawScrollCorner(const Point2I &offset)
|
||||
{
|
||||
Point2I pos = offset;
|
||||
pos.x += mRightArrowRect.point.x + mRightArrowRect.extent.x;
|
||||
pos.y += mRightArrowRect.point.y;
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, pos, mBitmapBounds[BmpStates * BmpResize]);
|
||||
}
|
||||
|
||||
void GuiScrollCtrl::autoScroll(Region reg)
|
||||
{
|
||||
scrollByRegion(reg);
|
||||
}
|
201
engine/gui/containers/guiScrollCtrl.h
Executable file
201
engine/gui/containers/guiScrollCtrl.h
Executable file
@ -0,0 +1,201 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GUISCROLLCTRL_H_
|
||||
#define _GUISCROLLCTRL_H_
|
||||
|
||||
#ifndef _GUICONTROL_H_
|
||||
#include "gui/core/guiControl.h"
|
||||
#endif
|
||||
|
||||
class GuiScrollCtrl : public GuiControl
|
||||
{
|
||||
private:
|
||||
typedef GuiControl Parent;
|
||||
|
||||
protected:
|
||||
|
||||
// the scroll control uses a bitmap array to draw all its
|
||||
// graphics... these are the bitmaps it needs:
|
||||
enum BitmapIndices
|
||||
{
|
||||
BmpUp,
|
||||
BmpDown,
|
||||
BmpVThumbTopCap,
|
||||
BmpVThumb,
|
||||
BmpVThumbBottomCap,
|
||||
BmpVPage,
|
||||
BmpLeft,
|
||||
BmpRight,
|
||||
BmpHThumbLeftCap,
|
||||
BmpHThumb,
|
||||
BmpHThumbRightCap,
|
||||
BmpHPage,
|
||||
BmpResize,
|
||||
|
||||
BmpCount
|
||||
};
|
||||
|
||||
enum BitmapStates
|
||||
{
|
||||
BmpDefault = 0,
|
||||
BmpHilite,
|
||||
BmpDisabled,
|
||||
|
||||
BmpStates
|
||||
};
|
||||
RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
|
||||
TextureHandle mTextureHandle;
|
||||
|
||||
S32 mBorderThickness; // this gets set per class in the constructor
|
||||
Point2I mChildMargin; // the thickeness of the margin around the child controls
|
||||
|
||||
// note - it is implicit in the scroll view that the buttons all have the same
|
||||
// arrow length and that horizontal and vertical scroll bars have the
|
||||
// same thickness
|
||||
|
||||
S32 mScrollBarThickness; // determined by the width of the vertical page bmp
|
||||
S32 mScrollBarArrowBtnLength; // determined by the height of the up arrow
|
||||
|
||||
bool mHBarEnabled;
|
||||
bool mVBarEnabled;
|
||||
bool mHasHScrollBar;
|
||||
bool mHasVScrollBar;
|
||||
|
||||
Point2I mContentPos; // the position of the content region in the control's coord system
|
||||
Point2I mContentExt; // the extent of the content region
|
||||
|
||||
Point2I mChildPos; // the position of the upper left corner of the child control(s)
|
||||
Point2I mChildExt;
|
||||
|
||||
Point2I mChildRelPos; // the relative position of the upper left content corner in
|
||||
// the child's coordinate system - 0,0 if scrolled all the way to upper left.
|
||||
|
||||
//--------------------------------------
|
||||
// for mouse dragging the thumb
|
||||
Point2I mChildRelPosAnchor; // the original childRelPos when scrolling started
|
||||
S32 mThumbMouseDelta;
|
||||
|
||||
S32 mLastUpdated;
|
||||
|
||||
S32 mHThumbSize;
|
||||
S32 mHThumbPos;
|
||||
|
||||
S32 mVThumbSize;
|
||||
S32 mVThumbPos;
|
||||
|
||||
S32 mBaseThumbSize;
|
||||
|
||||
RectI mUpArrowRect;
|
||||
RectI mDownArrowRect;
|
||||
RectI mLeftArrowRect;
|
||||
RectI mRightArrowRect;
|
||||
RectI mHTrackRect;
|
||||
RectI mVTrackRect;
|
||||
|
||||
//--------------------------------------
|
||||
// for determing hit area
|
||||
public: //called by the ComboPopUp class
|
||||
enum Region
|
||||
{
|
||||
UpArrow,
|
||||
DownArrow,
|
||||
LeftArrow,
|
||||
RightArrow,
|
||||
UpPage,
|
||||
DownPage,
|
||||
LeftPage,
|
||||
RightPage,
|
||||
VertThumb,
|
||||
HorizThumb,
|
||||
None
|
||||
};
|
||||
enum {
|
||||
ScrollBarAlwaysOn = 0,
|
||||
ScrollBarAlwaysOff = 1,
|
||||
ScrollBarDynamic = 2
|
||||
};
|
||||
|
||||
bool stateDepressed;
|
||||
Region curHitRegion;
|
||||
|
||||
bool disabled;
|
||||
S32 mForceHScrollBar;
|
||||
S32 mForceVScrollBar;
|
||||
|
||||
bool mUseConstantHeightThumb;
|
||||
bool mWillFirstRespond; // for automatically handling arrow keys
|
||||
|
||||
Region findHitRegion(const Point2I &);
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool calcChildExtents();
|
||||
virtual void calcScrollRects(void);
|
||||
void calcThumbs();
|
||||
void scrollByRegion(Region reg);
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
public:
|
||||
GuiScrollCtrl();
|
||||
DECLARE_CONOBJECT(GuiScrollCtrl);
|
||||
static void initPersistFields();
|
||||
void autoScroll(Region reg);
|
||||
|
||||
void scrollTo(S32 x, S32 y);
|
||||
void scrollDelta(S32 x, S32 y);
|
||||
void scrollRectVisible(RectI rect);
|
||||
|
||||
void computeSizes();
|
||||
|
||||
// you can change the bitmap array dynamically.
|
||||
void loadBitmapArray();
|
||||
|
||||
void addObject(SimObject *obj);
|
||||
void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
void childResized(GuiControl *child);
|
||||
Point2I getChildPos() { return mChildPos; }
|
||||
Point2I getChildExtent() { return mChildExt; }
|
||||
Point2I getContentExtent() { return mContentExt; }
|
||||
S32 getBorderThickness(void) { return mBorderThickness; }
|
||||
S32 scrollBarThickness() const { return(mScrollBarThickness); }
|
||||
S32 scrollBarArrowBtnLength() const { return(mScrollBarArrowBtnLength); }
|
||||
bool hasHScrollBar() const { return(mHasHScrollBar); }
|
||||
bool hasVScrollBar() const { return(mHasVScrollBar); }
|
||||
bool enabledHScrollBar() const { return(mHBarEnabled); }
|
||||
bool enabledVScrollBar() const { return(mVBarEnabled); }
|
||||
|
||||
bool isScrolledToBottom() { return mChildPos.y + mChildExt.y <= mContentPos.y + mContentExt.y; }
|
||||
|
||||
bool wantsTabListMembership();
|
||||
bool becomeFirstResponder();
|
||||
bool loseFirstResponder();
|
||||
|
||||
Region getCurHitRegion(void) { return curHitRegion; }
|
||||
|
||||
bool onKeyDown(const GuiEvent &event);
|
||||
void onMouseDown(const GuiEvent &event);
|
||||
void onMouseRepeat(const GuiEvent &event);
|
||||
void onMouseUp(const GuiEvent &event);
|
||||
void onMouseDragged(const GuiEvent &event);
|
||||
bool onMouseWheelUp(const GuiEvent &event);
|
||||
bool onMouseWheelDown(const GuiEvent &event);
|
||||
|
||||
bool onWake();
|
||||
void onSleep();
|
||||
|
||||
void onPreRender();
|
||||
void onRender(Point2I offset, const RectI &updateRect);
|
||||
virtual void drawBorder(const Point2I &offset, bool isFirstResponder);
|
||||
virtual void drawVScrollBar(const Point2I &offset);
|
||||
virtual void drawHScrollBar(const Point2I &offset);
|
||||
virtual void drawScrollCorner(const Point2I &offset);
|
||||
virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
|
||||
};
|
||||
|
||||
#endif //_GUI_SCROLL_CTRL_H
|
165
engine/gui/containers/guiStackCtrl.cc
Executable file
165
engine/gui/containers/guiStackCtrl.cc
Executable file
@ -0,0 +1,165 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "gui/containers/guiStackCtrl.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiStackControl);
|
||||
|
||||
GuiStackControl::GuiStackControl()
|
||||
{
|
||||
mMinExtent.set(60, 60);
|
||||
mResizing = false;
|
||||
mStackFromBottom = false;
|
||||
mPadding = 0;
|
||||
}
|
||||
|
||||
void GuiStackControl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("stackFromBottom", TypeBool, Offset(mStackFromBottom, GuiStackControl));
|
||||
addField("padding", TypeF32, Offset(mPadding, GuiStackControl));
|
||||
}
|
||||
|
||||
bool GuiStackControl::onWake()
|
||||
{
|
||||
if ( !Parent::onWake() )
|
||||
return false;
|
||||
|
||||
updatePanes();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiStackControl::onSleep()
|
||||
{
|
||||
Parent::onSleep();
|
||||
}
|
||||
|
||||
void GuiStackControl::updatePanes()
|
||||
{
|
||||
// Prevent recursion
|
||||
if(mResizing) return;
|
||||
|
||||
mResizing = true;
|
||||
|
||||
// Store the sum of the heights of our controls.
|
||||
S32 totalHeight=0;
|
||||
|
||||
Point2I curPos;
|
||||
|
||||
if(mStackFromBottom)
|
||||
{
|
||||
// If we go from the bottom, things are basically the same...
|
||||
// except we have to assign positions in an arse backwards way
|
||||
// (literally :P)
|
||||
|
||||
// Figure out how high everything is going to be...
|
||||
// Place each child...
|
||||
for(S32 i=0; i<size(); i++)
|
||||
{
|
||||
// Place control
|
||||
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](i));
|
||||
|
||||
if(gc)
|
||||
{
|
||||
Point2I childExtent = gc->getExtent();
|
||||
totalHeight += childExtent.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out where we're starting...
|
||||
curPos = getPosition();
|
||||
curPos.y += totalHeight;
|
||||
|
||||
// Offset so the bottom control goes in the right place...
|
||||
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](size()-1));
|
||||
if(gc)
|
||||
curPos.y -= gc->getExtent().y;
|
||||
|
||||
|
||||
// Now work up from there!
|
||||
for(S32 i=size()-1; i>=0; i--)
|
||||
{
|
||||
// Place control
|
||||
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](i));
|
||||
|
||||
if(gc)
|
||||
{
|
||||
// We must place the child...
|
||||
|
||||
// Make it have our width but keep its height
|
||||
Point2I childExtent = gc->getExtent();
|
||||
|
||||
// Update our state...
|
||||
curPos.y -= childExtent.y - mPadding;
|
||||
|
||||
// And resize...
|
||||
gc->resize(curPos - getPosition(), Point2I(getExtent().x, childExtent.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position and resize everything...
|
||||
curPos = getPosition();
|
||||
|
||||
// Place each child...
|
||||
for(S32 i=0; i<size(); i++)
|
||||
{
|
||||
// Place control
|
||||
GuiControl * gc = dynamic_cast<GuiControl*>(operator [](i));
|
||||
|
||||
if(gc)
|
||||
{
|
||||
// We must place the child...
|
||||
|
||||
// Make it have our width but keep its height
|
||||
Point2I childExtent = gc->getExtent();
|
||||
|
||||
gc->resize(curPos - getPosition(), Point2I(getExtent().x, childExtent.y));
|
||||
|
||||
// Update our state...
|
||||
curPos.y += childExtent.y + mPadding;
|
||||
totalHeight += childExtent.y + mPadding;
|
||||
}
|
||||
}
|
||||
|
||||
// Conform our size to the sum of the child sizes.
|
||||
curPos.x = getExtent().x;
|
||||
curPos.y = totalHeight;
|
||||
resize(getPosition(), curPos);
|
||||
}
|
||||
|
||||
mResizing = false;
|
||||
}
|
||||
|
||||
void GuiStackControl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
||||
{
|
||||
//call set update both before and after
|
||||
setUpdate();
|
||||
Point2I actualNewExtent = Point2I( getMax(mMinExtent.x, newExtent.x),
|
||||
getMax(mMinExtent.y, newExtent.y));
|
||||
mBounds.set(newPosition, actualNewExtent);
|
||||
|
||||
GuiControl *parent = getParent();
|
||||
if (parent)
|
||||
parent->childResized(this);
|
||||
setUpdate();
|
||||
|
||||
updatePanes();
|
||||
}
|
||||
|
||||
void GuiStackControl::addObject(SimObject *obj)
|
||||
{
|
||||
Parent::addObject(obj);
|
||||
|
||||
updatePanes();
|
||||
}
|
||||
|
||||
void GuiStackControl::childResized(GuiControl *child)
|
||||
{
|
||||
updatePanes();
|
||||
}
|
49
engine/gui/containers/guiStackCtrl.h
Executable file
49
engine/gui/containers/guiStackCtrl.h
Executable file
@ -0,0 +1,49 @@
|
||||
#ifndef _GUISTACKCTRL_H_
|
||||
#define _GUISTACKCTRL_H_
|
||||
|
||||
#ifndef _GUICONTROL_H_
|
||||
#include "gui/core/guiControl.h"
|
||||
#endif
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
/// A vertical stack of GUI controls.
|
||||
///
|
||||
/// This maintains a vertical stack of GUI controls. If one is deleted, or
|
||||
/// resized, then the stack is resized to fit. The order of the stack is
|
||||
/// determined by the internal order of the children (ie, order of addition).
|
||||
///
|
||||
/// Children set their own height but are constrained to the width of the
|
||||
/// stack control.
|
||||
///
|
||||
/// @todo Make this support horizontal stacks.
|
||||
class GuiStackControl : public GuiControl
|
||||
{
|
||||
private:
|
||||
typedef GuiControl Parent;
|
||||
bool mResizing;
|
||||
bool mStackFromBottom;
|
||||
F32 mPadding;
|
||||
|
||||
public:
|
||||
GuiStackControl();
|
||||
|
||||
void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
void childResized(GuiControl *child);
|
||||
|
||||
bool onWake();
|
||||
void onSleep();
|
||||
|
||||
void updatePanes();
|
||||
|
||||
S32 getCount() { return size(); }; /// Returns the number of children in the stack
|
||||
|
||||
void addObject(SimObject *obj);
|
||||
|
||||
static void initPersistFields();
|
||||
DECLARE_CONOBJECT(GuiStackControl);
|
||||
};
|
||||
|
||||
#endif
|
638
engine/gui/containers/guiTabBookCtrl.cc
Executable file
638
engine/gui/containers/guiTabBookCtrl.cc
Executable file
@ -0,0 +1,638 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/simBase.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "gui/containers/guiTabBookCtrl.h"
|
||||
#include "platform/event.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "gui/containers/guiScrollCtrl.h"
|
||||
#include "gui/editor/guiEditCtrl.h"
|
||||
#include "gui/controls/guiPopUpCtrl.h"
|
||||
|
||||
|
||||
// So we can set tab alignment via gui editor
|
||||
static EnumTable::Enums tabAlignEnums[] =
|
||||
{
|
||||
{ GuiTabBookCtrl::AlignTop, "Top" },
|
||||
// { GuiTabBookCtrl::AlignLeft, "Left" },
|
||||
{ GuiTabBookCtrl::AlignBottom,"Bottom" },
|
||||
// { GuiTabBookCtrl::AlignRight, "Right" }
|
||||
};
|
||||
static EnumTable gTabAlignEnums(/*4*/2,&tabAlignEnums[0]);
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiTabBookCtrl);
|
||||
|
||||
GuiTabBookCtrl::GuiTabBookCtrl()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPages);
|
||||
mTabHeight = 24;
|
||||
mLastTabHeight = mTabHeight;
|
||||
mTabWidth = 64;
|
||||
mLastTabWidth = mTabWidth;
|
||||
mTabPosition = GuiTabBookCtrl::AlignTop;
|
||||
mLastTabPosition = mTabPosition;
|
||||
mActivePage = NULL;
|
||||
mHoverTab = NULL;
|
||||
mHasTexture = false;
|
||||
mBitmapBounds = NULL;
|
||||
mBounds.extent.set( 400, 300 );
|
||||
mPageRect = RectI(0,0,0,0);
|
||||
mTabRect = RectI(0,0,0,0);
|
||||
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
addField("TabPosition", TypeEnum, Offset(mTabPosition,GuiTabBookCtrl), 1, &gTabAlignEnums );
|
||||
addField("TabHeight", TypeS32, Offset(mTabHeight,GuiTabBookCtrl) );
|
||||
addField("TabWidth", TypeS32, Offset(mTabWidth,GuiTabBookCtrl));
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Empty for now, will implement for handling design time context menu for manipulating pages
|
||||
ConsoleMethod( GuiTabBookCtrl, addPage, void, 2, 2, "(no arguments expected)")
|
||||
{
|
||||
object->addNewPage();
|
||||
}
|
||||
|
||||
//ConsoleMethod( GuiTabBookCtrl, removePage, void, 2, 2, "()")
|
||||
//{
|
||||
//}
|
||||
|
||||
|
||||
bool GuiTabBookCtrl::onAdd()
|
||||
{
|
||||
Parent::onAdd();
|
||||
|
||||
mTabRect = getTabBaseRect( Point2I(0,0) );
|
||||
mPageRect = getPageRect( mTabRect, Point2I(0,0) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void GuiTabBookCtrl::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
|
||||
{
|
||||
Vector<GuiTabPageCtrl*>::iterator i = mPages.begin();
|
||||
for( ; i != mPages.end(); i++)
|
||||
{
|
||||
GuiTabPageCtrl* tab = (*i);
|
||||
if( tab == child )
|
||||
{
|
||||
if( tab == mActivePage )
|
||||
mActivePage = NULL;
|
||||
mPages.erase( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( mPages.empty() )
|
||||
mActivePage = NULL;
|
||||
else if (mActivePage == NULL )
|
||||
mActivePage = (*mPages.begin());
|
||||
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onChildAdded( GuiControl *child )
|
||||
{
|
||||
GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child);
|
||||
if( !page )
|
||||
{
|
||||
Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page");
|
||||
SimObject *simObj = reinterpret_cast<SimObject*>(child);
|
||||
removeObject( simObj );
|
||||
if( mActivePage )
|
||||
{
|
||||
mActivePage->addObject( simObj );
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent");
|
||||
GuiControl *rent = getParent();
|
||||
if( rent )
|
||||
rent->addObject( simObj );
|
||||
}
|
||||
return;
|
||||
}
|
||||
child->resize( mPageRect.point, mPageRect.extent );
|
||||
mPages.push_back( page );
|
||||
selectPage( page );
|
||||
}
|
||||
|
||||
|
||||
bool GuiTabBookCtrl::onWake()
|
||||
{
|
||||
if (! Parent::onWake())
|
||||
return false;
|
||||
|
||||
mHasTexture = mProfile->constructBitmapArray();
|
||||
if( mHasTexture )
|
||||
mBitmapBounds = mProfile->mBitmapArrayRects.address();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onSleep()
|
||||
{
|
||||
Parent::onSleep();
|
||||
}
|
||||
|
||||
|
||||
void GuiTabBookCtrl::addNewPage()
|
||||
{
|
||||
char textbuf[1024];
|
||||
|
||||
GuiTabPageCtrl * page = new GuiTabPageCtrl();
|
||||
|
||||
page->setField("profile", "GuiTabPageProfile");
|
||||
|
||||
dSprintf(textbuf, sizeof(textbuf), "TabBookPage%d_%d", getId(), page->getId());
|
||||
page->registerObject(textbuf);
|
||||
|
||||
this->addObject( page );
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
||||
{
|
||||
Parent::resize( newPosition, newExtent );
|
||||
|
||||
mTabRect = getTabBaseRect( Point2I(0,0) );
|
||||
mPageRect = getPageRect( mTabRect, Point2I(0,0) );
|
||||
|
||||
RectI pageRect = getPageRectChild();
|
||||
|
||||
// Resize Children
|
||||
SimSet::iterator i;
|
||||
for(i = begin(); i != end(); i++)
|
||||
{
|
||||
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
||||
ctrl->mBounds = pageRect;
|
||||
}
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::childResized(GuiControl *child)
|
||||
{
|
||||
Parent::childResized( child );
|
||||
child->mBounds = getPageRectChild();
|
||||
}
|
||||
|
||||
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
|
||||
{
|
||||
return findHitTab( event.mousePoint );
|
||||
}
|
||||
|
||||
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
|
||||
{
|
||||
S32 hitTab = 0; // Which tab did we hit?
|
||||
|
||||
hitPoint = globalToLocalCoord( hitPoint );
|
||||
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
case AlignBottom:
|
||||
hitTab = (S32)mFloor( (F32)hitPoint.x / (F32)mTabWidth );
|
||||
break;
|
||||
case AlignLeft:
|
||||
case AlignRight:
|
||||
hitTab = (S32)mFloor( (F32)hitPoint.y / (F32)mTabWidth );
|
||||
break;
|
||||
};
|
||||
|
||||
if( mPages.size() > hitTab && hitTab >= 0 )
|
||||
return mPages[hitTab];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RectI GuiTabBookCtrl::getHitRect( Point2I offset )
|
||||
{
|
||||
// We can get our rect without a specified offset, but if we don't specify one
|
||||
// when rendering with GUI editor open, our offset is wrong because the editor
|
||||
// offsets the canvas to accomodate creation of it's own controls
|
||||
|
||||
RectI Result = RectI( localToGlobalCoord( Point2I(0,0) ), mBounds.extent );
|
||||
|
||||
if( ! offset.isZero() )
|
||||
Result.point -= offset;
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
Result.extent.y = mTabHeight;
|
||||
break;
|
||||
case AlignBottom:
|
||||
Result.point.y = (Result.point.y + Result.extent.y) - mTabHeight;
|
||||
Result.extent.y = mTabHeight;
|
||||
break;
|
||||
case AlignLeft:
|
||||
Result.extent.x = mTabHeight;
|
||||
break;
|
||||
case AlignRight:
|
||||
Result.point.x = (Result.point.x + Result.extent.x) - mTabHeight;
|
||||
Result.extent.x = mTabHeight;
|
||||
break;
|
||||
}
|
||||
return Result;
|
||||
|
||||
}
|
||||
|
||||
RectI GuiTabBookCtrl::getTabBaseRect( Point2I offset = Point2I(0,0) )
|
||||
{
|
||||
// We can get our rect without a specified offset, but if we don't specify one
|
||||
// when rendering with GUI editor open, our offset is wrong because the editor
|
||||
// offsets the canvas to accomodate creation of it's own controls
|
||||
|
||||
RectI Result = RectI( offset.isZero() ? mBounds.point : offset, mBounds.extent );
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
Result.extent.y = mTabHeight;
|
||||
break;
|
||||
case AlignBottom:
|
||||
Result.point.y = (Result.point.y + Result.extent.y) - mTabHeight;
|
||||
Result.extent.y = mTabHeight;
|
||||
break;
|
||||
case AlignLeft:
|
||||
Result.extent.x = mTabHeight;
|
||||
break;
|
||||
case AlignRight:
|
||||
Result.point.x = (Result.point.x + Result.extent.x) - mTabHeight;
|
||||
Result.extent.x = mTabHeight;
|
||||
break;
|
||||
}
|
||||
return Result;
|
||||
|
||||
}
|
||||
|
||||
RectI GuiTabBookCtrl::getPageRect( const RectI &tabBaseRect, Point2I offset )
|
||||
{
|
||||
RectI Result;
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
Result.point.x = tabBaseRect.point.x;
|
||||
Result.extent.x = tabBaseRect.extent.x;
|
||||
Result.point.y = tabBaseRect.point.y + tabBaseRect.extent.y;
|
||||
Result.extent.y = mBounds.extent.y - mTabHeight;
|
||||
break;
|
||||
case AlignBottom:
|
||||
Result.point.x = tabBaseRect.point.x;
|
||||
Result.extent.x = tabBaseRect.extent.x;
|
||||
Result.point.y = offset.y;
|
||||
Result.extent.y = mBounds.extent.y - mTabHeight;
|
||||
break;
|
||||
case AlignLeft:
|
||||
Result.point.x = offset.x + mTabHeight;
|
||||
Result.extent.x = mBounds.extent.x - mTabHeight;
|
||||
Result.point.y = offset.y;
|
||||
Result.extent.y = mBounds.extent.y;
|
||||
break;
|
||||
case AlignRight:
|
||||
Result.point.x = offset.x;
|
||||
Result.extent.x = mBounds.extent.x - mTabHeight;
|
||||
Result.point.y = offset.y;
|
||||
Result.extent.y = mBounds.extent.y;
|
||||
break;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
RectI GuiTabBookCtrl::getPageRectChild()
|
||||
{
|
||||
RectI Result;
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
Result.point.x = 0;
|
||||
Result.extent.x = mBounds.extent.x;
|
||||
Result.point.y = mTabHeight;
|
||||
Result.extent.y = mBounds.extent.y - mTabHeight;
|
||||
break;
|
||||
case AlignBottom:
|
||||
Result.point.x = 0;
|
||||
Result.extent.x = mBounds.extent.x;
|
||||
Result.point.y = 0;
|
||||
Result.extent.y = mBounds.extent.y - mTabHeight;
|
||||
break;
|
||||
case AlignLeft:
|
||||
Result.point.x = mTabHeight;
|
||||
Result.extent.x = mBounds.extent.x - mTabHeight;
|
||||
Result.point.y = 0;
|
||||
Result.extent.y = mBounds.extent.y;
|
||||
break;
|
||||
case AlignRight:
|
||||
Result.point.x = 0;
|
||||
Result.extent.x = mBounds.extent.x - mTabHeight;
|
||||
Result.point.y = 0;
|
||||
Result.extent.y = mBounds.extent.y;
|
||||
break;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onPreRender()
|
||||
{
|
||||
// sometimes we need to resize because of a changed persistent field
|
||||
// that's what this does
|
||||
solveDirty();
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::solveDirty()
|
||||
{
|
||||
bool dirty = false;
|
||||
if( mTabPosition != mLastTabPosition )
|
||||
{
|
||||
mLastTabPosition = mTabPosition;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if( mTabHeight != mLastTabHeight )
|
||||
{
|
||||
mLastTabHeight = mTabHeight;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if( mTabWidth != mLastTabWidth )
|
||||
{
|
||||
mLastTabWidth = mTabWidth;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if( dirty )
|
||||
resize( mBounds.point, mBounds.extent );
|
||||
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
RectI tabRect = getTabBaseRect( offset );
|
||||
RectI pageRect = getPageRect( tabRect, offset );
|
||||
|
||||
|
||||
// We're so nice we'll store the old modulation before we clear it for our rendering! :)
|
||||
ColorI oldModulation;
|
||||
dglGetBitmapModulation( &oldModulation );
|
||||
|
||||
// Wipe it out
|
||||
dglClearBitmapModulation();
|
||||
|
||||
// Render our tabs
|
||||
renderTabs( tabRect );
|
||||
|
||||
// This is ideal, we know what our active tab is, so we only render it
|
||||
if( mActivePage != NULL )
|
||||
{
|
||||
mActivePage->onRender( pageRect.point, pageRect );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Render only the active page
|
||||
if( ! mPages.empty() )
|
||||
{
|
||||
GuiTabPageCtrl* tab = reinterpret_cast<GuiTabPageCtrl*>(*mPages.begin());
|
||||
tab->onRender( pageRect.point, pageRect );
|
||||
mActivePage = tab;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we have no active page, render a filled rect only
|
||||
dglDrawRectFill( pageRect, mProfile->mFillColor );
|
||||
}
|
||||
}
|
||||
|
||||
// Restore old modulation
|
||||
dglSetBitmapModulation( oldModulation );
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::renderTabs( const RectI &bounds )
|
||||
{
|
||||
RectI oldClip = dglGetClipRect();
|
||||
|
||||
dglSetClipRect( bounds );
|
||||
|
||||
S32 fitTabs = 0; // How many tabs can we fit in the book?
|
||||
S32 extentY,extentX;
|
||||
|
||||
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
case AlignBottom:
|
||||
fitTabs = (S32)mCeil( (F32)bounds.extent.x / (F32)mTabWidth );
|
||||
extentX = mTabWidth;
|
||||
extentY = mTabHeight;
|
||||
break;
|
||||
case AlignLeft:
|
||||
case AlignRight:
|
||||
fitTabs = (S32)mCeil( (F32)bounds.extent.y / (F32)mTabWidth );
|
||||
extentY = mTabWidth;
|
||||
extentX = mTabHeight;
|
||||
break;
|
||||
};
|
||||
|
||||
Vector<GuiTabPageCtrl*>::iterator i = mPages.begin();
|
||||
for( S32 j=0 ; ( i != mPages.end() && j < fitTabs ) ; i++, j++)
|
||||
{
|
||||
RectI tabBounds = RectI( bounds.point.x, bounds.point.y, extentX, extentY );
|
||||
switch( mTabPosition )
|
||||
{
|
||||
case AlignTop:
|
||||
case AlignBottom:
|
||||
tabBounds.point.x = bounds.point.x + ( j * mTabWidth );
|
||||
break;
|
||||
case AlignLeft:
|
||||
case AlignRight:
|
||||
tabBounds.point.y = bounds.point.y + ( j * mTabWidth );
|
||||
break;
|
||||
};
|
||||
|
||||
GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>(*i);
|
||||
|
||||
renderTab( tabBounds, tab );
|
||||
}
|
||||
|
||||
dglSetClipRect( oldClip );
|
||||
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::renderTab( RectI tabRect, GuiTabPageCtrl *tab )
|
||||
{
|
||||
StringTableEntry text = tab->getText();
|
||||
ColorI oldColor;
|
||||
|
||||
dglGetBitmapModulation( &oldColor );
|
||||
|
||||
// Is this a skinned control?
|
||||
if( mHasTexture && mProfile->mBitmapArrayRects.size() == NumBitmaps)
|
||||
{
|
||||
S32 tabTextureIndex = 0;
|
||||
RectI stretchRect;
|
||||
|
||||
if ( mActivePage == tab )
|
||||
stretchRect = mBitmapBounds[ TabSelected ];
|
||||
else if( mHoverTab == tab )
|
||||
stretchRect = mBitmapBounds[ TabHover ];
|
||||
else
|
||||
stretchRect = mBitmapBounds[ TabNormal ];
|
||||
|
||||
dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, (mTabPosition == AlignBottom) ? /* Render texture upside down */ GFlip_Y : 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this isn't a skinned control or the bitmap is simply missing, handle it WELL
|
||||
if ( mActivePage == tab )
|
||||
dglDrawRectFill(tabRect, mProfile->mFillColor);
|
||||
else if( mHoverTab == tab )
|
||||
dglDrawRectFill(tabRect, mProfile->mFillColorHL);
|
||||
else
|
||||
dglDrawRectFill(tabRect, mProfile->mFillColorNA);
|
||||
|
||||
}
|
||||
|
||||
|
||||
dglSetBitmapModulation(mProfile->mFontColor);
|
||||
|
||||
renderJustifiedText( tabRect.point, tabRect.extent, text );
|
||||
|
||||
dglSetBitmapModulation( oldColor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GuiTabBookCtrl::selectPage( S32 index )
|
||||
{
|
||||
if( mPages.size() < index )
|
||||
return;
|
||||
|
||||
// Select the page
|
||||
selectPage( mPages[ index ] );
|
||||
}
|
||||
|
||||
|
||||
void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
|
||||
{
|
||||
Vector<GuiTabPageCtrl*>::iterator i = mPages.begin();
|
||||
for( ; i != mPages.end() ; i++ )
|
||||
{
|
||||
GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>(*i);
|
||||
if( page == tab )
|
||||
{
|
||||
mActivePage = tab;
|
||||
tab->setVisible( true );
|
||||
|
||||
// Notify User
|
||||
char *retBuffer = Con::getReturnBuffer( 512 );
|
||||
dStrcpy( retBuffer, tab->getText() );
|
||||
Con::executef( this, 2, "onTabSelected", retBuffer );
|
||||
|
||||
}
|
||||
else
|
||||
tab->setVisible( false );
|
||||
}
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
|
||||
{
|
||||
RectI TabBase = getHitRect();
|
||||
if( TabBase.pointInRect( event.mousePoint ) )
|
||||
{
|
||||
GuiTabPageCtrl *tab = findHitTab( event.mousePoint );
|
||||
if( tab != NULL )
|
||||
selectPage( tab );
|
||||
}
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onRightMouseDown(const GuiEvent &event)
|
||||
{
|
||||
if (! mActive)
|
||||
{
|
||||
Parent::onRightMouseDown(event);
|
||||
return;
|
||||
}
|
||||
|
||||
setFirstResponder();
|
||||
|
||||
//search for the control hit in any layer below the edit layer
|
||||
GuiControl *hitCtrl = findHitControl(globalToLocalCoord(event.mousePoint), mLayer - 1);
|
||||
if (hitCtrl != this)
|
||||
{
|
||||
Con::executef(this, 1, "onClearSelected");
|
||||
}
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onMouseMove(const GuiEvent &event)
|
||||
{
|
||||
RectI TabBase = getHitRect();
|
||||
|
||||
|
||||
if( TabBase.pointInRect( event.mousePoint ) )
|
||||
{
|
||||
|
||||
GuiTabPageCtrl *tab = findHitTab( event.mousePoint );
|
||||
if( tab != NULL && mHoverTab != tab )
|
||||
mHoverTab = tab;
|
||||
else if ( !tab )
|
||||
mHoverTab = NULL;
|
||||
}
|
||||
Parent::onMouseMove( event );
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event )
|
||||
{
|
||||
mHoverTab = NULL;
|
||||
}
|
||||
|
||||
void GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
|
||||
{
|
||||
RectI TabBase = getHitRect( offset );
|
||||
if( TabBase.pointInRect( event.mousePoint ) )
|
||||
{
|
||||
GuiTabPageCtrl *tab = findHitTab( event.mousePoint + offset );
|
||||
if( tab != NULL )
|
||||
selectPage( tab );
|
||||
}
|
||||
|
||||
// This shouldn't be called if it's not design time, but check just incase
|
||||
if ( GuiControl::smDesignTime )
|
||||
{
|
||||
// If we clicked in the editor and our addset is the tab book
|
||||
// ctrl, select the child ctrl so we can edit it's properties
|
||||
GuiEditCtrl* edit = GuiControl::smEditorHandle;
|
||||
if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL )
|
||||
edit->select( mActivePage );
|
||||
}
|
||||
|
||||
}
|
||||
void GuiTabBookCtrl::onRightMouseDownEditor(const GuiEvent &event, Point2I offset)
|
||||
{
|
||||
//////GuiPopUpTextListCtrl
|
||||
//GuiPopUpMenuCtrl * menu = new GuiPopUpMenuCtrl();
|
||||
//menu->setField("profile", "GuiPopUpMenuProfile");
|
||||
//menu->setField("text", "Duh");
|
||||
|
||||
////now add the entries
|
||||
//menu->addEntry("Test",1);
|
||||
//menu->onAction();
|
||||
|
||||
//menu->registerObject();
|
||||
|
||||
}
|
||||
|
198
engine/gui/containers/guiTabBookCtrl.h
Executable file
198
engine/gui/containers/guiTabBookCtrl.h
Executable file
@ -0,0 +1,198 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _GUI_TABBOOKCTRL_H_
|
||||
#define _GUI_TABBOOKCTRL_H_
|
||||
|
||||
#ifndef _GUICONTROL_H_
|
||||
#include "gui/core/guiControl.h"
|
||||
#endif
|
||||
|
||||
#ifndef _GUITABPAGECTRL_H_
|
||||
#include "gui/controls/guiTabPageCtrl.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Tab Book Control for creation of tabbed dialogs
|
||||
///
|
||||
/// @see GUI for an overview of the Torque GUI system.
|
||||
///
|
||||
/// @section GuiTabBookCtrl_Intro Introduction
|
||||
///
|
||||
/// GuiTabBookCtrl is a container class that holds children of type GuiTabPageCtrl
|
||||
///
|
||||
/// GuiTabBookCtrl creates an easy to work with system for creating tabbed dialogs
|
||||
/// allowing for creation of dialogs that store alot of information in a small area
|
||||
/// by seperating the information into pages which are changeable by clicking their
|
||||
/// page title on top or bottom of the control
|
||||
///
|
||||
/// tabs may be aligned to be on top or bottom of the book and are changeable while
|
||||
/// the GUI editor is open for quick switching between pages allowing multipage dialogs
|
||||
/// to be edited quickly and easily.
|
||||
///
|
||||
/// The control may only contain children of type GuiTabPageCtrl.
|
||||
/// If a control is added to the Book that is not of type GuiTabPageCtrl, it will be
|
||||
/// removed and relocated to the currently active page of the control.
|
||||
/// If there is no active page in the book, the child control will be relocated to the
|
||||
/// parent of the book.
|
||||
///
|
||||
/// @ref GUI has an overview of the GUI system.
|
||||
///
|
||||
/// @nosubgrouping
|
||||
class GuiTabBookCtrl : public GuiControl
|
||||
{
|
||||
public:
|
||||
enum TabPosition
|
||||
{
|
||||
AlignTop, ///< Align the tabs on the top of the tab book control
|
||||
AlignBottom,///< Align the tabs on the bottom of the tab book control
|
||||
AlignLeft, ///< Unsupported currently
|
||||
AlignRight ///< Unsupported currently
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
typedef GuiControl Parent; ///< typedef for parent class access
|
||||
|
||||
RectI mPageRect; ///< Rectangle of the tab page portion of the control
|
||||
RectI mTabRect; ///< Rectangle of the tab portion of the control
|
||||
Vector<GuiTabPageCtrl*> mPages; ///< Vector of pages contained by the control
|
||||
GuiTabPageCtrl* mActivePage; ///< Pointer to the active (selected) tab page child control
|
||||
GuiTabPageCtrl* mHoverTab; ///< Pointer to the tab page that currently has the mouse positioned ontop of its tab
|
||||
TabPosition mTabPosition; ///< Current tab position (see alignment)
|
||||
TabPosition mLastTabPosition; ///< Last known tab position, stored to compare to tabPosition to know when to resize children
|
||||
S32 mTabHeight; ///< Current tab height
|
||||
S32 mLastTabHeight; ///< Last known tab height, stored to compare to current tabHeight to know when to resize children
|
||||
S32 mTabWidth; ///< Current tab width
|
||||
S32 mLastTabWidth; ///< Last know tab width, stored to compare to current tabWidth to know when to resize children
|
||||
|
||||
enum
|
||||
{
|
||||
TabSelected = 0, ///< Index of selected tab texture
|
||||
TabNormal, ///< Index of normal tab texture
|
||||
TabHover, ///< Index of hover tab texture
|
||||
NumBitmaps ///< Number of bitmaps in this array
|
||||
};
|
||||
bool mHasTexture; ///< Indicates whether we have a texture to render the tabs with
|
||||
RectI *mBitmapBounds;///< Array of rectangles identifying textures for tab book
|
||||
|
||||
public:
|
||||
|
||||
GuiTabBookCtrl();
|
||||
DECLARE_CONOBJECT(GuiTabBookCtrl);
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
/// @name Control Events
|
||||
/// @{
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
bool onWake();
|
||||
void onSleep();
|
||||
void onPreRender();
|
||||
void onRender( Point2I offset, const RectI &updateRect );
|
||||
/// @}
|
||||
|
||||
/// @name Child events
|
||||
/// @{
|
||||
void onChildRemoved( GuiControl* child );
|
||||
void onChildAdded( GuiControl *child );
|
||||
/// @}
|
||||
|
||||
/// @name Rendering methods
|
||||
/// @{
|
||||
|
||||
/// Tab rendering routine, iterates through all tabs rendering one at a time
|
||||
/// @param bounds The rectanlge to render the tabs in
|
||||
void renderTabs( const RectI &bounds );
|
||||
|
||||
/// Tab rendering subroutine, renders one tab with specified options
|
||||
/// @param tabRect the rectangle to render the tab into
|
||||
/// @param tab pointer to the tab page control for which to render the tab
|
||||
void renderTab( RectI tabRect, GuiTabPageCtrl* tab );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Page Management
|
||||
/// @{
|
||||
|
||||
/// Create a new tab page child in the book
|
||||
///
|
||||
/// Pages created are not titled and appear with no text on their tab when created.
|
||||
/// This may change in the future.
|
||||
void addNewPage();
|
||||
|
||||
/// Select a tab page based on an index
|
||||
/// @param index The index in the list that specifies which page to select
|
||||
void selectPage( S32 index );
|
||||
|
||||
/// Select a tab page by a pointer to that page
|
||||
/// @param page A pointer to the GuiTabPageCtrl to select. This page must be a child of the tab book.
|
||||
void selectPage( GuiTabPageCtrl *page );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Internal Utility Functions
|
||||
/// @{
|
||||
|
||||
/// Checks to see if a tab option has changed and we need to reize children, resizes if necesary
|
||||
void solveDirty();
|
||||
|
||||
RectI getHitRect( Point2I offset = Point2I(0,0) );
|
||||
|
||||
/// Get the rectangle of the tab base
|
||||
RectI getTabBaseRect( Point2I offset );
|
||||
|
||||
/// Get the rectangle of the page portion of the control
|
||||
RectI getPageRect( const RectI &tabBaseRect, Point2I offset );
|
||||
|
||||
/// Get a page rect for a child control, with the offset point starting at (0,0)
|
||||
RectI getPageRectChild();
|
||||
|
||||
/// Find the tab that was hit by the current event, if any
|
||||
/// @param event The GuiEvent that caused this function call
|
||||
GuiTabPageCtrl *findHitTab( const GuiEvent &event );
|
||||
|
||||
/// Find the tab that was hit, based on a point
|
||||
/// @param hitPoint A Point2I that specifies where to search for a tab hit
|
||||
GuiTabPageCtrl *findHitTab( Point2I hitPoint );
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Sizing
|
||||
/// @{
|
||||
|
||||
/// Rezize our control
|
||||
/// This method is overridden so that we may handle resizing of our child tab
|
||||
/// pages when we are resized.
|
||||
/// This ensures we keep our sizing in sync when we are moved or sized.
|
||||
///
|
||||
/// @param newPosition The new position of the control
|
||||
/// @param newExtent The new extent of the control
|
||||
void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
|
||||
/// Called when a child page is resized
|
||||
/// This method is overridden so that we may handle resizing of our child tab
|
||||
/// pages when one of them is resized.
|
||||
/// This ensures we keep our sizing in sync when we our children are sized or moved.
|
||||
///
|
||||
/// @param child A pointer to the child control that has been resized
|
||||
void childResized(GuiControl *child);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
/// @name Mouse Events
|
||||
/// @{
|
||||
void onMouseDown(const GuiEvent &event);
|
||||
void onMouseMove(const GuiEvent &event);
|
||||
void onMouseLeave(const GuiEvent &event);
|
||||
void onRightMouseDown(const GuiEvent &event);
|
||||
void onMouseDownEditor(const GuiEvent &event, Point2I offset);
|
||||
void onRightMouseDownEditor(const GuiEvent &event, Point2I offset);
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif //_GUI_TABBOOKCTRL_H_
|
743
engine/gui/containers/guiWindowCtrl.cc
Executable file
743
engine/gui/containers/guiWindowCtrl.cc
Executable file
@ -0,0 +1,743 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/console.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "gui/containers/guiWindowCtrl.h"
|
||||
#include "gui/core/guiDefaultControlRender.h"
|
||||
|
||||
IMPLEMENT_CONOBJECT(GuiWindowCtrl);
|
||||
|
||||
GuiWindowCtrl::GuiWindowCtrl(void)
|
||||
{
|
||||
mResizeWidth = true;
|
||||
mResizeHeight = true;
|
||||
mCanMove = true;
|
||||
mCanClose = true;
|
||||
mCanMinimize = true;
|
||||
mCanMaximize = true;
|
||||
mTitleHeight = 20;
|
||||
mResizeRightWidth = 10;
|
||||
mResizeBottomHeight = 10;
|
||||
|
||||
mCloseCommand = StringTable->insert("");
|
||||
|
||||
mMinimized = false;
|
||||
mMaximized = false;
|
||||
mMouseMovingWin = false;
|
||||
mMouseResizeWidth = false;
|
||||
mMouseResizeHeight = false;
|
||||
mBounds.extent.set(100, 200);
|
||||
mMinSize.set(50, 50);
|
||||
mMinimizeIndex = -1;
|
||||
mTabIndex = -1;
|
||||
|
||||
RectI closeRect(80, 2, 16, 16);
|
||||
mCloseButton = closeRect;
|
||||
closeRect.point.x -= 18;
|
||||
mMaximizeButton = closeRect;
|
||||
closeRect.point.x -= 18;
|
||||
mMinimizeButton = closeRect;
|
||||
|
||||
//other defaults
|
||||
mActive = true;
|
||||
mPressClose = false;
|
||||
mPressMaximize = false;
|
||||
mPressMinimize = false;
|
||||
|
||||
mDefaultCursor = NULL;
|
||||
mNWSECursor = NULL;
|
||||
mNESWCursor = NULL;
|
||||
mUpDownCursor = NULL;
|
||||
mLeftRightCursor = NULL;
|
||||
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("resizeWidth", TypeBool, Offset(mResizeWidth, GuiWindowCtrl));
|
||||
addField("resizeHeight", TypeBool, Offset(mResizeHeight, GuiWindowCtrl));
|
||||
addField("canMove", TypeBool, Offset(mCanMove, GuiWindowCtrl));
|
||||
addField("canClose", TypeBool, Offset(mCanClose, GuiWindowCtrl));
|
||||
addField("canMinimize", TypeBool, Offset(mCanMinimize, GuiWindowCtrl));
|
||||
addField("canMaximize", TypeBool, Offset(mCanMaximize, GuiWindowCtrl));
|
||||
addField("minSize", TypePoint2I, Offset(mMinSize, GuiWindowCtrl));
|
||||
addField("closeCommand", TypeString, Offset(mCloseCommand, GuiWindowCtrl));
|
||||
}
|
||||
|
||||
bool GuiWindowCtrl::isMinimized(S32 &index)
|
||||
{
|
||||
index = mMinimizeIndex;
|
||||
return mMinimized && mVisible;
|
||||
}
|
||||
|
||||
// helper fn so button positioning shares code...
|
||||
void GuiWindowCtrl::PositionButtons(void)
|
||||
{
|
||||
S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x;
|
||||
S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y;
|
||||
Point2I mainOff = mProfile->mTextOffset;
|
||||
|
||||
// until a pref, if alignment is LEFT, put buttons RIGHT justified.
|
||||
// ELSE, put buttons LEFT justified.
|
||||
int closeLeft = mainOff.x, closeTop = mainOff.y, closeOff = buttonWidth + 2;
|
||||
if ( mProfile->mAlignment == GuiControlProfile::LeftJustify )
|
||||
{
|
||||
closeOff = -closeOff;
|
||||
closeLeft = mBounds.extent.x - buttonWidth - mainOff.x;
|
||||
}
|
||||
RectI closeRect(closeLeft, closeTop, buttonHeight, buttonWidth);
|
||||
mCloseButton = closeRect;
|
||||
|
||||
// Always put Minimize on left side of Maximize.
|
||||
closeRect.point.x += closeOff;
|
||||
if (closeOff>0)
|
||||
{
|
||||
mMinimizeButton = closeRect;
|
||||
closeRect.point.x += closeOff;
|
||||
mMaximizeButton = closeRect;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMaximizeButton = closeRect;
|
||||
closeRect.point.x += closeOff;
|
||||
mMinimizeButton = closeRect;
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiWindowCtrl::onWake()
|
||||
{
|
||||
if (! Parent::onWake())
|
||||
return false;
|
||||
|
||||
//get the texture for the close, minimize, and maximize buttons
|
||||
mTextureHandle = mProfile->mTextureHandle;
|
||||
bool result = mProfile->constructBitmapArray() >= NumBitmaps;
|
||||
AssertFatal(result, "Failed to create the bitmap array");
|
||||
if(!result)
|
||||
return false;
|
||||
|
||||
mBitmapBounds = mProfile->mBitmapArrayRects.address();
|
||||
S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x;
|
||||
S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y;
|
||||
|
||||
mTitleHeight = buttonHeight + 4;
|
||||
mResizeRightWidth = mTitleHeight / 2;
|
||||
mResizeBottomHeight = mTitleHeight / 2;
|
||||
|
||||
//set the button coords
|
||||
PositionButtons();
|
||||
|
||||
//set the tab index
|
||||
mTabIndex = -1;
|
||||
GuiControl *parent = getParent();
|
||||
if (parent && mFirstResponder)
|
||||
{
|
||||
mTabIndex = 0;
|
||||
|
||||
//count the number of windows preceeding this one
|
||||
iterator i;
|
||||
for (i = parent->begin(); i != parent->end(); i++)
|
||||
{
|
||||
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
|
||||
if (ctrl)
|
||||
{
|
||||
if (ctrl == this) break;
|
||||
else if (ctrl->mFirstResponder) mTabIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::onSleep()
|
||||
{
|
||||
mTextureHandle = NULL;
|
||||
Parent::onSleep();
|
||||
}
|
||||
|
||||
GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
|
||||
{
|
||||
if (! mMinimized)
|
||||
return Parent::findHitControl(pt, initialLayer);
|
||||
else
|
||||
return this;
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
||||
{
|
||||
Parent::resize(newPosition, newExtent);
|
||||
|
||||
//set the button coords
|
||||
PositionButtons();
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::onMouseDown(const GuiEvent &event)
|
||||
{
|
||||
setUpdate();
|
||||
|
||||
mOrigBounds = mBounds;
|
||||
|
||||
mMouseDownPosition = event.mousePoint;
|
||||
Point2I localPoint = globalToLocalCoord(event.mousePoint);
|
||||
|
||||
//select this window - move it to the front, and set the first responder
|
||||
selectWindow();
|
||||
|
||||
//if we clicked within the title bar
|
||||
if (localPoint.y < mTitleHeight)
|
||||
{
|
||||
//if we clicked on the close button
|
||||
if (mCanClose && mCloseButton.pointInRect(localPoint))
|
||||
{
|
||||
mPressClose = mCanClose;
|
||||
}
|
||||
else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
|
||||
{
|
||||
mPressMaximize = mCanMaximize;
|
||||
}
|
||||
else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
|
||||
{
|
||||
mPressMinimize = mCanMinimize;
|
||||
}
|
||||
|
||||
//else we clicked within the title
|
||||
else
|
||||
{
|
||||
mMouseMovingWin = mCanMove;
|
||||
mMouseResizeWidth = false;
|
||||
mMouseResizeHeight = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mMouseMovingWin = false;
|
||||
|
||||
//see if we clicked on the right edge
|
||||
if (mResizeWidth && (localPoint.x > mBounds.extent.x - mResizeRightWidth))
|
||||
{
|
||||
mMouseResizeWidth = true;
|
||||
}
|
||||
|
||||
//see if we clicked on the bottom edge (as well)
|
||||
if (mResizeHeight && (localPoint.y > mBounds.extent.y - mResizeBottomHeight))
|
||||
{
|
||||
mMouseResizeHeight = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mMouseMovingWin || mMouseResizeWidth || mMouseResizeHeight ||
|
||||
mPressClose || mPressMaximize || mPressMinimize)
|
||||
{
|
||||
mouseLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
GuiControl *ctrl = findHitControl(localPoint);
|
||||
if (ctrl && ctrl != this)
|
||||
ctrl->onMouseDown(event);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::onMouseDragged(const GuiEvent &event)
|
||||
{
|
||||
GuiControl *parent = getParent();
|
||||
GuiCanvas *root = getRoot();
|
||||
if (! root) return;
|
||||
|
||||
Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
|
||||
|
||||
Point2I newPosition = mBounds.point;
|
||||
Point2I newExtent = mBounds.extent;
|
||||
bool update = false;
|
||||
if (mMouseMovingWin && parent)
|
||||
{
|
||||
newPosition.x = getMax(0, getMin(parent->mBounds.extent.x - mBounds.extent.x, mOrigBounds.point.x + deltaMousePosition.x));
|
||||
newPosition.y = getMax(0, getMin(parent->mBounds.extent.y - mBounds.extent.y, mOrigBounds.point.y + deltaMousePosition.y));
|
||||
update = true;
|
||||
}
|
||||
else if(mPressClose || mPressMaximize || mPressMinimize)
|
||||
{
|
||||
setUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mMouseResizeWidth && parent)
|
||||
{
|
||||
newExtent.x = getMax(0, getMax(mMinSize.x, getMin(parent->mBounds.extent.x, mOrigBounds.extent.x + deltaMousePosition.x)));
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (mMouseResizeHeight && parent)
|
||||
{
|
||||
newExtent.y = getMax(0, getMax(mMinSize.y, getMin(parent->mBounds.extent.y, mOrigBounds.extent.y + deltaMousePosition.y)));
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update)
|
||||
{
|
||||
Point2I pos = parent->localToGlobalCoord(mBounds.point);
|
||||
root->addUpdateRegion(pos, mBounds.extent);
|
||||
resize(newPosition, newExtent);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::onMouseUp(const GuiEvent &event)
|
||||
{
|
||||
bool closing = mPressClose;
|
||||
bool maximizing = mPressMaximize;
|
||||
bool minimizing = mPressMinimize;
|
||||
mPressClose = false;
|
||||
mPressMaximize = false;
|
||||
mPressMinimize = false;
|
||||
|
||||
event;
|
||||
mouseUnlock();
|
||||
|
||||
mMouseMovingWin = false;
|
||||
mMouseResizeWidth = false;
|
||||
mMouseResizeHeight = false;
|
||||
|
||||
GuiControl *parent = getParent();
|
||||
if (! parent)
|
||||
return;
|
||||
|
||||
//see if we take an action
|
||||
Point2I localPoint = globalToLocalCoord(event.mousePoint);
|
||||
if (closing && mCloseButton.pointInRect(localPoint))
|
||||
{
|
||||
Con::evaluate(mCloseCommand);
|
||||
}
|
||||
else if (maximizing && mMaximizeButton.pointInRect(localPoint))
|
||||
{
|
||||
if (mMaximized)
|
||||
{
|
||||
//resize to the previous position and extent, bounded by the parent
|
||||
resize(Point2I(getMax(0, getMin(parent->mBounds.extent.x - mStandardBounds.extent.x, mStandardBounds.point.x)),
|
||||
getMax(0, getMin(parent->mBounds.extent.y - mStandardBounds.extent.y, mStandardBounds.point.y))),
|
||||
mStandardBounds.extent);
|
||||
//set the flag
|
||||
mMaximized = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//only save the position if we're not minimized
|
||||
if (! mMinimized)
|
||||
{
|
||||
mStandardBounds = mBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMinimized = false;
|
||||
}
|
||||
|
||||
//resize to fit the parent
|
||||
resize(Point2I(0, 0), parent->mBounds.extent);
|
||||
|
||||
//set the flag
|
||||
mMaximized = true;
|
||||
}
|
||||
}
|
||||
else if (minimizing && mMinimizeButton.pointInRect(localPoint))
|
||||
{
|
||||
if (mMinimized)
|
||||
{
|
||||
//resize to the previous position and extent, bounded by the parent
|
||||
resize(Point2I(getMax(0, getMin(parent->mBounds.extent.x - mStandardBounds.extent.x, mStandardBounds.point.x)),
|
||||
getMax(0, getMin(parent->mBounds.extent.y - mStandardBounds.extent.y, mStandardBounds.point.y))),
|
||||
mStandardBounds.extent);
|
||||
//set the flag
|
||||
mMinimized = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parent->mBounds.extent.x < 100 || parent->mBounds.extent.y < mTitleHeight + 3)
|
||||
return;
|
||||
|
||||
//only save the position if we're not maximized
|
||||
if (! mMaximized)
|
||||
{
|
||||
mStandardBounds = mBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
mMaximized = false;
|
||||
}
|
||||
|
||||
//first find the lowest unused minimized index up to 32 minimized windows
|
||||
U32 indexMask = 0;
|
||||
iterator i;
|
||||
S32 count = 0;
|
||||
for (i = parent->begin(); i != parent->end() && count < 32; i++)
|
||||
{
|
||||
count++;
|
||||
S32 index;
|
||||
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
|
||||
if (ctrl && ctrl->isMinimized(index))
|
||||
{
|
||||
indexMask |= (1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
//now find the first unused bit
|
||||
for (count = 0; count < 32; count++)
|
||||
{
|
||||
if (! (indexMask & (1 << count))) break;
|
||||
}
|
||||
|
||||
//if we have more than 32 minimized windows, use the first position
|
||||
count = getMax(0, count);
|
||||
|
||||
//this algorithm assumes all window have the same title height, and will minimize to 98 pix
|
||||
Point2I newExtent(98, mTitleHeight);
|
||||
|
||||
//first, how many can fit across
|
||||
S32 numAcross = getMax(1, (parent->mBounds.extent.x / newExtent.x + 2));
|
||||
|
||||
//find the new "mini position"
|
||||
Point2I newPosition;
|
||||
newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2;
|
||||
newPosition.y = parent->mBounds.extent.y - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2;
|
||||
|
||||
//find the minimized position and extent
|
||||
resize(newPosition, newExtent);
|
||||
|
||||
//set the index so other windows will not try to minimize to the same location
|
||||
mMinimizeIndex = count;
|
||||
|
||||
//set the flag
|
||||
mMinimized = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GuiControl *GuiWindowCtrl::findNextTabable(GuiControl *curResponder, bool firstCall)
|
||||
{
|
||||
//set the global if this is the first call (directly from the canvas)
|
||||
if (firstCall)
|
||||
{
|
||||
GuiControl::smCurResponder = NULL;
|
||||
}
|
||||
|
||||
//if the window does not already contain the first responder, return false
|
||||
//ie. Can't tab into or out of a window
|
||||
if (! ControlIsChild(curResponder))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//loop through, checking each child to see if it is the one that follows the firstResponder
|
||||
GuiControl *tabCtrl = NULL;
|
||||
iterator i;
|
||||
for (i = begin(); i != end(); i++)
|
||||
{
|
||||
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
||||
tabCtrl = ctrl->findNextTabable(curResponder, false);
|
||||
if (tabCtrl) break;
|
||||
}
|
||||
|
||||
//to ensure the tab cycles within the current window...
|
||||
if (! tabCtrl)
|
||||
{
|
||||
tabCtrl = findFirstTabable();
|
||||
}
|
||||
|
||||
mFirstResponder = tabCtrl;
|
||||
return tabCtrl;
|
||||
}
|
||||
|
||||
GuiControl *GuiWindowCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall)
|
||||
{
|
||||
if (firstCall)
|
||||
{
|
||||
GuiControl::smPrevResponder = NULL;
|
||||
}
|
||||
|
||||
//if the window does not already contain the first responder, return false
|
||||
//ie. Can't tab into or out of a window
|
||||
if (! ControlIsChild(curResponder))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//loop through, checking each child to see if it is the one that follows the firstResponder
|
||||
GuiControl *tabCtrl = NULL;
|
||||
iterator i;
|
||||
for (i = begin(); i != end(); i++)
|
||||
{
|
||||
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
||||
tabCtrl = ctrl->findPrevTabable(curResponder, false);
|
||||
if (tabCtrl) break;
|
||||
}
|
||||
|
||||
//to ensure the tab cycles within the current window...
|
||||
if (! tabCtrl)
|
||||
{
|
||||
tabCtrl = findLastTabable();
|
||||
}
|
||||
|
||||
mFirstResponder = tabCtrl;
|
||||
return tabCtrl;
|
||||
}
|
||||
|
||||
bool GuiWindowCtrl::onKeyDown(const GuiEvent &event)
|
||||
{
|
||||
//if this control is a dead end, kill the event
|
||||
if ((! mVisible) || (! mActive) || (! mAwake)) return true;
|
||||
|
||||
if ((event.keyCode == KEY_TAB) && (event.modifier & SI_CTRL))
|
||||
{
|
||||
//find the next sibling window, and select it
|
||||
GuiControl *parent = getParent();
|
||||
if (parent)
|
||||
{
|
||||
GuiWindowCtrl *firstWindow = NULL;
|
||||
iterator i;
|
||||
for (i = parent->begin(); i != parent->end(); i++)
|
||||
{
|
||||
GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
|
||||
if (ctrl && ctrl->getTabIndex() == mTabIndex + 1)
|
||||
{
|
||||
ctrl->selectWindow();
|
||||
return true;
|
||||
}
|
||||
else if (ctrl && ctrl->getTabIndex() == 0)
|
||||
{
|
||||
firstWindow = ctrl;
|
||||
}
|
||||
}
|
||||
//recycle from the beginning
|
||||
if (firstWindow != this)
|
||||
{
|
||||
firstWindow->selectWindow();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Parent::onKeyDown(event);
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::selectWindow(void)
|
||||
{
|
||||
//first make sure this window is the front most of its siblings
|
||||
GuiControl *parent = getParent();
|
||||
if (parent)
|
||||
{
|
||||
parent->pushObjectToBack(this);
|
||||
}
|
||||
|
||||
//also set the first responder to be the one within this window
|
||||
setFirstResponder(mFirstResponder);
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::drawWinRect(const RectI &myRect)
|
||||
{
|
||||
Point2I bl = myRect.point;
|
||||
Point2I tr;
|
||||
tr.x = myRect.point.x + myRect.extent.x - 1;
|
||||
tr.y = myRect.point.y + myRect.extent.y - 1;
|
||||
dglDrawRectFill(myRect, mProfile->mFillColor);
|
||||
dglDrawLine(Point2I(bl.x + 1, tr.y), Point2I(bl.x + 1, bl.y), ColorI(255, 255, 255));
|
||||
dglDrawLine(Point2I(bl.x, tr.y + 1), Point2I(tr.x, tr.y + 1), ColorI(255, 255, 255));
|
||||
//dglDrawRect(myRect, ColorI(0, 0, 0)); // Taken out, this is controled via mProfile->mBorder
|
||||
}
|
||||
|
||||
void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect)
|
||||
{
|
||||
//draw the outline
|
||||
RectI winRect;
|
||||
winRect.point = offset;
|
||||
winRect.extent = mBounds.extent;
|
||||
GuiCanvas *root = getRoot();
|
||||
GuiControl *firstResponder = root ? root->getFirstResponder() : NULL;
|
||||
|
||||
bool isKey = (!firstResponder || ControlIsChild(firstResponder));
|
||||
|
||||
U32 topBase = isKey ? BorderTopLeftKey : BorderTopLeftNoKey;
|
||||
winRect.point.x += mBitmapBounds[BorderLeft].extent.x;
|
||||
winRect.point.y += mBitmapBounds[topBase + 2].extent.y;
|
||||
|
||||
winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x;
|
||||
winRect.extent.y -= mBitmapBounds[topBase + 2].extent.y + mBitmapBounds[BorderBottom].extent.y;
|
||||
|
||||
dglDrawRectFill(winRect, mProfile->mFillColor);
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, offset, mBitmapBounds[topBase]);
|
||||
dglDrawBitmapSR(mTextureHandle, Point2I(offset.x + mBounds.extent.x - mBitmapBounds[topBase+1].extent.x, offset.y),
|
||||
mBitmapBounds[topBase + 1]);
|
||||
|
||||
RectI destRect;
|
||||
destRect.point.x = offset.x + mBitmapBounds[topBase].extent.x;
|
||||
destRect.point.y = offset.y;
|
||||
destRect.extent.x = mBounds.extent.x - mBitmapBounds[topBase].extent.x - mBitmapBounds[topBase + 1].extent.x;
|
||||
destRect.extent.y = mBitmapBounds[topBase + 2].extent.y;
|
||||
RectI stretchRect = mBitmapBounds[topBase + 2];
|
||||
stretchRect.inset(1,0);
|
||||
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
|
||||
|
||||
destRect.point.x = offset.x;
|
||||
destRect.point.y = offset.y + mBitmapBounds[topBase].extent.y;
|
||||
destRect.extent.x = mBitmapBounds[BorderLeft].extent.x;
|
||||
destRect.extent.y = mBounds.extent.y - mBitmapBounds[topBase].extent.y - mBitmapBounds[BorderBottomLeft].extent.y;
|
||||
stretchRect = mBitmapBounds[BorderLeft];
|
||||
stretchRect.inset(0,1);
|
||||
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
|
||||
|
||||
destRect.point.x = offset.x + mBounds.extent.x - mBitmapBounds[BorderRight].extent.x;
|
||||
destRect.extent.x = mBitmapBounds[BorderRight].extent.x;
|
||||
destRect.point.y = offset.y + mBitmapBounds[topBase + 1].extent.y;
|
||||
destRect.extent.y = mBounds.extent.y - mBitmapBounds[topBase + 1].extent.y - mBitmapBounds[BorderBottomRight].extent.y;
|
||||
|
||||
stretchRect = mBitmapBounds[BorderRight];
|
||||
stretchRect.inset(0,1);
|
||||
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
|
||||
|
||||
dglDrawBitmapSR(mTextureHandle, offset + Point2I(0, mBounds.extent.y - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]);
|
||||
dglDrawBitmapSR(mTextureHandle, offset + mBounds.extent - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]);
|
||||
|
||||
destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x;
|
||||
destRect.extent.x = mBounds.extent.x - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x;
|
||||
|
||||
destRect.point.y = offset.y + mBounds.extent.y - mBitmapBounds[BorderBottom].extent.y;
|
||||
destRect.extent.y = mBitmapBounds[BorderBottom].extent.y;
|
||||
stretchRect = mBitmapBounds[BorderBottom];
|
||||
stretchRect.inset(1,0);
|
||||
|
||||
dglDrawBitmapStretchSR(mTextureHandle, destRect, stretchRect);
|
||||
|
||||
//draw the title
|
||||
// dhc addition: copied/modded from renderJustifiedText, since we enforce a
|
||||
// different color usage here. NOTE: it currently CAN overdraw the controls
|
||||
// if mis-positioned or 'scrunched' in a small width.
|
||||
dglSetBitmapModulation(mProfile->mFontColor);
|
||||
S32 fontHeight = mFont->getHeight();
|
||||
S32 textWidth = mProfile->mFont->getStrWidth((const UTF8 *)mText);
|
||||
Point2I start(0,0);
|
||||
// align the horizontal
|
||||
if ( mProfile->mAlignment == GuiControlProfile::RightJustify )
|
||||
start.set( winRect.extent.x - textWidth, 0 );
|
||||
else if ( mProfile->mAlignment == GuiControlProfile::CenterJustify )
|
||||
start.set( ( winRect.extent.x - textWidth) / 2, 0 );
|
||||
else // GuiControlProfile::LeftJustify or garbage... ;)
|
||||
start.set( 0, 0 );
|
||||
// If the text is longer then the box size, (it'll get clipped) so force Left Justify
|
||||
if( textWidth > winRect.extent.x ) start.set( 0, 0 );
|
||||
// center the vertical
|
||||
// start.y = ( winRect.extent.y - ( font->getHeight() - 2 ) ) / 2;
|
||||
dglDrawText(mFont, start + offset + mProfile->mTextOffset, mText);
|
||||
|
||||
// deal with rendering the titlebar controls
|
||||
AssertFatal(root, "Unable to get the root Canvas.");
|
||||
Point2I localPoint = globalToLocalCoord(root->getCursorPos());
|
||||
|
||||
//draw the close button
|
||||
Point2I tempUL;
|
||||
Point2I tempLR;
|
||||
S32 bmp = BmpStates * BmpClose;
|
||||
|
||||
if( mCanClose ) {
|
||||
if( mCloseButton.pointInRect( localPoint ) && mPressClose )
|
||||
bmp += BmpHilite;
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR(mTextureHandle, offset + mCloseButton.point, mBitmapBounds[bmp]);
|
||||
}
|
||||
|
||||
//draw the maximize button
|
||||
if( mMaximized )
|
||||
bmp = BmpStates * BmpNormal;
|
||||
else
|
||||
bmp = BmpStates * BmpMaximize;
|
||||
|
||||
if( mCanMaximize ) {
|
||||
if( mMaximizeButton.pointInRect( localPoint ) && mPressMaximize )
|
||||
bmp += BmpHilite;
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR( mTextureHandle, offset + mMaximizeButton.point, mBitmapBounds[bmp] );
|
||||
}
|
||||
|
||||
//draw the minimize button
|
||||
if( mMinimized )
|
||||
bmp = BmpStates * BmpNormal;
|
||||
else
|
||||
bmp = BmpStates * BmpMinimize;
|
||||
|
||||
if( mCanMinimize ) {
|
||||
if( mMinimizeButton.pointInRect( localPoint ) && mPressMinimize )
|
||||
bmp += BmpHilite;
|
||||
|
||||
dglClearBitmapModulation();
|
||||
dglDrawBitmapSR( mTextureHandle, offset + mMinimizeButton.point, mBitmapBounds[bmp] );
|
||||
}
|
||||
|
||||
if( !mMinimized )
|
||||
{
|
||||
//render the children
|
||||
renderChildControls( offset, updateRect );
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiWindowCtrl::initCursors()
|
||||
{
|
||||
if ( mUpDownCursor == NULL || mLeftRightCursor == NULL || mDefaultCursor == NULL || mNWSECursor == NULL || mNESWCursor == NULL )
|
||||
{
|
||||
SimObject *obj;
|
||||
obj = Sim::findObject("UpDownCursor");
|
||||
mUpDownCursor = dynamic_cast<GuiCursor*>(obj);
|
||||
obj = Sim::findObject("LeftRightCursor");
|
||||
mLeftRightCursor = dynamic_cast<GuiCursor*>(obj);
|
||||
obj = Sim::findObject("DefaultCursor");
|
||||
mDefaultCursor = dynamic_cast<GuiCursor*>(obj);
|
||||
obj = Sim::findObject("NESWCursor");
|
||||
mNESWCursor = dynamic_cast<GuiCursor*>(obj);
|
||||
obj = Sim::findObject("NWSECursor");
|
||||
mNWSECursor = dynamic_cast<GuiCursor*>(obj);
|
||||
|
||||
return( mUpDownCursor != NULL && mLeftRightCursor != NULL && mDefaultCursor != NULL && mNWSECursor != NULL && mNESWCursor != NULL );
|
||||
}
|
||||
else
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
|
||||
{
|
||||
showCursor = true;
|
||||
|
||||
if( initCursors() )
|
||||
{
|
||||
Point2I mousePos = lastGuiEvent.mousePoint;
|
||||
RectI winRect = mBounds;
|
||||
RectI rightRect = RectI( ( ( winRect.extent.x + winRect.point.x ) - mResizeRightWidth), winRect.point.y, mResizeRightWidth, winRect.extent.y );
|
||||
RectI bottomRect = RectI( winRect.point.x, ( ( winRect.point.y + winRect.extent.y ) - mResizeBottomHeight), winRect.extent.x , mResizeBottomHeight );
|
||||
|
||||
bool resizeRight = rightRect.pointInRect( mousePos );
|
||||
bool resizeBottom = bottomRect.pointInRect( mousePos );
|
||||
|
||||
if ( resizeRight && resizeBottom && mResizeHeight && mResizeWidth )
|
||||
cursor = mNWSECursor;
|
||||
else if ( resizeBottom && mResizeHeight )
|
||||
cursor = mUpDownCursor;
|
||||
else if ( resizeRight && mResizeWidth )
|
||||
cursor = mLeftRightCursor;
|
||||
else
|
||||
cursor = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
132
engine/gui/containers/guiWindowCtrl.h
Executable file
132
engine/gui/containers/guiWindowCtrl.h
Executable file
@ -0,0 +1,132 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GUIWINDOWCTRL_H_
|
||||
#define _GUIWINDOWCTRL_H_
|
||||
|
||||
#ifndef _GUITEXTCTRL_H_
|
||||
#include "gui/controls/guiTextCtrl.h"
|
||||
#endif
|
||||
|
||||
class GuiWindowCtrl : public GuiTextCtrl
|
||||
{
|
||||
private:
|
||||
typedef GuiTextCtrl Parent;
|
||||
|
||||
bool mResizeWidth;
|
||||
bool mResizeHeight;
|
||||
bool mCanMove;
|
||||
bool mCanClose;
|
||||
bool mCanMinimize;
|
||||
bool mCanMaximize;
|
||||
bool mPressClose;
|
||||
bool mPressMinimize;
|
||||
bool mPressMaximize;
|
||||
Point2I mMinSize;
|
||||
|
||||
GuiCursor* mDefaultCursor;
|
||||
GuiCursor* mLeftRightCursor;
|
||||
GuiCursor* mUpDownCursor;
|
||||
GuiCursor* mNWSECursor;
|
||||
GuiCursor* mNESWCursor;
|
||||
|
||||
StringTableEntry mCloseCommand;
|
||||
|
||||
S32 mTitleHeight;
|
||||
S32 mResizeRightWidth;
|
||||
S32 mResizeBottomHeight;
|
||||
|
||||
bool mMouseMovingWin;
|
||||
bool mMouseResizeWidth;
|
||||
bool mMouseResizeHeight;
|
||||
bool mMinimized;
|
||||
bool mMaximized;
|
||||
|
||||
Point2I mMouseDownPosition;
|
||||
RectI mOrigBounds;
|
||||
RectI mStandardBounds;
|
||||
|
||||
RectI mCloseButton;
|
||||
RectI mMinimizeButton;
|
||||
RectI mMaximizeButton;
|
||||
S32 mMinimizeIndex;
|
||||
S32 mTabIndex;
|
||||
|
||||
void PositionButtons(void);
|
||||
|
||||
protected:
|
||||
enum BitmapIndices
|
||||
{
|
||||
BmpClose,
|
||||
BmpMaximize,
|
||||
BmpNormal,
|
||||
BmpMinimize,
|
||||
|
||||
BmpCount
|
||||
};
|
||||
enum {
|
||||
BorderTopLeftKey = 12,
|
||||
BorderTopRightKey,
|
||||
BorderTopKey,
|
||||
BorderTopLeftNoKey,
|
||||
BorderTopRightNoKey,
|
||||
BorderTopNoKey,
|
||||
BorderLeft,
|
||||
BorderRight,
|
||||
BorderBottomLeft,
|
||||
BorderBottom,
|
||||
BorderBottomRight,
|
||||
NumBitmaps
|
||||
};
|
||||
|
||||
enum BitmapStates
|
||||
{
|
||||
BmpDefault = 0,
|
||||
BmpHilite,
|
||||
BmpDisabled,
|
||||
|
||||
BmpStates
|
||||
};
|
||||
RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
|
||||
TextureHandle mTextureHandle;
|
||||
|
||||
|
||||
void drawWinRect(const RectI &myRect);
|
||||
|
||||
public:
|
||||
GuiWindowCtrl();
|
||||
DECLARE_CONOBJECT(GuiWindowCtrl);
|
||||
static void initPersistFields();
|
||||
|
||||
bool onWake();
|
||||
void onSleep();
|
||||
|
||||
bool isMinimized(S32 &index);
|
||||
|
||||
bool initCursors();
|
||||
void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
|
||||
|
||||
void setFont(S32 fntTag);
|
||||
|
||||
GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
|
||||
void resize(const Point2I &newPosition, const Point2I &newExtent);
|
||||
|
||||
void onMouseDown(const GuiEvent &event);
|
||||
void onMouseDragged(const GuiEvent &event);
|
||||
void onMouseUp(const GuiEvent &event);
|
||||
|
||||
//only cycle tabs through the current window, so overwrite the method
|
||||
GuiControl* findNextTabable(GuiControl *curResponder, bool firstCall = true);
|
||||
GuiControl* findPrevTabable(GuiControl *curResponder, bool firstCall = true);
|
||||
|
||||
bool onKeyDown(const GuiEvent &event);
|
||||
|
||||
S32 getTabIndex(void) { return mTabIndex; }
|
||||
void selectWindow(void);
|
||||
|
||||
void onRender(Point2I offset, const RectI &updateRect);
|
||||
};
|
||||
|
||||
#endif //_GUI_WINDOW_CTRL_H
|
Reference in New Issue
Block a user