123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- //-----------------------------------------------------------------------------
- // 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/core/guiArrayCtrl.h"
- #include "console/console.h"
- #include "console/engineAPI.h"
- #include "gui/containers/guiScrollCtrl.h"
- #include "gfx/gfxDrawUtil.h"
- #include "gui/core/guiDefaultControlRender.h"
- IMPLEMENT_CONOBJECT(GuiArrayCtrl);
- ConsoleDocClass( GuiArrayCtrl,
- "@brief Abstract base class for controls that store and display multiple elements in a single view.\n\n"
- "You cannot actually instantiate this class. Instead you can use its childre:\n\n"
- "- GuiConsole\n"
- "- GuiTextListCtrl\n"
- "- GuiTreeViewCtrl\n"
- "- DbgFileView\n"
- "- CreatorTree\n"
- "This base class is primarily used by other internal classes or those dedicated to editors.\n\n"
- "@ingroup GuiCore\n"
- "@internal"
- );
- IMPLEMENT_CALLBACK( GuiArrayCtrl, onCellSelected, void, ( const Point2I& cell ), ( cell ),
- "Call when a cell in the array is selected (clicked).\n\n"
- "@param @cell Coordinates of the cell"
- );
- IMPLEMENT_CALLBACK( GuiArrayCtrl, onCellHighlighted, void, ( const Point2I& cell ), ( cell ),
- "Call when a cell in the array is highlighted (moused over).\n\n"
- "@param @cell Coordinates of the cell"
- );
- //-----------------------------------------------------------------------------
- GuiArrayCtrl::GuiArrayCtrl()
- {
- mActive = true;
- mCellSize.set(80, 30);
- mSize = Point2I(5, 30);
- mSelectedCell.set(-1, -1);
- mMouseOverCell.set(-1, -1);
- mHeaderDim.set(0, 0);
- mIsContainer = true;
- }
- //-----------------------------------------------------------------------------
- bool GuiArrayCtrl::onWake()
- {
- if (! Parent::onWake())
- return false;
- //get the font
- mFont = mProfile->mFont;
- return true;
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onSleep()
- {
- Parent::onSleep();
- mFont = NULL;
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::setSize(Point2I newSize)
- {
- mSize = newSize;
- Point2I newExtent(newSize.x * mCellSize.x + mHeaderDim.x, newSize.y * mCellSize.y + mHeaderDim.y);
- setExtent(newExtent);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::getScrollDimensions(S32 &cell_size, S32 &num_cells)
- {
- cell_size = mCellSize.y;
- num_cells = mSize.y;
- }
- //-----------------------------------------------------------------------------
- bool GuiArrayCtrl::cellSelected(Point2I cell)
- {
- if (cell.x < 0 || cell.x >= mSize.x || cell.y < 0 || cell.y >= mSize.y)
- {
- mSelectedCell = Point2I(-1,-1);
- return false;
- }
- mSelectedCell = cell;
- scrollSelectionVisible();
- onCellSelected(cell);
- setUpdate();
- return true;
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onCellSelected(Point2I cell)
- {
- // [rene, 21-Jan-11 ] clashes with callbacks defined in derived classes
- Con::executef(this, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y));
- onCellSelected_callback( cell );
- //call the console function
- execConsoleCallback();
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onCellHighlighted(Point2I cell)
- {
- onCellHighlighted_callback( cell );
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::setSelectedCell(Point2I cell)
- {
- cellSelected(cell);
- }
- //-----------------------------------------------------------------------------
- Point2I GuiArrayCtrl::getSelectedCell()
- {
- return mSelectedCell;
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::scrollSelectionVisible()
- {
- scrollCellVisible(mSelectedCell);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::scrollCellVisible(Point2I cell)
- {
- //make sure we have a parent
- //make sure we have a valid cell selected
- GuiScrollCtrl *parent = dynamic_cast<GuiScrollCtrl*>(getParent());
- if(!parent || cell.x < 0 || cell.y < 0)
- return;
- RectI cellBounds(cell.x * mCellSize.x, cell.y * mCellSize.y, mCellSize.x, mCellSize.y);
- parent->scrollRectVisible(cellBounds);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onRenderColumnHeaders(Point2I offset, Point2I parentOffset, Point2I headerDim)
- {
- if (mProfile->mBorder)
- {
- RectI cellR(offset.x + headerDim.x, parentOffset.y, getWidth() - headerDim.x, headerDim.y);
- GFX->getDrawUtil()->drawRectFill(cellR, mProfile->mBorderColor);
- }
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onRenderRowHeader(Point2I offset, Point2I parentOffset, Point2I headerDim, Point2I cell)
- {
- ColorI color;
- RectI cellR;
- if (cell.x % 2)
- color.set(255, 0, 0, 255);
- else
- color.set(0, 255, 0, 255);
- cellR.point.set(parentOffset.x, offset.y);
- cellR.extent.set(headerDim.x, mCellSize.y);
- GFX->getDrawUtil()->drawRectFill(cellR, color);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
- {
- ColorI color(255 * (cell.x % 2), 255 * (cell.y % 2), 255 * ((cell.x + cell.y) % 2), 255);
- if (selected)
- {
- color.set(255, 0, 0, 255);
- }
- else if (mouseOver)
- {
- color.set(0, 0, 255, 255);
- }
- //draw the cell
- RectI cellR(offset.x, offset.y, mCellSize.x, mCellSize.y);
- GFX->getDrawUtil()->drawRectFill(cellR, color);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onRender(Point2I offset, const RectI &updateRect)
- {
- // The unmodified offset which was passed into this method.
- const Point2I inOffset( offset );
- //Parent::onRender( offset, updateRect );
- // We render our fill, borders, and child controls ourself.
- // This allows us to render child controls after we render cells,
- // so child controls appear on-top, as they should.
- // Render our fill and borders.
- // This code from GuiControl::onRender().
- {
- RectI ctrlRect(offset, getExtent());
- //if opaque, fill the update rect with the fill color
- if ( mProfile->mOpaque )
- GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor);
- //if there's a border, draw the border
- if ( mProfile->mBorder )
- renderBorder(ctrlRect, mProfile);
- }
- //make sure we have a parent
- GuiControl *parent = getParent();
- if (! parent)
- return;
- S32 i, j;
- RectI headerClip;
- RectI clipRect(updateRect.point, updateRect.extent);
- Point2I parentOffset = parent->localToGlobalCoord(Point2I(0, 0));
- //if we have column headings
- if (mHeaderDim.y > 0)
- {
- headerClip.point.x = parentOffset.x + mHeaderDim.x;
- headerClip.point.y = parentOffset.y;
- headerClip.extent.x = clipRect.extent.x;// - headerClip.point.x; // This seems to fix some strange problems with some Gui's, bug? -pw
- headerClip.extent.y = mHeaderDim.y;
- if (headerClip.intersect(clipRect))
- {
- GFX->setClipRect(headerClip);
- //now render the header
- onRenderColumnHeaders(offset, parentOffset, mHeaderDim);
- clipRect.point.y += headerClip.extent.y;
- clipRect.extent.y -= headerClip.extent.y;
- }
- offset.y += mHeaderDim.y;
- }
- //if we have row headings
- if (mHeaderDim.x > 0)
- {
- clipRect.point.x = getMax(clipRect.point.x, parentOffset.x + mHeaderDim.x);
- offset.x += mHeaderDim.x;
- }
- //save the original for clipping the row headers
- RectI origClipRect = clipRect;
- for (j = 0; j < mSize.y; j++)
- {
- //skip until we get to a visible row
- if ((j + 1) * mCellSize.y + offset.y < updateRect.point.y)
- continue;
- //break once we've reached the last visible row
- if(j * mCellSize.y + offset.y >= updateRect.point.y + updateRect.extent.y)
- break;
- //render the header
- if (mHeaderDim.x > 0)
- {
- headerClip.point.x = parentOffset.x;
- headerClip.extent.x = mHeaderDim.x;
- headerClip.point.y = offset.y + j * mCellSize.y;
- headerClip.extent.y = mCellSize.y;
- if (headerClip.intersect(origClipRect))
- {
- GFX->setClipRect(headerClip);
- //render the row header
- onRenderRowHeader(Point2I(0, offset.y + j * mCellSize.y),
- Point2I(parentOffset.x, offset.y + j * mCellSize.y),
- mHeaderDim, Point2I(0, j));
- }
- }
- //render the cells for the row
- for (i = 0; i < mSize.x; i++)
- {
- //skip past columns off the left edge
- if ((i + 1) * mCellSize.x + offset.x < updateRect.point.x)
- continue;
- //break once past the last visible column
- if (i * mCellSize.x + offset.x >= updateRect.point.x + updateRect.extent.x)
- break;
- S32 cellx = offset.x + i * mCellSize.x;
- S32 celly = offset.y + j * mCellSize.y;
- RectI cellClip(cellx, celly, mCellSize.x, mCellSize.y);
- //make sure the cell is within the update region
- if (cellClip.intersect(clipRect))
- {
- //set the clip rect
- GFX->setClipRect(cellClip);
- //render the cell
- onRenderCell(Point2I(cellx, celly), Point2I(i, j),
- i == mSelectedCell.x && j == mSelectedCell.y,
- i == mMouseOverCell.x && j == mMouseOverCell.y);
- }
- }
- }
- // Done rendering cells.
- // Render child controls, if any, on top.
- renderChildControls( inOffset, updateRect );
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onMouseDown( const GuiEvent &event )
- {
- if ( !mActive || !mAwake || !mVisible )
- return;
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onMouseUp( const GuiEvent &event )
- {
- if ( !mActive || !mAwake || !mVisible )
- return;
- //let the guiControl method take care of the rest
- Parent::onMouseUp(event);
- Point2I pt = globalToLocalCoord(event.mousePoint);
- pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
- Point2I cell(
- (pt.x < 0 ? -1 : pt.x / mCellSize.x),
- (pt.y < 0 ? -1 : pt.y / mCellSize.y)
- );
- if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
- {
- //store the previously selected cell
- Point2I prevSelected = mSelectedCell;
- //select the new cell
- cellSelected(Point2I(cell.x, cell.y));
- //if we double clicked on the *same* cell, evaluate the altConsole Command
- if ( ( event.mouseClickCount > 1 ) && ( prevSelected == mSelectedCell ) )
- execAltConsoleCallback();
- }
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onMouseEnter(const GuiEvent &event)
- {
- Point2I pt = globalToLocalCoord(event.mousePoint);
- pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
- //get the cell
- Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
- if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
- {
- mMouseOverCell = cell;
- setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x,
- cell.y * mCellSize.y + mHeaderDim.y), mCellSize );
- onCellHighlighted(mMouseOverCell);
- }
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onMouseLeave(const GuiEvent & /*event*/)
- {
- setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x,
- mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize);
- mMouseOverCell.set(-1,-1);
- onCellHighlighted(mMouseOverCell);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onMouseDragged(const GuiEvent &event)
- {
- // for the array control, the behavior of onMouseDragged is the same
- // as on mouse moved - basically just recalc the current mouse over cell
- // and set the update regions if necessary
- GuiArrayCtrl::onMouseMove(event);
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onMouseMove(const GuiEvent &event)
- {
- Point2I pt = globalToLocalCoord(event.mousePoint);
- pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
- Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y));
- if (cell.x != mMouseOverCell.x || cell.y != mMouseOverCell.y)
- {
- if (mMouseOverCell.x != -1)
- {
- setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x,
- mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize);
- }
- if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
- {
- setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x,
- cell.y * mCellSize.y + mHeaderDim.y), mCellSize);
- mMouseOverCell = cell;
- }
- else
- mMouseOverCell.set(-1,-1);
- }
- onCellHighlighted(mMouseOverCell);
- }
- //-----------------------------------------------------------------------------
- bool GuiArrayCtrl::onKeyDown(const GuiEvent &event)
- {
- //if this control is a dead end, kill the event
- if ((! mVisible) || (! mActive) || (! mAwake)) return true;
- //get the parent
- S32 pageSize = 1;
- GuiControl *parent = getParent();
- if (parent && mCellSize.y > 0)
- {
- pageSize = getMax(1, (parent->getHeight() / mCellSize.y) - 1);
- }
- Point2I delta(0,0);
- switch (event.keyCode)
- {
- case KEY_LEFT:
- delta.set(-1, 0);
- break;
- case KEY_RIGHT:
- delta.set(1, 0);
- break;
- case KEY_UP:
- delta.set(0, -1);
- break;
- case KEY_DOWN:
- delta.set(0, 1);
- break;
- case KEY_PAGE_UP:
- delta.set(0, -pageSize);
- break;
- case KEY_PAGE_DOWN:
- delta.set(0, pageSize);
- break;
- case KEY_HOME:
- cellSelected( Point2I( 0, 0 ) );
- return( true );
- case KEY_END:
- cellSelected( Point2I( 0, mSize.y - 1 ) );
- return( true );
- default:
- return Parent::onKeyDown(event);
- }
- if (mSize.x < 1 || mSize.y < 1)
- return true;
- //select the first cell if no previous cell was selected
- if (mSelectedCell.x == -1 || mSelectedCell.y == -1)
- {
- cellSelected(Point2I(0,0));
- return true;
- }
- //select the cell
- Point2I cell = mSelectedCell;
- cell.x = getMax(0, getMin(mSize.x - 1, cell.x + delta.x));
- cell.y = getMax(0, getMin(mSize.y - 1, cell.y + delta.y));
- cellSelected(cell);
- return true;
- }
- //-----------------------------------------------------------------------------
- void GuiArrayCtrl::onRightMouseDown(const GuiEvent &event)
- {
- if ( !mActive || !mAwake || !mVisible )
- return;
- Parent::onRightMouseDown( event );
- Point2I cell;
- if( _findHitCell( event.mousePoint, cell ) )
- {
- char buf[32];
- dSprintf( buf, sizeof( buf ), "%d %d", event.mousePoint.x, event.mousePoint.y );
- // [rene, 21-Jan-11 ] clashes with callbacks defined in derived classes
- // Pass it to the console:
- Con::executef(this, "onRightMouseDown", Con::getIntArg(cell.x), Con::getIntArg(cell.y), buf);
- }
- }
- //-----------------------------------------------------------------------------
- bool GuiArrayCtrl::_findHitCell( const Point2I& pos, Point2I& cellOut )
- {
- Point2I pt = globalToLocalCoord( pos );
- pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y;
- Point2I cell( ( pt.x < 0 ? -1 : pt.x / mCellSize.x ), ( pt.y < 0 ? -1 : pt.y / mCellSize.y ) );
- if( cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y )
- {
- cellOut = cell;
- return true;
- }
- return false;
- }
|