mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
1501 lines
45 KiB
C++
1501 lines
45 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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 "gui/core/guiCanvas.h"
|
|
#include "gui/controls/guiPopUpCtrl.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "console/engineAPI.h"
|
|
#include "gui/core/guiDefaultControlRender.h"
|
|
#include "gfx/primBuilder.h"
|
|
#include "gfx/gfxDrawUtil.h"
|
|
#include "console/engineAPI.h"
|
|
|
|
static ColorI colorWhite(255,255,255); // Added
|
|
|
|
// Function to return the number of columns in 'string' given delimeters in 'set'
|
|
static U32 getColumnCount(const char *string, const char *set)
|
|
{
|
|
U32 count = 0;
|
|
U8 last = 0;
|
|
while(*string)
|
|
{
|
|
last = *string++;
|
|
|
|
for(U32 i =0; set[i]; i++)
|
|
{
|
|
if(last == set[i])
|
|
{
|
|
count++;
|
|
last = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(last)
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
// Function to return the 'index' column from 'string' given delimeters in 'set'
|
|
static const char *getColumn(const char *string, char* returnbuff, U32 index, const char *set)
|
|
{
|
|
U32 sz;
|
|
while(index--)
|
|
{
|
|
if(!*string)
|
|
return "";
|
|
sz = dStrcspn(string, set);
|
|
if (string[sz] == 0)
|
|
return "";
|
|
string += (sz + 1);
|
|
}
|
|
sz = dStrcspn(string, set);
|
|
if (sz == 0)
|
|
return "";
|
|
char *ret = returnbuff;
|
|
dStrncpy(ret, string, sz);
|
|
ret[sz] = '\0';
|
|
return ret;
|
|
}
|
|
|
|
GuiPopUpBackgroundCtrl::GuiPopUpBackgroundCtrl(GuiPopUpMenuCtrl *ctrl, GuiPopupTextListCtrl *textList)
|
|
{
|
|
mPopUpCtrl = ctrl;
|
|
mTextList = textList;
|
|
}
|
|
|
|
void GuiPopUpBackgroundCtrl::onMouseDown(const GuiEvent &event)
|
|
{
|
|
mPopUpCtrl->mBackgroundCancel = true; // Set that the user didn't click within the text list. Replaces the line above.
|
|
mPopUpCtrl->closePopUp();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
GuiPopupTextListCtrl::GuiPopupTextListCtrl()
|
|
{
|
|
mPopUpCtrl = NULL;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
GuiPopupTextListCtrl::GuiPopupTextListCtrl(GuiPopUpMenuCtrl *ctrl)
|
|
{
|
|
mPopUpCtrl = ctrl;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
//void GuiPopUpTextListCtrl::onCellSelected( Point2I /*cell*/ )
|
|
//{
|
|
// // Do nothing, the parent control will take care of everything...
|
|
//}
|
|
void GuiPopupTextListCtrl::onCellSelected( Point2I cell )
|
|
{
|
|
// The old function is above. This new one will only call the the select
|
|
// functions if we were not cancelled by a background click.
|
|
|
|
// Check if we were cancelled by the user clicking on the Background ie: anywhere
|
|
// other than within the text list.
|
|
if(mPopUpCtrl->mBackgroundCancel)
|
|
return;
|
|
|
|
if( isMethod( "onSelect" ) )
|
|
Con::executef(this, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y));
|
|
|
|
//call the console function
|
|
execConsoleCallback();
|
|
//if (mConsoleCommand[0])
|
|
// Con::evaluate(mConsoleCommand, false);
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool GuiPopupTextListCtrl::onKeyDown(const GuiEvent &event)
|
|
{
|
|
//if the control is a dead end, don't process the input:
|
|
if ( !mVisible || !mActive || !mAwake )
|
|
return false;
|
|
|
|
//see if the key down is a <return> or not
|
|
if ( event.modifier == 0 )
|
|
{
|
|
if ( event.keyCode == KEY_RETURN )
|
|
{
|
|
mPopUpCtrl->closePopUp();
|
|
return true;
|
|
}
|
|
else if ( event.keyCode == KEY_ESCAPE )
|
|
{
|
|
mSelectedCell.set( -1, -1 );
|
|
mPopUpCtrl->closePopUp();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//otherwise, pass the event to it's parent
|
|
return Parent::onKeyDown(event);
|
|
}
|
|
|
|
void GuiPopupTextListCtrl::onMouseUp(const GuiEvent &event)
|
|
{
|
|
Parent::onMouseUp( event );
|
|
mPopUpCtrl->closePopUp();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopupTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
|
|
{
|
|
Point2I size;
|
|
getCellSize( size );
|
|
|
|
// Render a background color for the cell
|
|
if ( mouseOver )
|
|
{
|
|
RectI cellR( offset.x, offset.y, size.x, size.y );
|
|
GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorHL );
|
|
|
|
}
|
|
else if ( selected )
|
|
{
|
|
RectI cellR( offset.x, offset.y, size.x, size.y );
|
|
GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorSEL );
|
|
}
|
|
|
|
// Define the default x offset for the text
|
|
U32 textXOffset = offset.x + mProfile->mTextOffset.x;
|
|
|
|
// Do we also draw a colored box beside the text?
|
|
ColorI boxColor;
|
|
bool drawbox = mPopUpCtrl->getColoredBox( boxColor, mList[cell.y].id);
|
|
if(drawbox)
|
|
{
|
|
Point2I coloredboxsize(15,10);
|
|
RectI boxBounds(offset.x + mProfile->mTextOffset.x, offset.y+2, coloredboxsize.x, coloredboxsize.y);
|
|
GFX->getDrawUtil()->drawRectFill(boxBounds, boxColor);
|
|
GFX->getDrawUtil()->drawRect(boxBounds, ColorI(0,0,0));
|
|
|
|
textXOffset += coloredboxsize.x + mProfile->mTextOffset.x;
|
|
}
|
|
|
|
ColorI fontColor;
|
|
mPopUpCtrl->getFontColor( fontColor, mList[cell.y].id, selected, mouseOver );
|
|
|
|
GFX->getDrawUtil()->setBitmapModulation( fontColor );
|
|
//GFX->drawText( mFont, Point2I( offset.x + 4, offset.y ), mList[cell.y].text );
|
|
|
|
// Get the number of columns in the cell
|
|
S32 colcount = getColumnCount(mList[cell.y].text, "\t");
|
|
|
|
// Are there two or more columns?
|
|
if(colcount >= 2)
|
|
{
|
|
char buff[256];
|
|
|
|
// Draw the first column
|
|
getColumn(mList[cell.y].text, buff, 0, "\t");
|
|
GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
|
|
|
|
// Draw the second column to the right
|
|
getColumn(mList[cell.y].text, buff, 1, "\t");
|
|
S32 txt_w = mFont->getStrWidth(buff);
|
|
|
|
GFX->getDrawUtil()->drawText( mFont, Point2I( offset.x+size.x-mProfile->mTextOffset.x-txt_w, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
|
|
|
|
} else
|
|
{
|
|
GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), mList[cell.y].text ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'.
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
IMPLEMENT_CONOBJECT(GuiPopUpMenuCtrl);
|
|
|
|
ConsoleDocClass( GuiPopUpMenuCtrl,
|
|
"@brief A control that allows to select a value from a drop-down list.\n\n"
|
|
|
|
"For a nearly identical GUI with additional features, use GuiPopUpMenuCtrlEx.\n\n"
|
|
|
|
"@tsexample\n"
|
|
"new GuiPopUpMenuCtrl()\n"
|
|
"{\n"
|
|
" maxPopupHeight = \"200\";\n"
|
|
" sbUsesNAColor = \"0\";\n"
|
|
" reverseTextList = \"0\";\n"
|
|
" bitmapBounds = \"16 16\";\n"
|
|
" maxLength = \"1024\";\n"
|
|
" position = \"56 31\";\n"
|
|
" extent = \"64 64\";\n"
|
|
" minExtent = \"8 2\";\n"
|
|
" profile = \"GuiPopUpMenuProfile\";\n"
|
|
" tooltipProfile = \"GuiToolTipProfile\";\n"
|
|
"};\n"
|
|
"@endtsexample\n\n"
|
|
|
|
"@note This is definitely going to be deprecated soon.\n\n"
|
|
|
|
"@see GuiPopUpMenuCtrlEx for more features and better explanations.\n"
|
|
|
|
"@ingroup GuiControls\n");
|
|
|
|
GuiPopUpMenuCtrl::GuiPopUpMenuCtrl(void)
|
|
{
|
|
VECTOR_SET_ASSOCIATION(mEntries);
|
|
VECTOR_SET_ASSOCIATION(mSchemes);
|
|
|
|
mSelIndex = -1;
|
|
mActive = true;
|
|
mMaxPopupHeight = 200;
|
|
mScrollDir = GuiScrollCtrl::None;
|
|
mScrollCount = 0;
|
|
mLastYvalue = 0;
|
|
mIncValue = 0;
|
|
mRevNum = 0;
|
|
mInAction = false;
|
|
mMouseOver = false; // Added
|
|
mRenderScrollInNA = false; // Added
|
|
mBackgroundCancel = false; // Added
|
|
mReverseTextList = false; // Added - Don't reverse text list if displaying up
|
|
mBitmapName = StringTable->EmptyString(); // Added
|
|
mBitmapBounds.set(16, 16); // Added
|
|
mIdMax = -1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
GuiPopUpMenuCtrl::~GuiPopUpMenuCtrl()
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::initPersistFields(void)
|
|
{
|
|
addField("maxPopupHeight", TypeS32, Offset(mMaxPopupHeight, GuiPopUpMenuCtrl));
|
|
addField("sbUsesNAColor", TypeBool, Offset(mRenderScrollInNA, GuiPopUpMenuCtrl));
|
|
addField("reverseTextList", TypeBool, Offset(mReverseTextList, GuiPopUpMenuCtrl));
|
|
addField("bitmap", TypeFilename, Offset(mBitmapName, GuiPopUpMenuCtrl));
|
|
addField("bitmapBounds", TypePoint2I, Offset(mBitmapBounds, GuiPopUpMenuCtrl));
|
|
|
|
Parent::initPersistFields();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, add, void, (const char * name, S32 idNum, U32 scheme), ("", -1, 0), "(string name, int idNum, int scheme=0)")
|
|
{
|
|
object->addEntry(name, idNum, scheme);
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, addScheme, void, (U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL), ,
|
|
"(int id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL)")
|
|
{
|
|
|
|
object->addScheme( id, fontColor, fontColorHL, fontColorSEL );
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, getText, const char*, (), , "")
|
|
{
|
|
return object->getText();
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, clear, void, (), , "Clear the popup list.")
|
|
{
|
|
object->clear();
|
|
}
|
|
|
|
//FIXME: clashes with SimSet.sort
|
|
DefineEngineMethod(GuiPopUpMenuCtrl, sort, void, (), , "Sort the list alphabetically.")
|
|
{
|
|
object->sort();
|
|
}
|
|
|
|
// Added to sort the entries by ID
|
|
DefineEngineMethod(GuiPopUpMenuCtrl, sortID, void, (), , "Sort the list by ID.")
|
|
{
|
|
object->sortID();
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, forceOnAction, void, (), , "")
|
|
{
|
|
object->onAction();
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, forceClose, void, (), , "")
|
|
{
|
|
object->closePopUp();
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, getSelected, S32, (), , "Gets the selected index")
|
|
{
|
|
return object->getSelected();
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, setSelected, void, (S32 id, bool scriptCallback), (true), "(int id, [scriptCallback=true])")
|
|
{
|
|
object->setSelected( id, scriptCallback );
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, setFirstSelected, void, (bool scriptCallback), (true), "([scriptCallback=true])")
|
|
{
|
|
object->setFirstSelected( scriptCallback );
|
|
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, setNoneSelected, void, (), , "")
|
|
{
|
|
object->setNoneSelected();
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, getTextById, const char*, (S32 id), , "(int id)")
|
|
{
|
|
return(object->getTextById(id));
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, changeTextById, void, ( S32 id, const char * text ), , "( int id, string text )" )
|
|
{
|
|
object->setEntryText( id, text );
|
|
}
|
|
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, setEnumContent, void, (const char * className, const char * enumName), , "(string class, string enum)"
|
|
"This fills the popup with a classrep's field enumeration type info.\n\n"
|
|
"More of a helper function than anything. If console access to the field list is added, "
|
|
"at least for the enumerated types, then this should go away..")
|
|
{
|
|
AbstractClassRep * classRep = AbstractClassRep::getClassList();
|
|
|
|
// walk the class list to get our class
|
|
while(classRep)
|
|
{
|
|
if(!dStricmp(classRep->getClassName(), className))
|
|
break;
|
|
classRep = classRep->getNextClass();
|
|
}
|
|
|
|
// get it?
|
|
if(!classRep)
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", className);
|
|
return;
|
|
}
|
|
|
|
// walk the fields to check for this one (findField checks StringTableEntry ptrs...)
|
|
U32 i;
|
|
for(i = 0; i < classRep->mFieldList.size(); i++)
|
|
if(!dStricmp(classRep->mFieldList[i].pFieldname, enumName))
|
|
break;
|
|
|
|
// found it?
|
|
if(i == classRep->mFieldList.size())
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", enumName, className);
|
|
return;
|
|
}
|
|
|
|
const AbstractClassRep::Field & field = classRep->mFieldList[i];
|
|
ConsoleBaseType* conType = ConsoleBaseType::getType( field.type );
|
|
|
|
// check the type
|
|
if( !conType->getEnumTable() )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", enumName, className);
|
|
return;
|
|
}
|
|
|
|
// fill it
|
|
const EngineEnumTable& table = *( conType->getEnumTable() );
|
|
const U32 numValues = table.getNumValues();
|
|
|
|
for(i = 0; i < numValues; i++)
|
|
object->addEntry( table[i].getName(), table[i] );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, findText, S32, (const char * text), , "(string text)"
|
|
"Returns the position of the first entry containing the specified text or -1 if not found.")
|
|
{
|
|
return( object->findText( text ) );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, size, S32, (), , "Get the size of the menu - the number of entries in it.")
|
|
{
|
|
return( object->getNumEntries() );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, replaceText, void, (bool doReplaceText), , "(bool doReplaceText)")
|
|
{
|
|
object->replaceText(S32(doReplaceText));
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added
|
|
bool GuiPopUpMenuCtrl::onWake()
|
|
{
|
|
if ( !Parent::onWake() )
|
|
return false;
|
|
|
|
// Set the bitmap for the popup.
|
|
setBitmap( mBitmapName );
|
|
|
|
// Now update the Form Control's bitmap array, and possibly the child's too
|
|
mProfile->constructBitmapArray();
|
|
|
|
if ( mProfile->getChildrenProfile() )
|
|
mProfile->getChildrenProfile()->constructBitmapArray();
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool GuiPopUpMenuCtrl::onAdd()
|
|
{
|
|
if ( !Parent::onAdd() )
|
|
return false;
|
|
mSelIndex = -1;
|
|
mReplaceText = true;
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::onSleep()
|
|
{
|
|
mTextureNormal = NULL; // Added
|
|
mTextureDepressed = NULL; // Added
|
|
Parent::onSleep();
|
|
closePopUp(); // Tests in function.
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::clear()
|
|
{
|
|
mEntries.setSize(0);
|
|
setText("");
|
|
mSelIndex = -1;
|
|
mRevNum = 0;
|
|
mIdMax = -1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::clearEntry( S32 entry )
|
|
{
|
|
if( entry == -1 )
|
|
return;
|
|
|
|
U32 i = 0;
|
|
for ( ; i < mEntries.size(); i++ )
|
|
{
|
|
if ( mEntries[i].id == entry )
|
|
break;
|
|
}
|
|
|
|
mEntries.erase( i );
|
|
|
|
if( mEntries.size() <= 0 )
|
|
{
|
|
mEntries.setSize(0);
|
|
setText("");
|
|
mSelIndex = -1;
|
|
mRevNum = 0;
|
|
}
|
|
else
|
|
{
|
|
if (entry < mSelIndex)
|
|
{
|
|
mSelIndex--;
|
|
}
|
|
else if( entry == mSelIndex )
|
|
{
|
|
setText("");
|
|
mSelIndex = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
DefineEngineMethod( GuiPopUpMenuCtrl, clearEntry, void, (S32 entry), , "(S32 entry)")
|
|
{
|
|
object->clearEntry(entry);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static S32 QSORT_CALLBACK textCompare(const void *a,const void *b)
|
|
{
|
|
GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a);
|
|
GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b);
|
|
return (dStrnatcasecmp(ea->buf, eb->buf));
|
|
}
|
|
|
|
// Added to sort by entry ID
|
|
//------------------------------------------------------------------------------
|
|
static S32 QSORT_CALLBACK idCompare(const void *a,const void *b)
|
|
{
|
|
GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a);
|
|
GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b);
|
|
return ( (ea->id < eb->id) ? -1 : ((ea->id > eb->id) ? 1 : 0) );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added
|
|
void GuiPopUpMenuCtrl::setBitmap( const char *name )
|
|
{
|
|
mBitmapName = StringTable->insert( name );
|
|
if ( !isAwake() )
|
|
return;
|
|
|
|
if ( *mBitmapName )
|
|
{
|
|
char buffer[1024];
|
|
char *p;
|
|
dStrcpy(buffer, name, 1024);
|
|
p = buffer + dStrlen(buffer);
|
|
S32 pLen = 1024 - dStrlen(buffer);
|
|
|
|
dStrcpy(p, "_n", pLen);
|
|
mTextureNormal = GFXTexHandle( (StringTableEntry)buffer, &GFXDefaultGUIProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__) );
|
|
|
|
dStrcpy(p, "_d", pLen);
|
|
mTextureDepressed = GFXTexHandle( (StringTableEntry)buffer, &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__) );
|
|
if ( !mTextureDepressed )
|
|
mTextureDepressed = mTextureNormal;
|
|
}
|
|
else
|
|
{
|
|
mTextureNormal = NULL;
|
|
mTextureDepressed = NULL;
|
|
}
|
|
setUpdate();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::sort()
|
|
{
|
|
S32 selId = getSelected();
|
|
|
|
S32 size = mEntries.size();
|
|
if( size > 0 )
|
|
dQsort( mEntries.address(), size, sizeof(Entry), textCompare);
|
|
|
|
if( selId != -1 )
|
|
setSelected( selId, false );
|
|
}
|
|
|
|
// Added to sort by entry ID
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::sortID()
|
|
{
|
|
S32 selId = getSelected();
|
|
|
|
S32 size = mEntries.size();
|
|
if( size > 0 )
|
|
dQsort( mEntries.address(), size, sizeof(Entry), idCompare);
|
|
|
|
if( selId != -1 )
|
|
setSelected( selId, false );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::addEntry( const char *buf, S32 id, U32 scheme )
|
|
{
|
|
if( !buf )
|
|
{
|
|
//Con::printf( "GuiPopupMenuCtrlEx::addEntry - Invalid buffer!" );
|
|
return;
|
|
}
|
|
|
|
// Ensure that there are no other entries with exactly the same name
|
|
for ( U32 i = 0; i < mEntries.size(); i++ )
|
|
{
|
|
if ( dStrcmp( mEntries[i].buf, buf ) == 0 )
|
|
return;
|
|
}
|
|
|
|
// If we don't give an id, create one from mIdMax
|
|
if( id == -1 )
|
|
id = mIdMax + 1;
|
|
|
|
// Increase mIdMax when an id is greater than it
|
|
if( id > mIdMax )
|
|
mIdMax = id;
|
|
|
|
Entry e;
|
|
dStrcpy( e.buf, buf, 256 );
|
|
e.id = id;
|
|
e.scheme = scheme;
|
|
|
|
// see if there is a shortcut key
|
|
char * cp = dStrchr( e.buf, '~' );
|
|
e.ascii = cp ? cp[1] : 0;
|
|
|
|
// See if there is a colour box defined with the text
|
|
char *cb = dStrchr( e.buf, '|' );
|
|
if ( cb )
|
|
{
|
|
e.usesColorBox = true;
|
|
cb[0] = '\0';
|
|
|
|
char* red = &cb[1];
|
|
cb = dStrchr(red, '|');
|
|
cb[0] = '\0';
|
|
char* green = &cb[1];
|
|
cb = dStrchr(green, '|');
|
|
cb[0] = '\0';
|
|
char* blue = &cb[1];
|
|
|
|
U32 r = dAtoi(red);
|
|
U32 g = dAtoi(green);
|
|
U32 b = dAtoi(blue);
|
|
|
|
e.colorbox = ColorI(r,g,b);
|
|
|
|
}
|
|
else
|
|
{
|
|
e.usesColorBox = false;
|
|
}
|
|
|
|
mEntries.push_back(e);
|
|
|
|
if ( mInAction && mTl )
|
|
{
|
|
// Add the new entry:
|
|
mTl->addEntry( e.id, e.buf );
|
|
repositionPopup();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::addScheme( U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL )
|
|
{
|
|
if ( !id )
|
|
return;
|
|
|
|
Scheme newScheme;
|
|
newScheme.id = id;
|
|
newScheme.fontColor = fontColor;
|
|
newScheme.fontColorHL = fontColorHL;
|
|
newScheme.fontColorSEL = fontColorSEL;
|
|
|
|
mSchemes.push_back( newScheme );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
S32 GuiPopUpMenuCtrl::getSelected()
|
|
{
|
|
if (mSelIndex == -1)
|
|
return 0;
|
|
return mEntries[mSelIndex].id;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool GuiPopUpMenuCtrl::setEntryText( S32 id, const char* buf )
|
|
{
|
|
const U32 numEntries = getNumEntries();
|
|
for( U32 i = 0; i < numEntries; i++ )
|
|
{
|
|
if( mEntries[ i ].id == id )
|
|
{
|
|
Entry& entry = mEntries[ i ];
|
|
dStrncpy( entry.buf, buf, sizeof( entry.buf ) );
|
|
entry.buf[ sizeof( entry.buf ) - 1 ] = '\0';
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char* GuiPopUpMenuCtrl::getTextById(S32 id)
|
|
{
|
|
for ( U32 i = 0; i < mEntries.size(); i++ )
|
|
{
|
|
if ( mEntries[i].id == id )
|
|
return( mEntries[i].buf );
|
|
}
|
|
|
|
return( "" );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
S32 GuiPopUpMenuCtrl::findText( const char* text )
|
|
{
|
|
for ( U32 i = 0; i < mEntries.size(); i++ )
|
|
{
|
|
if ( dStrcmp( text, mEntries[i].buf ) == 0 )
|
|
return( mEntries[i].id );
|
|
}
|
|
return( -1 );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::setSelected(S32 id, bool bNotifyScript )
|
|
{
|
|
for( S32 i = 0; i < mEntries.size(); i++ )
|
|
{
|
|
if( id == mEntries[i].id )
|
|
{
|
|
i = ( mRevNum > i ) ? mRevNum - i : i;
|
|
mSelIndex = i;
|
|
|
|
if( mReplaceText ) // Only change the displayed text if appropriate.
|
|
setText( mEntries[ i ].buf );
|
|
|
|
// Now perform the popup action:
|
|
|
|
if( bNotifyScript )
|
|
{
|
|
if( isMethod( "onSelect" ) )
|
|
Con::executef( this, "onSelect", Con::getIntArg( mEntries[ mSelIndex ].id ), mEntries[mSelIndex].buf );
|
|
|
|
execConsoleCallback();
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if( mReplaceText ) // Only change the displayed text if appropriate.
|
|
{
|
|
setText("");
|
|
}
|
|
mSelIndex = -1;
|
|
|
|
if( bNotifyScript && isMethod( "onCancel" ) )
|
|
Con::executef( this, "onCancel" );
|
|
|
|
if( id == -1 )
|
|
return;
|
|
|
|
// Execute the popup console command:
|
|
if( bNotifyScript )
|
|
execConsoleCallback();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added to set the first item as selected.
|
|
void GuiPopUpMenuCtrl::setFirstSelected( bool bNotifyScript )
|
|
{
|
|
if( mEntries.size() > 0 )
|
|
{
|
|
mSelIndex = 0;
|
|
if ( mReplaceText ) // Only change the displayed text if appropriate.
|
|
{
|
|
setText( mEntries[0].buf );
|
|
}
|
|
|
|
// Execute the popup console command:
|
|
if( bNotifyScript )
|
|
{
|
|
if ( isMethod( "onSelect" ) )
|
|
Con::executef( this, "onSelect", Con::getIntArg( mEntries[ mSelIndex ].id ), mEntries[mSelIndex].buf );
|
|
|
|
execConsoleCallback();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( mReplaceText ) // Only change the displayed text if appropriate.
|
|
setText("");
|
|
|
|
mSelIndex = -1;
|
|
|
|
if( bNotifyScript )
|
|
{
|
|
Con::executef( this, "onCancel" );
|
|
execConsoleCallback();
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added to set no items as selected.
|
|
void GuiPopUpMenuCtrl::setNoneSelected()
|
|
{
|
|
if ( mReplaceText ) // Only change the displayed text if appropriate.
|
|
{
|
|
setText("");
|
|
}
|
|
mSelIndex = -1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char *GuiPopUpMenuCtrl::getScriptValue()
|
|
{
|
|
return getText();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::onRender( Point2I offset, const RectI &updateRect )
|
|
{
|
|
TORQUE_UNUSED(updateRect);
|
|
Point2I localStart;
|
|
|
|
if ( mScrollDir != GuiScrollCtrl::None )
|
|
autoScroll();
|
|
|
|
GFXDrawUtil* drawUtil = GFX->getDrawUtil();
|
|
|
|
RectI baseRect( offset, getExtent() );
|
|
if ( mInAction )
|
|
{
|
|
S32 left = baseRect.point.x, right = baseRect.point.x + baseRect.extent.x - 1;
|
|
S32 top = baseRect.point.y, bottom = baseRect.point.y + baseRect.extent.y - 1;
|
|
|
|
// Do we render a bitmap border or lines?
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// Render the fixed, filled in border
|
|
renderFixedBitmapBordersFilled(baseRect, 3, mProfile );
|
|
|
|
}
|
|
else
|
|
{
|
|
//renderSlightlyLoweredBox(r, mProfile);
|
|
drawUtil->drawRectFill(baseRect, mProfile->mFillColor );
|
|
}
|
|
|
|
// Draw a bitmap over the background?
|
|
if ( mTextureDepressed )
|
|
{
|
|
RectI rect(offset, mBitmapBounds);
|
|
drawUtil->clearBitmapModulation();
|
|
drawUtil->drawBitmapStretch( mTextureDepressed, rect );
|
|
}
|
|
else if ( mTextureNormal )
|
|
{
|
|
RectI rect(offset, mBitmapBounds);
|
|
drawUtil->clearBitmapModulation();
|
|
drawUtil->drawBitmapStretch( mTextureNormal, rect );
|
|
}
|
|
|
|
// Do we render a bitmap border or lines?
|
|
if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) )
|
|
{
|
|
drawUtil->drawLine(left, top, left, bottom, colorWhite );
|
|
drawUtil->drawLine(left, top, right, top, colorWhite );
|
|
drawUtil->drawLine(left + 1, bottom, right, bottom, mProfile->mBorderColor );
|
|
drawUtil->drawLine(right, top + 1, right, bottom - 1, mProfile->mBorderColor );
|
|
}
|
|
|
|
}
|
|
else
|
|
// TODO: Implement
|
|
// TODO: Add onMouseEnter() and onMouseLeave() and a definition of mMouseOver (see guiButtonBaseCtrl) for this to work.
|
|
if ( mMouseOver )
|
|
{
|
|
S32 left = baseRect.point.x, right = baseRect.point.x + baseRect.extent.x - 1;
|
|
S32 top = baseRect.point.y, bottom = baseRect.point.y + baseRect.extent.y - 1;
|
|
|
|
// Do we render a bitmap border or lines?
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// Render the fixed, filled in border
|
|
renderFixedBitmapBordersFilled(baseRect, 2, mProfile );
|
|
|
|
}
|
|
else
|
|
{
|
|
drawUtil->drawRectFill(baseRect, mProfile->mFillColorHL );
|
|
}
|
|
|
|
// Draw a bitmap over the background?
|
|
if ( mTextureNormal )
|
|
{
|
|
RectI rect( offset, mBitmapBounds );
|
|
drawUtil->clearBitmapModulation();
|
|
drawUtil->drawBitmapStretch( mTextureNormal, rect );
|
|
}
|
|
|
|
// Do we render a bitmap border or lines?
|
|
if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) )
|
|
{
|
|
drawUtil->drawLine(left, top, left, bottom, colorWhite);
|
|
drawUtil->drawLine(left, top, right, top, colorWhite);
|
|
drawUtil->drawLine(left + 1, bottom, right, bottom, mProfile->mBorderColor);
|
|
drawUtil->drawLine(right, top + 1, right, bottom - 1, mProfile->mBorderColor);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Do we render a bitmap border or lines?
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// Render the fixed, filled in border
|
|
renderFixedBitmapBordersFilled(baseRect, 1, mProfile );
|
|
}
|
|
else
|
|
{
|
|
drawUtil->drawRectFill(baseRect, mProfile->mFillColorNA );
|
|
}
|
|
|
|
// Draw a bitmap over the background?
|
|
if ( mTextureNormal )
|
|
{
|
|
RectI rect(offset, mBitmapBounds);
|
|
drawUtil->clearBitmapModulation();
|
|
drawUtil->drawBitmapStretch( mTextureNormal, rect );
|
|
}
|
|
|
|
// Do we render a bitmap border or lines?
|
|
if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) )
|
|
{
|
|
drawUtil->drawRect( baseRect, mProfile->mBorderColorNA );
|
|
}
|
|
}
|
|
// renderSlightlyRaisedBox(r, mProfile); // Used to be the only 'else' condition to mInAction above.
|
|
|
|
S32 txt_w = mProfile->mFont->getStrWidth(mText);
|
|
localStart.x = 0;
|
|
localStart.y = (getHeight() - (mProfile->mFont->getHeight())) / 2;
|
|
|
|
// align the horizontal
|
|
switch (mProfile->mAlignment)
|
|
{
|
|
case GuiControlProfile::RightJustify:
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// We're making use of a bitmap border, so take into account the
|
|
// right cap of the border.
|
|
RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w;
|
|
}
|
|
else
|
|
{
|
|
localStart.x = getWidth() - txt_w;
|
|
}
|
|
break;
|
|
case GuiControlProfile::CenterJustify:
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// We're making use of a bitmap border, so take into account the
|
|
// right cap of the border.
|
|
RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
localStart.x = (getWidth() - bitmapBounds[2].extent.x - txt_w) / 2;
|
|
|
|
} else
|
|
{
|
|
localStart.x = (getWidth() - txt_w) / 2;
|
|
}
|
|
break;
|
|
default:
|
|
// GuiControlProfile::LeftJustify
|
|
if ( txt_w > getWidth() )
|
|
{
|
|
// The width of the text is greater than the width of the control.
|
|
// In this case we will right justify the text and leave some space
|
|
// for the down arrow.
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// We're making use of a bitmap border, so take into account the
|
|
// right cap of the border.
|
|
RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w;
|
|
}
|
|
else
|
|
{
|
|
localStart.x = getWidth() - txt_w - 12;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
localStart.x = mProfile->mTextOffset.x; // Use mProfile->mTextOffset as a controlable margin for the control's text.
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Do we first draw a coloured box beside the text?
|
|
ColorI boxColor;
|
|
bool drawbox = getColoredBox( boxColor, mSelIndex);
|
|
if ( drawbox )
|
|
{
|
|
Point2I coloredboxsize( 15, 10 );
|
|
RectI boxBounds( offset.x + mProfile->mTextOffset.x, offset.y + ( (getHeight() - coloredboxsize.y ) / 2 ), coloredboxsize.x, coloredboxsize.y );
|
|
drawUtil->drawRectFill(boxBounds, boxColor);
|
|
drawUtil->drawRect(boxBounds, ColorI(0,0,0));
|
|
|
|
localStart.x += coloredboxsize.x + mProfile->mTextOffset.x;
|
|
}
|
|
|
|
// Draw the text
|
|
Point2I globalStart = localToGlobalCoord( localStart );
|
|
ColorI fontColor = mActive ? ( mInAction ? mProfile->mFontColor : mProfile->mFontColorNA ) : mProfile->mFontColorNA;
|
|
drawUtil->setBitmapModulation( fontColor ); // was: (mProfile->mFontColor);
|
|
|
|
// Get the number of columns in the text
|
|
S32 colcount = getColumnCount( mText, "\t" );
|
|
|
|
// Are there two or more columns?
|
|
if ( colcount >= 2 )
|
|
{
|
|
char buff[256];
|
|
|
|
// Draw the first column
|
|
getColumn( mText, buff, 0, "\t" );
|
|
drawUtil->drawText( mProfile->mFont, globalStart, buff, mProfile->mFontColors );
|
|
|
|
// Draw the second column to the right
|
|
getColumn( mText, buff, 1, "\t" );
|
|
S32 colTxt_w = mProfile->mFont->getStrWidth( buff );
|
|
if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() )
|
|
{
|
|
// We're making use of a bitmap border, so take into account the
|
|
// right cap of the border.
|
|
RectI* bitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
Point2I textpos = localToGlobalCoord( Point2I( getWidth() - colTxt_w - bitmapBounds[2].extent.x, localStart.y ) );
|
|
drawUtil->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors );
|
|
|
|
} else
|
|
{
|
|
Point2I textpos = localToGlobalCoord( Point2I( getWidth() - colTxt_w - 12, localStart.y ) );
|
|
drawUtil->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors );
|
|
}
|
|
|
|
} else
|
|
{
|
|
drawUtil->drawText( mProfile->mFont, globalStart, mText, mProfile->mFontColors );
|
|
}
|
|
|
|
// If we're rendering a bitmap border, then it will take care of the arrow.
|
|
if ( !(mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size()) )
|
|
{
|
|
// Draw a triangle (down arrow)
|
|
S32 left = baseRect.point.x + baseRect.extent.x - 12;
|
|
S32 right = left + 8;
|
|
S32 middle = left + 4;
|
|
S32 top = baseRect.extent.y / 2 + baseRect.point.y - 4;
|
|
S32 bottom = top + 8;
|
|
|
|
PrimBuild::color( mProfile->mFontColor );
|
|
|
|
PrimBuild::begin( GFXTriangleList, 3 );
|
|
PrimBuild::vertex2fv( Point3F( (F32)left, (F32)top, 0.0f ) );
|
|
PrimBuild::vertex2fv( Point3F( (F32)right, (F32)top, 0.0f ) );
|
|
PrimBuild::vertex2fv( Point3F( (F32)middle, (F32)bottom, 0.0f ) );
|
|
PrimBuild::end();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::closePopUp()
|
|
{
|
|
if ( !mInAction )
|
|
return;
|
|
|
|
// Get the selection from the text list:
|
|
|
|
if( !mBackgroundCancel )
|
|
{
|
|
mSelIndex = mTl->getSelectedCell().y;
|
|
mSelIndex = ( mRevNum >= mSelIndex && mSelIndex != -1 ) ? mRevNum - mSelIndex : mSelIndex;
|
|
if ( mSelIndex != -1 )
|
|
{
|
|
if ( mReplaceText )
|
|
setText( mEntries[mSelIndex].buf );
|
|
setIntVariable( mEntries[mSelIndex].id );
|
|
}
|
|
}
|
|
|
|
// Release the mouse:
|
|
mInAction = false;
|
|
mTl->mouseUnlock();
|
|
|
|
// Now perform the popup action:
|
|
if( mSelIndex != -1 && !mBackgroundCancel )
|
|
{
|
|
if ( isMethod( "onSelect" ) )
|
|
Con::executef( this, "onSelect", Con::getIntArg( mEntries[ mSelIndex ].id ), mEntries[mSelIndex].buf );
|
|
|
|
// Execute the popup console command:
|
|
execConsoleCallback();
|
|
}
|
|
else if ( isMethod( "onCancel" ) )
|
|
Con::executef( this, "onCancel" );
|
|
|
|
// Pop the background:
|
|
GuiCanvas *root = getRoot();
|
|
if ( root )
|
|
root->popDialogControl(mBackground);
|
|
|
|
// Kill the popup:
|
|
mBackground->removeObject( mSc );
|
|
mTl->deleteObject();
|
|
mSc->deleteObject();
|
|
mBackground->deleteObject();
|
|
|
|
mBackground = NULL;
|
|
mTl = NULL;
|
|
mSc = NULL;
|
|
|
|
// Set this as the first responder:
|
|
setFirstResponder();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool GuiPopUpMenuCtrl::onKeyDown(const GuiEvent &event)
|
|
{
|
|
//if the control is a dead end, don't process the input:
|
|
if ( !mVisible || !mActive || !mAwake )
|
|
return false;
|
|
|
|
//see if the key down is a <return> or not
|
|
if ( event.keyCode == KEY_RETURN && event.modifier == 0 )
|
|
{
|
|
onAction();
|
|
return true;
|
|
}
|
|
|
|
//otherwise, pass the event to its parent
|
|
return Parent::onKeyDown( event );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::onAction()
|
|
{
|
|
GuiControl *canCtrl = getParent();
|
|
|
|
addChildren();
|
|
|
|
GuiCanvas *root = getRoot();
|
|
Point2I windowExt = root->getExtent();
|
|
|
|
mBackground->resize( Point2I(0,0), root->getExtent() );
|
|
|
|
S32 textWidth = 0, width = getWidth();
|
|
const S32 textSpace = 2;
|
|
bool setScroll = false;
|
|
|
|
for ( U32 i = 0; i < mEntries.size(); ++i )
|
|
if ( S32(mProfile->mFont->getStrWidth( mEntries[i].buf )) > textWidth )
|
|
textWidth = mProfile->mFont->getStrWidth( mEntries[i].buf );
|
|
|
|
S32 sbWidth = mSc->getControlProfile()->mBorderThickness * 2 + mSc->scrollBarThickness(); // Calculate the scroll bar width
|
|
if ( textWidth > ( getWidth() - sbWidth-mProfile->mTextOffset.x - mSc->getChildMargin().x * 2 ) ) // The text draw area to test against is the width of the drop-down minus the scroll bar width, the text margin and the scroll bar child margins.
|
|
{
|
|
textWidth +=sbWidth + mProfile->mTextOffset.x + mSc->getChildMargin().x * 2; // The new width is the width of the text plus the scroll bar width plus the text margin size plus the scroll bar child margins.
|
|
width = textWidth;
|
|
|
|
// If a child margin is not defined for the scroll control, let's add
|
|
// some space between the text and scroll control for readability
|
|
if(mSc->getChildMargin().x == 0)
|
|
width += textSpace;
|
|
}
|
|
|
|
mTl->setCellSize(Point2I(width, mProfile->mFont->getHeight() + textSpace));
|
|
|
|
for ( U32 j = 0; j < mEntries.size(); ++j )
|
|
mTl->addEntry( mEntries[j].id, mEntries[j].buf );
|
|
|
|
if ( mSelIndex >= 0 )
|
|
mTl->setSelectedCell( Point2I( 0, mSelIndex ) );
|
|
|
|
Point2I pointInGC = canCtrl->localToGlobalCoord( getPosition() );
|
|
Point2I scrollPoint( pointInGC.x, pointInGC.y + getHeight() );
|
|
|
|
//Calc max Y distance, so Scroll Ctrl will fit on window
|
|
|
|
S32 sbBorder = mSc->getControlProfile()->mBorderThickness * 2 + mSc->getChildMargin().y * 2;
|
|
S32 maxYdis = windowExt.y - pointInGC.y - getHeight() - sbBorder;
|
|
|
|
//If scroll bars need to be added
|
|
mRevNum = 0;
|
|
if ( maxYdis < mTl->getHeight() + sbBorder )
|
|
{
|
|
//Should we pop menu list above the button
|
|
if ( maxYdis < pointInGC.y )
|
|
{
|
|
if(mReverseTextList)
|
|
reverseTextList();
|
|
|
|
maxYdis = pointInGC.y;
|
|
//Does the menu need a scroll bar
|
|
if ( maxYdis < mTl->getHeight() + sbBorder )
|
|
{
|
|
setScroll = true;
|
|
}
|
|
//No scroll bar needed
|
|
else
|
|
{
|
|
maxYdis = mTl->getHeight() + sbBorder;
|
|
}
|
|
|
|
// Added the next two lines
|
|
scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis); // Used to have the following on the end: '-1);'
|
|
}
|
|
//Scroll bar needed but Don't pop above button
|
|
else
|
|
{
|
|
setScroll = true;
|
|
}
|
|
}
|
|
//No scroll bar needed
|
|
else
|
|
{
|
|
maxYdis = mTl->getHeight() + sbBorder;
|
|
}
|
|
|
|
RectI newBounds = mSc->getBounds();
|
|
|
|
//offset it from the background so it lines up properly
|
|
newBounds.point = mBackground->globalToLocalCoord( scrollPoint );
|
|
|
|
if ( newBounds.point.x + width > mBackground->getWidth() )
|
|
if ( width - getWidth() > 0 )
|
|
newBounds.point.x -= width - getWidth();
|
|
|
|
newBounds.extent.set( width, maxYdis );
|
|
mSc->setBounds( newBounds );
|
|
|
|
mSc->registerObject();
|
|
mTl->registerObject();
|
|
mBackground->registerObject();
|
|
|
|
mSc->addObject( mTl );
|
|
mBackground->addObject( mSc );
|
|
|
|
mBackgroundCancel = false; // Setup check if user clicked on the background instead of the text list (ie: didn't want to change their current selection).
|
|
|
|
root->pushDialogControl( mBackground, 99 );
|
|
|
|
if ( setScroll )
|
|
{
|
|
// Resize the text list
|
|
Point2I cellSize;
|
|
mTl->getCellSize( cellSize );
|
|
cellSize.x = width - mSc->scrollBarThickness() - sbBorder;
|
|
mTl->setCellSize( cellSize );
|
|
mTl->setWidth( cellSize.x );
|
|
|
|
if ( mSelIndex )
|
|
mTl->scrollCellVisible( Point2I( 0, mSelIndex ) );
|
|
else
|
|
mTl->scrollCellVisible( Point2I( 0, 0 ) );
|
|
}
|
|
|
|
mTl->setFirstResponder();
|
|
|
|
mInAction = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::addChildren()
|
|
{
|
|
// Create Text List.
|
|
mTl = new GuiPopupTextListCtrl( this );
|
|
AssertFatal( mTl, "Failed to create the GuiPopUpTextListCtrl for the PopUpMenu" );
|
|
// Use the children's profile rather than the parent's profile, if it exists.
|
|
mTl->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile );
|
|
mTl->setField("noDuplicates", "false");
|
|
|
|
mSc = new GuiScrollCtrl;
|
|
AssertFatal( mSc, "Failed to create the GuiScrollCtrl for the PopUpMenu" );
|
|
GuiControlProfile *prof;
|
|
if ( Sim::findObject( "GuiScrollProfile", prof ) )
|
|
{
|
|
mSc->setControlProfile( prof );
|
|
}
|
|
else
|
|
{
|
|
// Use the children's profile rather than the parent's profile, if it exists.
|
|
mSc->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile );
|
|
}
|
|
|
|
mSc->setField( "hScrollBar", "AlwaysOff" );
|
|
mSc->setField( "vScrollBar", "dynamic" );
|
|
//if(mRenderScrollInNA) // Force the scroll control to render using fillColorNA rather than fillColor
|
|
// mSc->mUseNABackground = true;
|
|
|
|
mBackground = new GuiPopUpBackgroundCtrl( this, mTl );
|
|
AssertFatal( mBackground, "Failed to create the GuiBackgroundCtrl for the PopUpMenu" );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::repositionPopup()
|
|
{
|
|
if ( !mInAction || !mSc || !mTl )
|
|
return;
|
|
|
|
// I'm not concerned with this right now...
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::reverseTextList()
|
|
{
|
|
mTl->clear();
|
|
for ( S32 i = mEntries.size()-1; i >= 0; --i )
|
|
mTl->addEntry( mEntries[i].id, mEntries[i].buf );
|
|
|
|
// Don't lose the selected cell:
|
|
if ( mSelIndex >= 0 )
|
|
mTl->setSelectedCell( Point2I( 0, mEntries.size() - mSelIndex - 1 ) );
|
|
|
|
mRevNum = mEntries.size() - 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool GuiPopUpMenuCtrl::getFontColor( ColorI &fontColor, S32 id, bool selected, bool mouseOver )
|
|
{
|
|
U32 i;
|
|
Entry* entry = NULL;
|
|
for ( i = 0; i < mEntries.size(); i++ )
|
|
{
|
|
if ( mEntries[i].id == id )
|
|
{
|
|
entry = &mEntries[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !entry )
|
|
return( false );
|
|
|
|
if ( entry->scheme != 0 )
|
|
{
|
|
// Find the entry's color scheme:
|
|
for ( i = 0; i < mSchemes.size(); i++ )
|
|
{
|
|
if ( mSchemes[i].id == entry->scheme )
|
|
{
|
|
fontColor = selected ? mSchemes[i].fontColorSEL : mouseOver ? mSchemes[i].fontColorHL : mSchemes[i].fontColor;
|
|
return( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default color scheme...
|
|
fontColor = selected ? mProfile->mFontColorSEL : mouseOver ? mProfile->mFontColorHL : mProfile->mFontColorNA; // Modified the final color choice from mProfile->mFontColor to mProfile->mFontColorNA
|
|
|
|
return( true );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added
|
|
bool GuiPopUpMenuCtrl::getColoredBox( ColorI &fontColor, S32 id )
|
|
{
|
|
U32 i;
|
|
Entry* entry = NULL;
|
|
for ( i = 0; i < mEntries.size(); i++ )
|
|
{
|
|
if ( mEntries[i].id == id )
|
|
{
|
|
entry = &mEntries[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !entry )
|
|
return false;
|
|
|
|
if ( entry->usesColorBox == false )
|
|
return false;
|
|
|
|
fontColor = entry->colorbox;
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::onMouseDown( const GuiEvent &event )
|
|
{
|
|
TORQUE_UNUSED(event);
|
|
|
|
if( !mVisible || !mActive || !mAwake )
|
|
return;
|
|
|
|
onAction();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::onMouseUp( const GuiEvent &event )
|
|
{
|
|
TORQUE_UNUSED(event);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added
|
|
void GuiPopUpMenuCtrl::onMouseEnter( const GuiEvent &event )
|
|
{
|
|
mMouseOver = true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Added
|
|
void GuiPopUpMenuCtrl::onMouseLeave( const GuiEvent &event )
|
|
{
|
|
mMouseOver = false;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::setupAutoScroll( const GuiEvent &event )
|
|
{
|
|
GuiControl *parent = getParent();
|
|
if ( !parent )
|
|
return;
|
|
|
|
Point2I mousePt = mSc->globalToLocalCoord( event.mousePoint );
|
|
|
|
mEventSave = event;
|
|
|
|
if ( mLastYvalue != mousePt.y )
|
|
{
|
|
mScrollDir = GuiScrollCtrl::None;
|
|
if ( mousePt.y > mSc->getHeight() || mousePt.y < 0 )
|
|
{
|
|
S32 topOrBottom = ( mousePt.y > mSc->getHeight() ) ? 1 : 0;
|
|
mSc->scrollTo( 0, topOrBottom );
|
|
return;
|
|
}
|
|
|
|
F32 percent = (F32)mousePt.y / (F32)mSc->getHeight();
|
|
if ( percent > 0.7f && mousePt.y > mLastYvalue )
|
|
{
|
|
mIncValue = percent - 0.5f;
|
|
mScrollDir = GuiScrollCtrl::DownArrow;
|
|
}
|
|
else if ( percent < 0.3f && mousePt.y < mLastYvalue )
|
|
{
|
|
mIncValue = 0.5f - percent;
|
|
mScrollDir = GuiScrollCtrl::UpArrow;
|
|
}
|
|
mLastYvalue = mousePt.y;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::autoScroll()
|
|
{
|
|
mScrollCount += mIncValue;
|
|
|
|
while ( mScrollCount > 1 )
|
|
{
|
|
mSc->autoScroll( mScrollDir );
|
|
mScrollCount -= 1;
|
|
}
|
|
mTl->onMouseMove( mEventSave );
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void GuiPopUpMenuCtrl::replaceText(S32 boolVal)
|
|
{
|
|
mReplaceText = boolVal;
|
|
}
|