//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "console/consoleTypes.h" #include "dgl/dgl.h" #include "gui/core/guiCanvas.h" #include "gui/containers/guiFrameCtrl.h" //----------------------------------------------------------------------------- IMPLEMENT_CONOBJECT(GuiFrameSetCtrl); //----------------------------------------------------------------------------- static EnumTable::Enums borderStateEnums[] = { { GuiFrameSetCtrl::FRAME_STATE_ON, "alwaysOn" }, { GuiFrameSetCtrl::FRAME_STATE_OFF, "alwaysOff" }, { GuiFrameSetCtrl::FRAME_STATE_AUTO, "dynamic" } }; static EnumTable gBorderStateTable(3, &borderStateEnums[0]); //----------------------------------------------------------------------------- void GuiFrameSetCtrl::initPersistFields() { Parent::initPersistFields(); addField("columns", TypeS32Vector, Offset(mColumnOffsets, GuiFrameSetCtrl)); addField("rows", TypeS32Vector, Offset(mRowOffsets, GuiFrameSetCtrl)); addField("borderWidth", TypeS32, Offset(mFramesetDetails.mBorderWidth, GuiFrameSetCtrl)); addField("borderColor", TypeColorI, Offset(mFramesetDetails.mBorderColor, GuiFrameSetCtrl)); addField("borderEnable", TypeEnum, Offset(mFramesetDetails.mBorderEnable, GuiFrameSetCtrl), 1, &gBorderStateTable); addField("borderMovable", TypeEnum, Offset(mFramesetDetails.mBorderMovable, GuiFrameSetCtrl), 1, &gBorderStateTable); addField("autoBalance", TypeBool, Offset(mAutoBalance, GuiFrameSetCtrl)); addField("fudgeFactor", TypeS32, Offset(mFudgeFactor, GuiFrameSetCtrl)); } //----------------------------------------------------------------------------- ConsoleMethod( GuiFrameSetCtrl, frameBorder, void, 3, 4, "(int index, bool enable=true)") { S32 index = dAtoi(argv[2]); if (argc == 3) object->frameBorderEnable(index); else object->frameBorderEnable(index, argv[3]); } ConsoleMethod( GuiFrameSetCtrl, frameMovable, void, 3, 4, "(int index, bool enable=true)" ) { S32 index = dAtoi(argv[2]); if (argc == 3) object->frameBorderMovable(index); else object->frameBorderMovable(index, argv[3]); } ConsoleMethod( GuiFrameSetCtrl, frameMinExtent, void, 5, 5, "(int index, int w, int h)") { Point2I extent(getMax(0, dAtoi(argv[3])), getMax(0, dAtoi(argv[4]))); object->frameMinExtent(dAtoi(argv[2]), extent); } ConsoleMethod( GuiFrameSetCtrl, addColumn, void, 2, 2, "") { Vector * columns = object->columnOffsets(); columns->push_back(0); object->balanceFrames(); } ConsoleMethod( GuiFrameSetCtrl, addRow, void, 2, 2, "") { Vector * rows = object->rowOffsets(); rows->push_back(0); object->balanceFrames(); } ConsoleMethod( GuiFrameSetCtrl, removeColumn, void, 2, 2, "") { Vector * columns = object->columnOffsets(); if(columns->size() > 0) { columns->setSize(columns->size() - 1); object->balanceFrames(); } else Con::errorf(ConsoleLogEntry::General, "No columns exist to remove"); } ConsoleMethod( GuiFrameSetCtrl, removeRow, void, 2, 2, "") { Vector * rows = object->rowOffsets(); if(rows->size() > 0) { rows->setSize(rows->size() - 1); object->balanceFrames(); } else Con::errorf(ConsoleLogEntry::General, "No rows exist to remove"); } ConsoleMethod( GuiFrameSetCtrl, getColumnCount, S32, 2, 2, "") { return(object->columnOffsets()->size()); } ConsoleMethod( GuiFrameSetCtrl, getRowCount, S32, 2, 2, "") { return(object->rowOffsets()->size()); } ConsoleMethod( GuiFrameSetCtrl, getColumnOffset, S32, 3, 3, "(int index)") { S32 index = dAtoi(argv[2]); if(index < 0 || index > object->columnOffsets()->size()) { Con::errorf(ConsoleLogEntry::General, "Column index out of range"); return(0); } return((*object->columnOffsets())[index]); } ConsoleMethod( GuiFrameSetCtrl, getRowOffset, S32, 3, 3, "(int index)") { S32 index = dAtoi(argv[2]); if(index < 0 || index > object->rowOffsets()->size()) { Con::errorf(ConsoleLogEntry::General, "Row index out of range"); return(0); } return((*object->rowOffsets())[index]); } ConsoleMethod( GuiFrameSetCtrl, setColumnOffset, void, 4, 4, "(int index, int offset)") { Vector & columns = *(object->columnOffsets()); S32 index = dAtoi(argv[2]); if(index < 0 || index > columns.size()) { Con::errorf(ConsoleLogEntry::General, "Column index out of range"); return; } // S32 offset = dAtoi(argv[3]); // check the offset if(((index > 0) && (offset < columns[index-1])) || ((index < (columns.size() - 1)) && (offset > columns[index+1]))) { Con::errorf(ConsoleLogEntry::General, "Invalid column offset"); return; } columns[index] = offset; object->updateSizes(); } ConsoleMethod( GuiFrameSetCtrl, setRowOffset, void, 4, 4, "(int index, int offset)") { Vector & rows = *(object->rowOffsets()); S32 index = dAtoi(argv[2]); if(index < 0 || index > rows.size()) { Con::errorf(ConsoleLogEntry::General, "Row index out of range"); return; } // S32 offset = dAtoi(argv[3]); // check the offset if(((index > 0) && (offset < rows[index-1])) || ((index < (rows.size() - 1)) && (offset > rows[index+1]))) { Con::errorf(ConsoleLogEntry::General, "Invalid row offset"); return; } rows[index] = offset; object->updateSizes(); } //----------------------------------------------------------------------------- GuiFrameSetCtrl::GuiFrameSetCtrl() { VECTOR_SET_ASSOCIATION(mColumnOffsets); VECTOR_SET_ASSOCIATION(mRowOffsets); VECTOR_SET_ASSOCIATION(mFrameDetails); mAutoBalance = true; init(1, 1, NULL, NULL); } //----------------------------------------------------------------------------- GuiFrameSetCtrl::GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]) { init(columns, rows, columnOffsets, rowOffsets); } //----------------------------------------------------------------------------- GuiFrameSetCtrl::~GuiFrameSetCtrl() { while (mFrameDetails.size() > 0) { delete mFrameDetails.last(); mFrameDetails.pop_back(); } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::addObject(SimObject *object) { AssertFatal(object != NULL, "GuiFrameSetCtrl::addObject: NULL object"); // assign the object to a frame - give it default frame details Parent::addObject(object); GuiControl *gc = dynamic_cast(object); if (gc != NULL) { FrameDetail *detail = new FrameDetail; detail->mMinExtent = gc->mMinExtent; mFrameDetails.push_back(detail); } else mFrameDetails.push_back(NULL); // resize it to fit into the frame to which it is assigned (if no frame for it, don't bother resizing) if(isAwake()) computeSizes(); } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::removeObject(SimObject *object) { if (object != NULL) { VectorPtr::iterator soitr; VectorPtr::iterator fditr = mFrameDetails.begin(); for (soitr = begin(); soitr != end(); soitr++, fditr++) { if (*soitr == object) { delete *fditr; mFrameDetails.erase(fditr); break; } } } Parent::removeObject(object); } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::resize(const Point2I &newPos, const Point2I &newExtent) { // rebalance before losing the old extent (if required) if (mAutoBalance == true) rebalance(newExtent); Parent::resize(newPos, newExtent); // compute new sizing info for the frames - takes care of resizing the children computeSizes( !mAutoBalance ); } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::getCursor(GuiCursor *&cursor, bool &visible, const GuiEvent &event) { visible = true; if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF && mFramesetDetails.mBorderMovable != FRAME_STATE_OFF && initCursors() == true) { Point2I curMousePos = globalToLocalCoord(event.mousePoint); Region curRegion = pointInAnyRegion(curMousePos); switch (curRegion) { case VERTICAL_DIVIDER: // change to left-right cursor cursor = mLeftRightCursor; break; case HORIZONTAL_DIVIDER: // change to up-down cursor cursor = mUpDownCursor; break; case DIVIDER_INTERSECTION: // change to move cursor cursor = mMoveCursor; break; case NONE: default: cursor = NULL; break; } } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::onMouseDown(const GuiEvent &event) { if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF && mFramesetDetails.mBorderMovable != FRAME_STATE_OFF) { // determine if a divider was hit Point2I curMousePos = globalToLocalCoord(event.mousePoint); findHitRegion(curMousePos); // sets mCurVerticalHit, mCurHorizontalHit, & mCurHitRegion if (mCurHitRegion != NONE) { mouseLock(); setFirstResponder(); setUpdate(); } } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::onMouseUp(const GuiEvent &event) { event; if (mCurHitRegion != NONE) { mCurHitRegion = NONE; mCurVerticalHit = NO_HIT; mCurHorizontalHit = NO_HIT; mouseUnlock(); setUpdate(); } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::onMouseDragged(const GuiEvent &event) { if (mCurHitRegion != NONE) { // identify the frames involved in the resizing, checking if they are resizable S32 indexes[4]; S32 activeFrames = findResizableFrames(indexes); if (activeFrames > 0) { S32 range[4]; // determine the range of movement, limiting as specified by individual frames computeMovableRange(mCurHitRegion, mCurVerticalHit, mCurHorizontalHit, activeFrames, indexes, range); Point2I curMousePos = globalToLocalCoord(event.mousePoint); switch (mCurHitRegion) { case VERTICAL_DIVIDER: mColumnOffsets[mCurVerticalHit] = getMin(getMax(range[0], curMousePos.x - mLocOnDivider.x), range[1]); break; case HORIZONTAL_DIVIDER: mRowOffsets[mCurHorizontalHit] = getMin(getMax(range[0], curMousePos.y - mLocOnDivider.y), range[1]); break; case DIVIDER_INTERSECTION: mColumnOffsets[mCurVerticalHit] = getMin(getMax(range[0], curMousePos.x - mLocOnDivider.x), range[1]); mRowOffsets[mCurHorizontalHit] = getMin(getMax(range[2], curMousePos.y - mLocOnDivider.y), range[3]); break; default: return; } computeSizes(); } } } void GuiFrameSetCtrl::onMouseEnter(const GuiEvent &event) { // change cursor to appropriate one... onMouseMove(event); } void GuiFrameSetCtrl::onMouseLeave(const GuiEvent &event) { // restore cursor to default... onMouseMove(event); } //----------------------------------------------------------------------------- bool GuiFrameSetCtrl::onAdd() { if (Parent::onAdd() == false) return(false); return(true); } bool GuiFrameSetCtrl::onWake() { if (Parent::onWake() == false) return(false); computeSizes(); return(true); } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::onRender(Point2I offset, const RectI &updateRect ) { RectI r(offset.x, offset.y, mBounds.extent.x, mBounds.extent.y); // draw the border of the frameset if specified if (mProfile->mOpaque) dglDrawRectFill(r, mProfile->mFillColor); drawDividers(offset); if (mProfile->mBorder) dglDrawRect(r, mProfile->mBorderColor); // draw the frame contents renderChildControls(offset, updateRect); } //----------------------------------------------------------------------------- bool GuiFrameSetCtrl::init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]) { if (columns != 0 && rows != 0) { mColumnOffsets.clear(); mRowOffsets.clear(); U32 i; for (i = 0; i < columns; i++) { if (columnOffsets == NULL) mColumnOffsets.push_back(0); else { AssertFatal(columnOffsets != NULL, "GuiFrameSetCtrl::init: NULL column offsets"); mColumnOffsets.push_back((U32)columnOffsets[i]); if (i > 0) { AssertFatal(mColumnOffsets[i - 1] < mColumnOffsets[i], "GuiFrameSetCtrl::init: column offsets must be monotonically increasing"); mColumnOffsets.clear(); return(false); } } } for (i = 0; i < rows; i++) { if (rowOffsets == NULL) mRowOffsets.push_back(0); else { AssertFatal(rowOffsets != NULL, "GuiFrameSetCtrl::init: NULL row offsets"); mRowOffsets.push_back((U32)rowOffsets[i]); if (i > 0) { AssertFatal(mRowOffsets[i - 1] < mRowOffsets[i], "GuiFrameSetCtrl::init: row offsets must be monotonically increasing"); mRowOffsets.clear(); return(false); } } } } mMoveCursor = NULL; mUpDownCursor = NULL; mLeftRightCursor = NULL; mDefaultCursor = NULL; initCursors(); mFramesetDetails.mBorderWidth = DEFAULT_BORDER_WIDTH; mFramesetDetails.mBorderEnable = FRAME_STATE_AUTO; mFramesetDetails.mBorderMovable = FRAME_STATE_AUTO; mAutoBalance = false; mFudgeFactor = 0; mCurHitRegion = NONE; mCurVerticalHit = NO_HIT; mCurHorizontalHit = NO_HIT; return(true); } //----------------------------------------------------------------------------- bool GuiFrameSetCtrl::initCursors() { if (mMoveCursor == NULL || mUpDownCursor == NULL || mLeftRightCursor == NULL || mDefaultCursor == NULL) { SimObject *obj; obj = Sim::findObject("MoveCursor"); mMoveCursor = dynamic_cast(obj); obj = Sim::findObject("UpDownCursor"); mUpDownCursor = dynamic_cast(obj); obj = Sim::findObject("LeftRightCursor"); mLeftRightCursor = dynamic_cast(obj); obj = Sim::findObject("DefaultCursor"); mDefaultCursor = dynamic_cast(obj); return(mMoveCursor != NULL && mUpDownCursor != NULL && mLeftRightCursor != NULL && mDefaultCursor != NULL); } else return(true); } //----------------------------------------------------------------------------- // point is assumed to already be in local coordinates. GuiFrameSetCtrl::Region GuiFrameSetCtrl::findHitRegion(const Point2I &point) { Vector::iterator itr; S32 i = 1; // step through vertical dividers for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++, i++) { if (hitVerticalDivider(*itr, point) == true) { mCurVerticalHit = i; mLocOnDivider.x = point.x - (*itr); break; } } i = 1; // step through horizontal dividers for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++, i++) { if (hitHorizontalDivider(*itr, point) == true) { mCurHorizontalHit = i; mLocOnDivider.y = point.y - (*itr); break; } } // now set type of hit... if (mCurVerticalHit != NO_HIT) { if (mCurHorizontalHit != NO_HIT) return(mCurHitRegion = DIVIDER_INTERSECTION); else return(mCurHitRegion = VERTICAL_DIVIDER); } else if (mCurHorizontalHit != NO_HIT) return(mCurHitRegion = HORIZONTAL_DIVIDER); else return(mCurHitRegion = NONE); } GuiFrameSetCtrl::Region GuiFrameSetCtrl::pointInAnyRegion(const Point2I &point) { Vector::iterator itr; S32 i = 1; S32 curVertHit = NO_HIT, curHorzHit = NO_HIT; Region result = NONE; // step through vertical dividers for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++, i++) { if (hitVerticalDivider(*itr, point) == true) { curVertHit = i; break; } } i = 1; // step through horizontal dividers for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++, i++) { if (hitHorizontalDivider(*itr, point) == true) { curHorzHit = i; break; } } // now select the type of region in which the point lies if (curVertHit != NO_HIT) { if (curHorzHit != NO_HIT) result = DIVIDER_INTERSECTION; else result = VERTICAL_DIVIDER; } else if (curHorzHit != NO_HIT) result = HORIZONTAL_DIVIDER; return(result); } //----------------------------------------------------------------------------- // indexes must have at least 4 entries. // This *may* modify mCurVerticalHit, mCurHorizontalHit, and mCurHitRegion if it // determines that movement is disabled by frame content. // If it does make such a change, it also needs to do the reset performed by // onMouseUp if it sets mCurHitRegion to NONE. S32 GuiFrameSetCtrl::findResizableFrames(S32 indexes[]) { AssertFatal(indexes != NULL, "GuiFrameSetCtrl::findResizableFrames: NULL indexes"); // first, find the column and row indexes of the affected columns/rows S32 validIndexes = 0; switch (mCurHitRegion) { case VERTICAL_DIVIDER: // columns indexes[0] = mCurVerticalHit - 1; indexes[1] = mCurVerticalHit; validIndexes = 2; break; case HORIZONTAL_DIVIDER: // rows indexes[0] = mCurHorizontalHit - 1; indexes[1] = mCurHorizontalHit; validIndexes = 2; break; case DIVIDER_INTERSECTION: // columns & rows indexes[0] = mCurVerticalHit - 1; // columns indexes[1] = mCurVerticalHit; indexes[2] = mCurHorizontalHit - 1; // rows indexes[3] = mCurHorizontalHit; validIndexes = 4; break; default: break; } // now, make sure these indexes are for movable frames VectorPtr::iterator soitr; VectorPtr::iterator fditr = mFrameDetails.begin(); GuiControl *gc; S32 column = 0; S32 row = 0; S32 columns = mColumnOffsets.size(); S32 rows = mRowOffsets.size(); for (soitr = begin(); soitr != end() && validIndexes > 0; soitr++, fditr++) { // don't continue if some of the frames are empty if (fditr == mFrameDetails.end()) break; // otherwise, check the gui elements for move-restrictions gc = dynamic_cast(*soitr); if (gc != NULL) { if (column == columns) { column = 0; row++; } if (row == rows) break; switch (mCurHitRegion) { case VERTICAL_DIVIDER: if ((column == indexes[0] || column == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) validIndexes = 0; break; case HORIZONTAL_DIVIDER: if ((row == indexes[0] || row == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) validIndexes = 0; break; case DIVIDER_INTERSECTION: if ((column == indexes[0] || column == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) { if ((row == indexes[2] || row == indexes[3]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) validIndexes = 0; else { mCurHitRegion = HORIZONTAL_DIVIDER; mCurVerticalHit = NO_HIT; indexes[0] = indexes[2]; indexes[1] = indexes[3]; validIndexes = 2; } } else if ((row == indexes[2] || row == indexes[3]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) { mCurHitRegion = VERTICAL_DIVIDER; mCurHorizontalHit = NO_HIT; validIndexes = 2; } break; default: return(0); } column++; } } if (validIndexes == 0) { mCurHitRegion = NONE; mCurVerticalHit = NO_HIT; mCurHorizontalHit = NO_HIT; mouseUnlock(); setUpdate(); } return(validIndexes); } //----------------------------------------------------------------------------- // This method locates the gui control and frame detail associated with a // particular frame index. bool GuiFrameSetCtrl::findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd) { AssertFatal(gc != NULL, "GuiFrameSetCtrl::findFrameContents: NULL gui control pointer"); AssertFatal(fd != NULL, "GuiFrameSetCtrl::findFrameContents: NULL frame detail pointer"); AssertFatal(*gc == NULL, "GuiFrameSetCtrl::findFrameContents: contents of gui control must be NULL"); AssertFatal(*fd == NULL, "GuiFrameSetCtrl::findFrameContents: contents of frame detail must be NULL"); if (index >= 0 && index < size()) { VectorPtr::iterator soitr; VectorPtr::iterator fditr = mFrameDetails.begin(); for (soitr = begin(); soitr != end(); soitr++, fditr++, index--) { if (index == 0) { GuiControl *guiCtrl = dynamic_cast(*soitr); if (guiCtrl != NULL) { *gc = guiCtrl; *fd = *fditr; return(true); } else break; } } } return(false); } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::computeSizes(bool balanceFrames) { S32 columns = mColumnOffsets.size(); S32 rows = mRowOffsets.size(); S32 vDividers = columns - 1; S32 hDividers = rows - 1; if ( !balanceFrames && mFrameDetails.size() == ( columns * rows ) ) { // This will do some goofy things if you allow this control to resize smaller than // the additive minimum extents of its frames--so don't. S32 index, delta; if ( columns > 1 ) { index = columns - 1; delta = mFrameDetails[index]->mMinExtent.x - ( mBounds.extent.x - mColumnOffsets[index] ); while ( delta > 0 ) { mColumnOffsets[index--] -= delta; if ( index >= 0 ) delta = mFrameDetails[index]->mMinExtent.x - ( mColumnOffsets[index + 1] - mColumnOffsets[index] ); else break; } } if ( rows > 1 ) { index = rows - 1; delta = mFrameDetails[columns * index]->mMinExtent.y - ( mBounds.extent.y - mRowOffsets[index] ); while ( delta > 0 ) { mRowOffsets[index--] -= delta; if ( index >= 0 ) delta = mFrameDetails[columns * index]->mMinExtent.y - ( mRowOffsets[index + 1] - mRowOffsets[index] ); else break; } } } // first, update the divider placement if necessary if (balanceFrames == true && mColumnOffsets.size() > 0 && mRowOffsets.size() > 0) { Vector::iterator itr; F32 totWidth = F32(mBounds.extent.x - vDividers * mFramesetDetails.mBorderWidth); F32 totHeight = F32(mBounds.extent.y - hDividers * mFramesetDetails.mBorderWidth); F32 frameWidth = totWidth/(F32)columns; F32 frameHeight = totHeight/(F32)rows; F32 i = 0.; for (itr = mColumnOffsets.begin(); itr != mColumnOffsets.end(); itr++, i++) *itr = (S32)(i * (frameWidth + (F32)mFramesetDetails.mBorderWidth)); i = 0.; for (itr = mRowOffsets.begin(); itr != mRowOffsets.end(); itr++, i++) *itr = (S32)(i * (frameHeight + (F32)mFramesetDetails.mBorderWidth)); } // now, resize the contents of each frame (and move content w/o a frame beyond visible range) VectorPtr::iterator soitr; GuiControl *gc; S32 column = 0; S32 row = 0; Point2I newPos; Point2I newExtent; // step through all the children for (soitr = begin(); soitr != end(); soitr++) { // column and row track the current frame being resized if (column == columns) { column = 0; row++; } // resize the contents if its a gui control... gc = dynamic_cast(*soitr); if (gc != NULL) { if (row >= rows) { // no more visible frames newPos = mBounds.extent; newExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT); gc->resize(newPos, newExtent); continue; } else { // determine x components of new position & extent newPos.x = mColumnOffsets[column]; if (column == vDividers) newExtent.x = mBounds.extent.x - mColumnOffsets[column]; // last column else newExtent.x = mColumnOffsets[column + 1] - mColumnOffsets[column] - mFramesetDetails.mBorderWidth; // any other column // determine y components of new position & extent newPos.y = mRowOffsets[row]; if (row == hDividers) newExtent.y = mBounds.extent.y - mRowOffsets[row]; // last row else newExtent.y = mRowOffsets[row + 1] - mRowOffsets[row] - mFramesetDetails.mBorderWidth; // any other row // apply the new position & extent gc->resize(newPos, newExtent); column++; } } } } //----------------------------------------------------------------------------- // this method looks at the previous offsets, and uses them to redistribute // the available height & width proportionally. void GuiFrameSetCtrl::rebalance(const Point2I &newExtent) { newExtent; // look at old_width and old_height - current extent F32 widthScale = (F32)newExtent.x/(F32)mBounds.extent.x; F32 heightScale = (F32)newExtent.y/(F32)mBounds.extent.y; Vector::iterator itr; // look at old width offsets for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++) // multiply each by new_width/old_width *itr = S32(F32(*itr) * widthScale); // look at old height offsets for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++) // multiply each by new_height/new_width *itr = S32(F32(*itr) * heightScale); } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[]) { S32 hardRanges[4]; switch (numIndexes) { case 2: switch (hitRegion) { case VERTICAL_DIVIDER: ranges[0] = hardRanges[0] = (vertHit <= 1) ? mFramesetDetails.mBorderWidth : mColumnOffsets[vertHit - 1] + mFramesetDetails.mBorderWidth; ranges[1] = hardRanges[1] = (vertHit >= (mColumnOffsets.size() - 1)) ? mBounds.extent.x : mColumnOffsets[vertHit + 1] - mFramesetDetails.mBorderWidth; break; case HORIZONTAL_DIVIDER: ranges[0] = hardRanges[0] = (horzHit <= 1) ? mFramesetDetails.mBorderWidth : mRowOffsets[horzHit - 1] + mFramesetDetails.mBorderWidth; ranges[1] = hardRanges[1] = (horzHit >= (mRowOffsets.size() - 1)) ? mBounds.extent.y : mRowOffsets[horzHit + 1] - mFramesetDetails.mBorderWidth; break; default: return; } break; case 4: if (hitRegion == DIVIDER_INTERSECTION) { ranges[0] = hardRanges[0] = (vertHit <= 1) ? mFramesetDetails.mBorderWidth : mColumnOffsets[vertHit - 1] + mFramesetDetails.mBorderWidth; ranges[1] = hardRanges[1] = (vertHit >= (mColumnOffsets.size() - 1)) ? mBounds.extent.x : mColumnOffsets[vertHit + 1] - mFramesetDetails.mBorderWidth; ranges[2] = hardRanges[2] = (horzHit <= 1) ? mFramesetDetails.mBorderWidth : mRowOffsets[horzHit - 1] + mFramesetDetails.mBorderWidth; ranges[3] = hardRanges[3] = (horzHit >= (mRowOffsets.size() - 1)) ? mBounds.extent.y : mRowOffsets[horzHit + 1] - mFramesetDetails.mBorderWidth; } else return; break; default: return; } // now that we have the hard ranges, reduce ranges based on minimum frame extents VectorPtr::iterator soitr; VectorPtr::iterator fditr = mFrameDetails.begin(); GuiControl *gc; S32 column = 0; S32 row = 0; S32 columns = mColumnOffsets.size(); S32 rows = mRowOffsets.size(); for (soitr = begin(); soitr != end(); soitr++, fditr++) { // only worry about visible frames if (column == columns) { column = 0; row++; } if (row == rows) return; gc = dynamic_cast(*soitr); if (gc != NULL) { // the gui control is in a visible frame, so look at its frame details if ((*fditr) != NULL) { switch (hitRegion) { case VERTICAL_DIVIDER: if (column == indexes[0]) ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.x); if (column == indexes[1]) ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.x); break; case HORIZONTAL_DIVIDER: if (row == indexes[0]) ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.y); if (row == indexes[1]) ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.y); break; case DIVIDER_INTERSECTION: if (column == indexes[0]) ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.x); if (column == indexes[1]) ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.x); if (row == indexes[2]) ranges[2] = getMax(ranges[2], hardRanges[2] + (*fditr)->mMinExtent.y); if (row == indexes[3]) ranges[3] = getMin(ranges[3], hardRanges[3] - (*fditr)->mMinExtent.y); break; default: return; } } column++; } } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::drawDividers(const Point2I &offset) { // draw the frame dividers, if they are enabled if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF) { RectI r; Vector::iterator itr; for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++) { r.point = Point2I(*itr - mFramesetDetails.mBorderWidth, mFudgeFactor) + offset; r.extent.set(mFramesetDetails.mBorderWidth, mBounds.extent.y - ( 2 * mFudgeFactor ) ); dglDrawRectFill(r, mFramesetDetails.mBorderColor); } for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++) { r.point = Point2I(mFudgeFactor, *itr - mFramesetDetails.mBorderWidth) + offset; r.extent.set(mBounds.extent.x - ( 2 * mFudgeFactor ), mFramesetDetails.mBorderWidth); dglDrawRectFill(r, mFramesetDetails.mBorderColor); } } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::frameBorderEnable(S32 index, const char *state) { GuiControl *gc = NULL; FrameDetail *fd = NULL; if (findFrameContents(index, &gc, &fd) == true && fd != NULL) { if (state != NULL) { // find the value for the detail member for (S32 i = 0; i < gBorderStateTable.size; i++) { if (dStrcmp(state, gBorderStateTable.table[i].label) == 0) fd->mBorderEnable = gBorderStateTable.table[i].index; } } else // defaults to AUTO if NULL passed in state fd->mBorderEnable = FRAME_STATE_AUTO; } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::frameBorderMovable(S32 index, const char *state) { GuiControl *gc = NULL; FrameDetail *fd = NULL; if (findFrameContents(index, &gc, &fd) == true && fd != NULL) { if (state != NULL) { // find the value for the detail member for (S32 i = 0; i < gBorderStateTable.size; i++) { if (dStrcmp(state, gBorderStateTable.table[i].label) == 0) fd->mBorderMovable = gBorderStateTable.table[i].index; } } else // defaults to AUTO if NULL passed in state fd->mBorderMovable = FRAME_STATE_AUTO; } } //----------------------------------------------------------------------------- void GuiFrameSetCtrl::frameMinExtent(S32 index, const Point2I &extent) { GuiControl *gc = NULL; FrameDetail *fd = NULL; if (findFrameContents(index, &gc, &fd) == true && fd != NULL) fd->mMinExtent = extent; }