12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "gui/containers/guiFrameCtrl.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "gui/core/guiCanvas.h"
- #include "gfx/gfxDrawUtil.h"
- IMPLEMENT_CONOBJECT(GuiFrameSetCtrl);
- ConsoleDocClass( GuiFrameSetCtrl,
- "@brief A gui control allowing a window to be subdivided into panes, each "
- "of which displays a gui control child of the GuiFrameSetCtrl.\n\n"
- "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.\n\n"
- "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. For example, if a frameset had two columns and "
- "two rows but only three gui control children they would be assigned to "
- "frames as follows:\n"
- "<pre>\n"
- " 1 | 2\n"
- " -----\n"
- " 3 |\n"
- "</pre>\n"
- "The last frame would be blank.\n\n"
- "@tsexample\n"
- "new GuiFrameSetCtrl()\n"
- "{\n"
- " columns = \"3\";\n"
- " rows = \"2\";\n"
- " borderWidth = \"1\";\n"
- " borderColor = \"128 128 128\";\n"
- " borderEnable = \"dynamic\";\n"
- " borderMovable = \"dynamic\";\n"
- " autoBalance = \"1\";\n"
- " fudgeFactor = \"0\";\n"
- " //Properties not specific to this control have been omitted from this example.\n"
- "};\n"
- "@endtsexample\n\n"
- "@ingroup GuiContainers"
- );
- //-----------------------------------------------------------------------------
- ImplementEnumType( GuiFrameState,
- "\n\n"
- "@ingroup GuiContainers" )
- { GuiFrameSetCtrl::FRAME_STATE_ON, "alwaysOn" },
- { GuiFrameSetCtrl::FRAME_STATE_OFF, "alwaysOff" },
- { GuiFrameSetCtrl::FRAME_STATE_AUTO, "dynamic" }
- EndImplementEnumType;
- //-----------------------------------------------------------------------------
- void GuiFrameSetCtrl::initPersistFields()
- {
- docsURL;
- addField( "columns", TypeS32Vector, Offset(mColumnOffsets, GuiFrameSetCtrl),
- "A vector of column offsets (determines the width of each column)." );
- addField( "rows", TypeS32Vector, Offset(mRowOffsets, GuiFrameSetCtrl),
- "A vector of row offsets (determines the height of each row)." );
- addField( "borderWidth", TypeS32, Offset(mFramesetDetails.mBorderWidth, GuiFrameSetCtrl),
- "Width of interior borders between cells in pixels." );
- addField( "borderColor", TypeColorI, Offset(mFramesetDetails.mBorderColor, GuiFrameSetCtrl),
- "Color of interior borders between cells." );
- addField( "borderEnable", TYPEID< FrameState >(), Offset(mFramesetDetails.mBorderEnable, GuiFrameSetCtrl),
- "Controls whether frame borders are enabled.\n\nFrames use this value "
- "unless overridden for that frame using <i>%ctrl.frameBorder(index)</i>" );
- addField( "borderMovable", TYPEID< FrameState >(), Offset(mFramesetDetails.mBorderMovable, GuiFrameSetCtrl),
- "Controls whether borders can be dynamically repositioned with the mouse "
- "by the user.\n\nFrames use this value unless overridden for that frame "
- "using <i>%ctrl.frameMovable(index)</i>" );
- addField( "autoBalance", TypeBool, Offset(mAutoBalance, GuiFrameSetCtrl),
- "If true, row and column offsets are automatically scaled to match the "
- "new extents when the control is resized." );
- addField( "fudgeFactor", TypeS32, Offset(mFudgeFactor, GuiFrameSetCtrl),
- "Offset for row and column dividers in pixels" );
- Parent::initPersistFields();
- }
- //-----------------------------------------------------------------------------
- DefineEngineMethod( GuiFrameSetCtrl, frameBorder, void, ( S32 index, const char* state ), ( "dynamic" ),
- "Override the <i>borderEnable</i> setting for this frame.\n\n"
- "@param index Index of the frame to modify\n"
- "@param state New borderEnable state: \"on\", \"off\" or \"dynamic\"\n" )
- {
- object->frameBorderEnable( index, state );
- }
- DefineEngineMethod( GuiFrameSetCtrl, frameMovable, void, ( S32 index, const char* state ), ( "dynamic" ),
- "Override the <i>borderMovable</i> setting for this frame.\n\n"
- "@param index Index of the frame to modify\n"
- "@param state New borderEnable state: \"on\", \"off\" or \"dynamic\"\n" )
- {
- object->frameBorderMovable( index, state );
- }
- DefineEngineMethod( GuiFrameSetCtrl, frameMinExtent, void, ( S32 index, S32 width, S32 height ),,
- "Set the minimum width and height for the frame. It will not be possible "
- "for the user to resize the frame smaller than this.\n\n"
- "@param index Index of the frame to modify\n"
- "@param width Minimum width in pixels\n"
- "@param height Minimum height in pixels\n" )
- {
- Point2I extent( getMax( 0, width ), getMax( 0, height ) );
- object->frameMinExtent( index, extent);
- }
- DefineEngineMethod( GuiFrameSetCtrl, framePadding, void, ( S32 index, RectSpacingI padding ),,
- "Set the padding for this frame. Padding introduces blank space on the inside "
- "edge of the frame.\n\n"
- "@param index Index of the frame to modify\n"
- "@param padding Frame top, bottom, left, and right padding\n" )
- {
- object->framePadding( index, padding);
- }
- DefineEngineMethod( GuiFrameSetCtrl, getFramePadding, RectSpacingI, ( S32 index ),,
- "Get the padding for this frame.\n\n"
- "@param index Index of the frame to query\n" )
- {
- return object->getFramePadding( index );
- }
- DefineEngineMethod( GuiFrameSetCtrl, addColumn, void, (),,
- "Add a new column.\n\n" )
- {
- Vector<S32> * columns = object->columnOffsets();
- columns->push_back(0);
- object->balanceFrames();
- }
- DefineEngineMethod( GuiFrameSetCtrl, addRow, void, (),,
- "Add a new row.\n\n" )
- {
- Vector<S32> * rows = object->rowOffsets();
- rows->push_back(0);
- object->balanceFrames();
- }
- DefineEngineMethod( GuiFrameSetCtrl, removeColumn, void, (),,
- "Remove the last (rightmost) column.\n\n" )
- {
- Vector<S32> * columns = object->columnOffsets();
- if(columns->size() > 0)
- {
- columns->setSize(columns->size() - 1);
- object->balanceFrames();
- }
- else
- Con::errorf(ConsoleLogEntry::General, "No columns exist to remove");
- }
- DefineEngineMethod( GuiFrameSetCtrl, removeRow, void, (),,
- "Remove the last (bottom) row.\n\n" )
- {
- Vector<S32> * rows = object->rowOffsets();
- if(rows->size() > 0)
- {
- rows->setSize(rows->size() - 1);
- object->balanceFrames();
- }
- else
- Con::errorf(ConsoleLogEntry::General, "No rows exist to remove");
- }
- DefineEngineMethod( GuiFrameSetCtrl, getColumnCount, S32, (),,
- "Get the number of columns.\n\n"
- "@return The number of columns\n" )
- {
- return(object->columnOffsets()->size());
- }
- DefineEngineMethod( GuiFrameSetCtrl, getRowCount, S32, (),,
- "Get the number of rows.\n\n"
- "@return The number of rows\n" )
- {
- return(object->rowOffsets()->size());
- }
- DefineEngineMethod( GuiFrameSetCtrl, getColumnOffset, S32, ( S32 index ),,
- "Get the horizontal offset of a column.\n\n"
- "@param index Index of the column to query\n"
- "@return Column offset in pixels\n" )
- {
- if(index < 0 || index > object->columnOffsets()->size())
- {
- Con::errorf(ConsoleLogEntry::General, "Column index out of range");
- return(0);
- }
- return((*object->columnOffsets())[index]);
- }
- DefineEngineMethod( GuiFrameSetCtrl, getRowOffset, S32, ( S32 index ),,
- "Get the vertical offset of a row.\n\n"
- "@param index Index of the row to query\n"
- "@return Row offset in pixels\n" )
- {
- if(index < 0 || index > object->rowOffsets()->size())
- {
- Con::errorf(ConsoleLogEntry::General, "Row index out of range");
- return(0);
- }
- return((*object->rowOffsets())[index]);
- }
- DefineEngineMethod( GuiFrameSetCtrl, setColumnOffset, void, ( S32 index, S32 offset ),,
- "Set the horizontal offset of a column.\n\n"
- "Note that column offsets must always be in increasing order, and therefore "
- "this offset must be between the offsets of the colunns either side.\n"
- "@param index Index of the column to modify\n"
- "@param offset New column offset\n" )
- {
- Vector<S32> & columns = *(object->columnOffsets());
- if(index < 0 || index > columns.size())
- {
- Con::errorf(ConsoleLogEntry::General, "Column index out of range");
- return;
- }
- // 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();
- }
- DefineEngineMethod( GuiFrameSetCtrl, setRowOffset, void, ( S32 index, S32 offset ),,
- "Set the vertical offset of a row.\n\n"
- "Note that row offsets must always be in increasing order, and therefore "
- "this offset must be between the offsets of the rows either side.\n"
- "@param index Index of the row to modify\n"
- "@param offset New row offset\n" )
- {
- Vector<S32> & rows = *(object->rowOffsets());
- if(index < 0 || index > rows.size())
- {
- Con::errorf(ConsoleLogEntry::General, "Row index out of range");
- return;
- }
- // 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();
- }
- DefineEngineMethod( GuiFrameSetCtrl, updateSizes, void, (),,
- "Recalculates child control sizes." )
- {
- object->updateSizes();
- }
- //-----------------------------------------------------------------------------
- GuiFrameSetCtrl::GuiFrameSetCtrl()
- {
- VECTOR_SET_ASSOCIATION(mColumnOffsets);
- VECTOR_SET_ASSOCIATION(mRowOffsets);
- VECTOR_SET_ASSOCIATION(mFrameDetails);
- mAutoBalance = true;
- mIsContainer = 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<GuiControl *>(object);
- if (gc != NULL)
- {
- FrameDetail *detail = new FrameDetail;
- detail->mMinExtent = gc->getMinExtent();
- 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<SimObject *>::iterator soitr;
- VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
- for (soitr = begin(); soitr != end(); soitr++, fditr++)
- {
- if (*soitr == object)
- {
- delete *fditr;
- mFrameDetails.erase(fditr);
- break;
- }
- }
- }
- Parent::removeObject(object);
- if(isAwake())
- computeSizes();
- }
- //-----------------------------------------------------------------------------
- bool GuiFrameSetCtrl::resize(const Point2I &newPos, const Point2I &newExtent)
- {
- // rebalance before losing the old extent (if required)
- if (mAutoBalance == true)
- rebalance(newExtent);
- if( !Parent::resize(newPos, newExtent) )
- return false;
- // compute new sizing info for the frames - takes care of resizing the children
- computeSizes( !mAutoBalance );
- return true;
- }
- //-----------------------------------------------------------------------------
- void GuiFrameSetCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
- {
- GuiCanvas *pRoot = getRoot();
- if( !pRoot )
- return;
- Region curRegion = NONE;
- // Determine the region by mouse position.
- Point2I curMousePos = globalToLocalCoord(lastGuiEvent.mousePoint);
- curRegion = pointInAnyRegion(curMousePos);
- PlatformWindow *pWindow = pRoot->getPlatformWindow();
- AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible.");
- PlatformCursorController *pController = pWindow->getCursorController();
- AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
- switch (curRegion)
- {
- case VERTICAL_DIVIDER:
- // change to left-right cursor
- if(pRoot->mCursorChanged != PlatformCursorController::curResizeVert)
- {
- //*** We've already changed the cursor, so set it back before we change it again.
- if(pRoot->mCursorChanged != -1)
- pController->popCursor();
- //*** Now change the cursor shape
- pController->pushCursor(PlatformCursorController::curResizeVert);
- pRoot->mCursorChanged = PlatformCursorController::curResizeVert;
- }
- break;
- case HORIZONTAL_DIVIDER:
- // change to up-down cursor
- if(pRoot->mCursorChanged != PlatformCursorController::curResizeHorz)
- {
- //*** We've already changed the cursor, so set it back before we change it again.
- if(pRoot->mCursorChanged != -1)
- pController->popCursor();
- //*** Now change the cursor shape
- pController->pushCursor(PlatformCursorController::curResizeHorz);
- pRoot->mCursorChanged = PlatformCursorController::curResizeHorz;
- }
- break;
- case DIVIDER_INTERSECTION:
- // change to move cursor
- if(pRoot->mCursorChanged != PlatformCursorController::curResizeAll)
- {
- //*** We've already changed the cursor, so set it back before we change it again.
- if(pRoot->mCursorChanged != -1)
- pController->popCursor();
- //*** Now change the cursor shape
- pController->pushCursor(PlatformCursorController::curResizeAll);
- pRoot->mCursorChanged = PlatformCursorController::curResizeAll;
- }
- break;
- case NONE:
- default:
- if(pRoot->mCursorChanged != -1)
- {
- //*** We've already changed the cursor, so set it back before we change it again.
- pController->popCursor();
- pRoot->mCursorChanged = -1;
- }
- 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)
- {
- TORQUE_UNUSED(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();
- }
- }
- }
- //-----------------------------------------------------------------------------
- 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 )
- {
- Parent::onRender( offset, updateRect );
-
- drawDividers(offset);
- }
- //-----------------------------------------------------------------------------
- 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);
- }
- }
- }
- }
- 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);
- }
- //-----------------------------------------------------------------------------
- // point is assumed to already be in local coordinates.
- GuiFrameSetCtrl::Region GuiFrameSetCtrl::findHitRegion(const Point2I &point)
- {
- Vector<S32>::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<S32>::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<SimObject *>::iterator soitr;
- VectorPtr<FrameDetail *>::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<GuiControl *>(*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<SimObject *>::iterator soitr;
- VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
- for (soitr = begin(); soitr != end(); soitr++, fditr++, index--)
- {
- if (index == 0)
- {
- GuiControl *guiCtrl = dynamic_cast<GuiControl *>(*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 - ( getWidth() - 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 - ( getHeight() - 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<S32>::iterator itr;
- F32 totWidth = F32(getWidth() - vDividers * mFramesetDetails.mBorderWidth);
- F32 totHeight = F32(getHeight() - 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<SimObject *>::iterator soitr;
- VectorPtr<FrameDetail *>::iterator fditr = mFrameDetails.begin();
- GuiControl *gc;
- S32 column = 0;
- S32 row = 0;
- Point2I newPos;
- Point2I newExtent;
- // step through all the children
- for (soitr = begin(); soitr != end(); soitr++, fditr++)
- {
- // 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<GuiControl *>(*soitr);
- if (gc != NULL)
- {
- if (row >= rows)
- {
- // no more visible frames
- newPos = getExtent();
- 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 = getWidth() - 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 = getHeight() - mRowOffsets[row]; // last row
- else
- newExtent.y = mRowOffsets[row + 1] - mRowOffsets[row] - mFramesetDetails.mBorderWidth; // any other row
- if ( *fditr )
- {
- FrameDetail *fd = ( *fditr );
- // Account for Padding.
- newPos.x += fd->mPadding.left;
- newPos.y += fd->mPadding.top;
- newExtent.x -= ( fd->mPadding.left + fd->mPadding.right );
- newExtent.y -= ( fd->mPadding.top + fd->mPadding.bottom );
- }
- // 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)
- {
- TORQUE_UNUSED(newExtent);
- // look at old_width and old_height - current extent
- F32 widthScale = (F32)newExtent.x/(F32)getWidth();
- F32 heightScale = (F32)newExtent.y/(F32)getHeight();
- Vector<S32>::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] = { 0 };
- 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)) ? getWidth() : 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)) ? getHeight() : 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)) ? getWidth() : 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)) ? getHeight() : 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<SimObject *>::iterator soitr;
- VectorPtr<FrameDetail *>::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<GuiControl *>(*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<S32>::iterator itr;
- for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++)
- {
- r.point = Point2I(*itr - mFramesetDetails.mBorderWidth, mFudgeFactor) + offset;
- r.extent.set(mFramesetDetails.mBorderWidth, getHeight() - ( 2 * mFudgeFactor ) );
- GFX->getDrawUtil()->drawRectFill(r, mFramesetDetails.mBorderColor);
- }
- for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++)
- {
- r.point = Point2I(mFudgeFactor, *itr - mFramesetDetails.mBorderWidth) + offset;
- r.extent.set(getWidth() - ( 2 * mFudgeFactor ), mFramesetDetails.mBorderWidth);
- GFX->getDrawUtil()->drawRectFill(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)
- fd->mBorderEnable = EngineUnmarshallData< FrameState >()( state );
- 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)
- fd->mBorderMovable = EngineUnmarshallData< FrameState >()( state );
- 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;
- }
- void GuiFrameSetCtrl::framePadding(S32 index, const RectSpacingI &padding)
- {
- GuiControl *gc = NULL;
- FrameDetail *fd = NULL;
- if (findFrameContents(index, &gc, &fd) == true && fd != NULL)
- fd->mPadding = padding;
- }
- RectSpacingI GuiFrameSetCtrl::getFramePadding(S32 index)
- {
- GuiControl *gc = NULL;
- FrameDetail *fd = NULL;
- if (findFrameContents(index, &gc, &fd) == true && fd != NULL)
- return fd->mPadding;
- return RectSpacingI( 0, 0, 0, 0 );
- }
|