|
@@ -25,6 +25,9 @@
|
|
#include "console/consoleTypes.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "console/engineAPI.h"
|
|
#include "console/engineAPI.h"
|
|
#include "gfx/gfxDrawUtil.h"
|
|
#include "gfx/gfxDrawUtil.h"
|
|
|
|
+#include "gui/containers/guiScrollCtrl.h"
|
|
|
|
+#include "sim\actionMap.h"
|
|
|
|
+#include "core\strings\stringUnit.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// GuiGameListMenuCtrl
|
|
// GuiGameListMenuCtrl
|
|
@@ -33,7 +36,8 @@
|
|
GuiGameListMenuCtrl::GuiGameListMenuCtrl()
|
|
GuiGameListMenuCtrl::GuiGameListMenuCtrl()
|
|
: mSelected(NO_ROW),
|
|
: mSelected(NO_ROW),
|
|
mDebugRender(false),
|
|
mDebugRender(false),
|
|
- mHighlighted(NO_ROW)
|
|
|
|
|
|
+ mHighlighted(NO_ROW),
|
|
|
|
+ mCallbackOnInputs(false)
|
|
{
|
|
{
|
|
VECTOR_SET_ASSOCIATION(mRows);
|
|
VECTOR_SET_ASSOCIATION(mRows);
|
|
|
|
|
|
@@ -93,7 +97,7 @@ void GuiGameListMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
U32 buttonTextureIndex;
|
|
U32 buttonTextureIndex;
|
|
S32 iconIndex = (*row)->mIconIndex;
|
|
S32 iconIndex = (*row)->mIconIndex;
|
|
bool useHighlightIcon = (*row)->mUseHighlightIcon;
|
|
bool useHighlightIcon = (*row)->mUseHighlightIcon;
|
|
- if (! (*row)->mEnabled)
|
|
|
|
|
|
+ if (!(*row)->mEnabled)
|
|
{
|
|
{
|
|
buttonTextureIndex = Profile::TEX_DISABLED;
|
|
buttonTextureIndex = Profile::TEX_DISABLED;
|
|
fontColor = profile->mFontColorNA;
|
|
fontColor = profile->mFontColorNA;
|
|
@@ -127,7 +131,7 @@ void GuiGameListMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
drawUtil->drawBitmapStretchSR(profile->mTextureObject, RectI(currentOffset, rowExtent), profile->getBitmapArrayRect(buttonTextureIndex));
|
|
drawUtil->drawBitmapStretchSR(profile->mTextureObject, RectI(currentOffset, rowExtent), profile->getBitmapArrayRect(buttonTextureIndex));
|
|
|
|
|
|
// render the row icon if it has one
|
|
// render the row icon if it has one
|
|
- if ((iconIndex != NO_ICON) && profileHasIcons && (! profile->getBitmapArrayRect((U32)iconIndex).extent.isZero()))
|
|
|
|
|
|
+ if ((iconIndex != NO_ICON) && profileHasIcons && (!profile->getBitmapArrayRect((U32)iconIndex).extent.isZero()))
|
|
{
|
|
{
|
|
iconIndex += Profile::TEX_FIRST_ICON;
|
|
iconIndex += Profile::TEX_FIRST_ICON;
|
|
drawUtil->clearBitmapModulation();
|
|
drawUtil->clearBitmapModulation();
|
|
@@ -137,6 +141,11 @@ void GuiGameListMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
// render the row text
|
|
// render the row text
|
|
drawUtil->setBitmapModulation(fontColor);
|
|
drawUtil->setBitmapModulation(fontColor);
|
|
renderJustifiedText(currentOffset + textOffset, textExtent, (*row)->mLabel);
|
|
renderJustifiedText(currentOffset + textOffset, textExtent, (*row)->mLabel);
|
|
|
|
+
|
|
|
|
+ if ((*row)->mMode == Row::Mode::OptionList)
|
|
|
|
+ {
|
|
|
|
+ onRenderOptionList((*row), currentOffset);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
if (mDebugRender)
|
|
if (mDebugRender)
|
|
@@ -147,6 +156,92 @@ void GuiGameListMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
renderChildControls(offset, updateRect);
|
|
renderChildControls(offset, updateRect);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void GuiGameListMenuCtrl::onRenderOptionList(Row* row, Point2I currentOffset)
|
|
|
|
+{
|
|
|
|
+ GuiGameListMenuProfile* profile = (GuiGameListMenuProfile*)mProfile;
|
|
|
|
+
|
|
|
|
+ F32 xScale = (float)getWidth() / profile->getRowWidth();
|
|
|
|
+
|
|
|
|
+ S32 rowHeight = profile->getRowHeight();
|
|
|
|
+
|
|
|
|
+ bool profileHasArrows = profile->hasArrows();
|
|
|
|
+ Point2I arrowExtent;
|
|
|
|
+ S32 arrowOffsetY(0);
|
|
|
|
+ if (profileHasArrows)
|
|
|
|
+ {
|
|
|
|
+ arrowExtent = profile->getArrowExtent();
|
|
|
|
+
|
|
|
|
+ // icon is centered vertically
|
|
|
|
+ arrowOffsetY = (rowHeight - arrowExtent.y) >> 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ GFXDrawUtil* drawer = GFX->getDrawUtil();
|
|
|
|
+
|
|
|
|
+ Point2I arrowOffset;
|
|
|
|
+ S32 columnSplit = profile->mColumnSplit * xScale;
|
|
|
|
+
|
|
|
|
+ S32 iconIndex;
|
|
|
|
+ bool hasOptions = (row->mOptions.size() > 0) && row->mSelectedOption > -1;
|
|
|
|
+ if (hasOptions)
|
|
|
|
+ {
|
|
|
|
+ bool isRowSelected = (getSelected() != NO_ROW) && (row == mRows[getSelected()]);
|
|
|
|
+ bool isRowHighlighted = (getHighlighted() != NO_ROW) ? ((row == mRows[getHighlighted()]) && (row->mEnabled)) : false;
|
|
|
|
+ if (profileHasArrows)
|
|
|
|
+ {
|
|
|
|
+ // render the left arrow
|
|
|
|
+ bool arrowOnL = (isRowSelected || isRowHighlighted) && (row->mWrapOptions || (row->mSelectedOption > 0));
|
|
|
|
+ iconIndex = (arrowOnL) ? Profile::TEX_L_ARROW_ON : Profile::TEX_L_ARROW_OFF;
|
|
|
|
+ arrowOffset.x = currentOffset.x + columnSplit;
|
|
|
|
+ arrowOffset.y = currentOffset.y + arrowOffsetY;
|
|
|
|
+
|
|
|
|
+ drawer->clearBitmapModulation();
|
|
|
|
+ drawer->drawBitmapStretchSR(profile->mTextureObject, RectI(arrowOffset, arrowExtent), profile->getBitmapArrayRect((U32)iconIndex));
|
|
|
|
+
|
|
|
|
+ // render the right arrow
|
|
|
|
+ bool arrowOnR = (isRowSelected || isRowHighlighted) && (row->mWrapOptions || (row->mSelectedOption < row->mOptions.size() - 1));
|
|
|
|
+ iconIndex = (arrowOnR) ? Profile::TEX_R_ARROW_ON : Profile::TEX_R_ARROW_OFF;
|
|
|
|
+ arrowOffset.x = currentOffset.x + (profile->mHitAreaLowerRight.x - profile->mRightPad) * xScale - arrowExtent.x;
|
|
|
|
+ arrowOffset.y = currentOffset.y + arrowOffsetY;
|
|
|
|
+
|
|
|
|
+ drawer->clearBitmapModulation();
|
|
|
|
+ drawer->drawBitmapStretchSR(profile->mTextureObject, RectI(arrowOffset, arrowExtent), profile->getBitmapArrayRect((U32)iconIndex));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // get the appropriate font color
|
|
|
|
+ ColorI fontColor;
|
|
|
|
+ if (!row->mEnabled)
|
|
|
|
+ {
|
|
|
|
+ fontColor = profile->mFontColorNA;
|
|
|
|
+ }
|
|
|
|
+ else if (isRowSelected)
|
|
|
|
+ {
|
|
|
|
+ fontColor = profile->mFontColorSEL;
|
|
|
|
+ }
|
|
|
|
+ else if (isRowHighlighted)
|
|
|
|
+ {
|
|
|
|
+ fontColor = profile->mFontColorHL;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ fontColor = profile->mFontColor;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // calculate text to be at the center between the arrows
|
|
|
|
+ GFont* font = profile->mFont;
|
|
|
|
+ StringTableEntry text = row->mOptions[row->mSelectedOption];
|
|
|
|
+ S32 textWidth = font->getStrWidth(text);
|
|
|
|
+ S32 columnWidth = profile->mHitAreaLowerRight.x * xScale - profile->mRightPad - columnSplit;
|
|
|
|
+ S32 columnCenter = columnSplit + (columnWidth >> 1);
|
|
|
|
+ S32 textStartX = columnCenter - (textWidth >> 1);
|
|
|
|
+ Point2I textOffset(textStartX, 0);
|
|
|
|
+
|
|
|
|
+ // render the option text itself
|
|
|
|
+ Point2I textExtent(columnWidth, rowHeight);
|
|
|
|
+ drawer->setBitmapModulation(fontColor);
|
|
|
|
+ renderJustifiedText(currentOffset + textOffset, textExtent, text);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
void GuiGameListMenuCtrl::onDebugRender(Point2I offset)
|
|
void GuiGameListMenuCtrl::onDebugRender(Point2I offset)
|
|
{
|
|
{
|
|
GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile;
|
|
GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile;
|
|
@@ -197,13 +292,13 @@ void GuiGameListMenuCtrl::onDebugRender(Point2I offset)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-void GuiGameListMenuCtrl::addRow(const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled)
|
|
|
|
|
|
+void GuiGameListMenuCtrl::addRow(const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled, S32 mode)
|
|
{
|
|
{
|
|
Row * row = new Row();
|
|
Row * row = new Row();
|
|
- addRow(row, label, callback, icon, yPad, useHighlightIcon, enabled);
|
|
|
|
|
|
+ addRow(row, label, callback, icon, yPad, useHighlightIcon, enabled, mode);
|
|
}
|
|
}
|
|
|
|
|
|
-void GuiGameListMenuCtrl::addRow(Row * row, const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled)
|
|
|
|
|
|
+void GuiGameListMenuCtrl::addRow(Row * row, const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled, S32 mode)
|
|
{
|
|
{
|
|
row->mLabel = StringTable->insert(label, true);
|
|
row->mLabel = StringTable->insert(label, true);
|
|
row->mScriptCallback = (dStrlen(callback) > 0) ? StringTable->insert(callback, true) : NULL;
|
|
row->mScriptCallback = (dStrlen(callback) > 0) ? StringTable->insert(callback, true) : NULL;
|
|
@@ -211,6 +306,7 @@ void GuiGameListMenuCtrl::addRow(Row * row, const char* label, const char* callb
|
|
row->mHeightPad = yPad;
|
|
row->mHeightPad = yPad;
|
|
row->mUseHighlightIcon = useHighlightIcon;
|
|
row->mUseHighlightIcon = useHighlightIcon;
|
|
row->mEnabled = enabled;
|
|
row->mEnabled = enabled;
|
|
|
|
+ row->mMode = (Row::Mode)mode;
|
|
|
|
|
|
mRows.push_back(row);
|
|
mRows.push_back(row);
|
|
|
|
|
|
@@ -222,6 +318,24 @@ void GuiGameListMenuCtrl::addRow(Row * row, const char* label, const char* callb
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void GuiGameListMenuCtrl::addRow(const char* label, const char* optionsList, bool wrapOptions, const char* callback, S32 icon, S32 yPad, bool enabled)
|
|
|
|
+{
|
|
|
|
+ static StringTableEntry DELIM = StringTable->insert("\t", true);
|
|
|
|
+ Row* row = new Row();
|
|
|
|
+ Vector<StringTableEntry> options(__FILE__, __LINE__);
|
|
|
|
+ S32 count = StringUnit::getUnitCount(optionsList, DELIM);
|
|
|
|
+ for (S32 i = 0; i < count; ++i)
|
|
|
|
+ {
|
|
|
|
+ const char* option = StringUnit::getUnit(optionsList, i, DELIM);
|
|
|
|
+ options.push_back(StringTable->insert(option, true));
|
|
|
|
+ }
|
|
|
|
+ row->mOptions = options;
|
|
|
|
+ bool hasOptions = row->mOptions.size() > 0;
|
|
|
|
+ row->mSelectedOption = (hasOptions) ? 0 : NO_OPTION;
|
|
|
|
+ row->mWrapOptions = wrapOptions;
|
|
|
|
+ addRow(row, label, callback, icon, yPad, true, (hasOptions) ? enabled : false, Row::Mode::OptionList);
|
|
|
|
+}
|
|
|
|
+
|
|
Point2I GuiGameListMenuCtrl::getMinExtent() const
|
|
Point2I GuiGameListMenuCtrl::getMinExtent() const
|
|
{
|
|
{
|
|
Point2I parentMin = Parent::getMinExtent();
|
|
Point2I parentMin = Parent::getMinExtent();
|
|
@@ -280,12 +394,12 @@ bool GuiGameListMenuCtrl::onWake()
|
|
if( !hasValidProfile() )
|
|
if( !hasValidProfile() )
|
|
return false;
|
|
return false;
|
|
|
|
|
|
- if( mRows.empty() )
|
|
|
|
|
|
+ /*if( mRows.empty() )
|
|
{
|
|
{
|
|
Con::errorf( "GuiGameListMenuCtrl: %s can't be woken up without any rows. Please use \"addRow\" to add at least one row to the control before pushing it to the canvas.",
|
|
Con::errorf( "GuiGameListMenuCtrl: %s can't be woken up without any rows. Please use \"addRow\" to add at least one row to the control before pushing it to the canvas.",
|
|
getName() );
|
|
getName() );
|
|
return false;
|
|
return false;
|
|
- }
|
|
|
|
|
|
+ }*/
|
|
|
|
|
|
enforceConstraints();
|
|
enforceConstraints();
|
|
|
|
|
|
@@ -349,7 +463,15 @@ void GuiGameListMenuCtrl::onMouseUp(const GuiEvent &event)
|
|
S32 hitRow = getRow(event.mousePoint);
|
|
S32 hitRow = getRow(event.mousePoint);
|
|
if ((hitRow != NO_ROW) && isRowEnabled(hitRow) && (hitRow == getSelected()))
|
|
if ((hitRow != NO_ROW) && isRowEnabled(hitRow) && (hitRow == getSelected()))
|
|
{
|
|
{
|
|
- activateRow();
|
|
|
|
|
|
+ if (mRows[hitRow]->mMode == Row::Mode::Default)
|
|
|
|
+ {
|
|
|
|
+ activateRow();
|
|
|
|
+ }
|
|
|
|
+ else if (mRows[hitRow]->mMode == Row::Mode::OptionList)
|
|
|
|
+ {
|
|
|
|
+ S32 xPos = globalToLocalCoord(event.mousePoint).x;
|
|
|
|
+ clickOption((Row*)mRows[getSelected()], xPos);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -425,6 +547,13 @@ void GuiGameListMenuCtrl::setSelected(S32 index)
|
|
}
|
|
}
|
|
|
|
|
|
mSelected = mClamp(index, 0, mRows.size() - 1);
|
|
mSelected = mClamp(index, 0, mRows.size() - 1);
|
|
|
|
+
|
|
|
|
+ //If we're childed to a scroll container, make sure us changing rows has our new position visible
|
|
|
|
+ GuiScrollCtrl* scroll = dynamic_cast<GuiScrollCtrl*>(getParent());
|
|
|
|
+ if (scroll)
|
|
|
|
+ {
|
|
|
|
+ scroll->scrollRectVisible(getRowBounds(mSelected));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
bool GuiGameListMenuCtrl::isRowEnabled(S32 index) const
|
|
bool GuiGameListMenuCtrl::isRowEnabled(S32 index) const
|
|
@@ -470,6 +599,63 @@ void GuiGameListMenuCtrl::selectFirstEnabledRow()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+bool GuiGameListMenuCtrl::onInputEvent(const InputEventInfo& event)
|
|
|
|
+{
|
|
|
|
+ if (mCallbackOnInputs)
|
|
|
|
+ {
|
|
|
|
+ char deviceString[32];
|
|
|
|
+ if (!ActionMap::getDeviceName(event.deviceType, event.deviceInst, deviceString))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (event.action == SI_MAKE || event.action == SI_BREAK)
|
|
|
|
+ {
|
|
|
|
+ bool isModifier = false;
|
|
|
|
+
|
|
|
|
+ switch (event.objInst)
|
|
|
|
+ {
|
|
|
|
+ case KEY_LCONTROL:
|
|
|
|
+ case KEY_RCONTROL:
|
|
|
|
+ case KEY_LALT:
|
|
|
|
+ case KEY_RALT:
|
|
|
|
+ case KEY_LSHIFT:
|
|
|
|
+ case KEY_RSHIFT:
|
|
|
|
+ case KEY_MAC_LOPT:
|
|
|
|
+ case KEY_MAC_ROPT:
|
|
|
|
+ isModifier = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((event.objType == SI_KEY) && isModifier)
|
|
|
|
+ {
|
|
|
|
+ char keyString[32];
|
|
|
|
+ if (!ActionMap::getKeyString(event.objInst, keyString))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ onInputEvent_callback(deviceString, keyString, event.action);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ const char* actionString = ActionMap::buildActionString(&event);
|
|
|
|
+ onInputEvent_callback(deviceString, actionString, event.action);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (event.objType == SI_AXIS || event.objType == SI_INT || event.objType == SI_FLOAT)
|
|
|
|
+ {
|
|
|
|
+ F32 fValue = event.fValue;
|
|
|
|
+ if (event.objType == SI_INT)
|
|
|
|
+ fValue = (F32)event.iValue;
|
|
|
|
+
|
|
|
|
+ if (!ActionMap::getDeviceName(event.deviceType, event.deviceInst, deviceString))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ const char* actionString = ActionMap::buildActionString(&event);
|
|
|
|
+
|
|
|
|
+ onAxisEvent_callback(deviceString, actionString, fValue);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
bool GuiGameListMenuCtrl::onKeyDown(const GuiEvent &event)
|
|
bool GuiGameListMenuCtrl::onKeyDown(const GuiEvent &event)
|
|
{
|
|
{
|
|
switch (event.keyCode)
|
|
switch (event.keyCode)
|
|
@@ -528,6 +714,18 @@ bool GuiGameListMenuCtrl::onGamepadAxisDown(const GuiEvent &event)
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+bool GuiGameListMenuCtrl::onGamepadAxisLeft(const GuiEvent& event)
|
|
|
|
+{
|
|
|
|
+ changeOption(-1);
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool GuiGameListMenuCtrl::onGamepadAxisRight(const GuiEvent& event)
|
|
|
|
+{
|
|
|
|
+ changeOption(1);
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command)
|
|
void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command)
|
|
{
|
|
{
|
|
if (command && command[0])
|
|
if (command && command[0])
|
|
@@ -589,10 +787,179 @@ void GuiGameListMenuCtrl::setRowLabel(S32 rowIndex, const char * label)
|
|
mRows[rowIndex]->mLabel = StringTable->insert(label, true);
|
|
mRows[rowIndex]->mLabel = StringTable->insert(label, true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void GuiGameListMenuCtrl::clearRows()
|
|
|
|
+{
|
|
|
|
+ mRows.clear();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+RectI GuiGameListMenuCtrl::getRowBounds(S32 rowIndex)
|
|
|
|
+{
|
|
|
|
+ GuiGameListMenuProfile* profile = (GuiGameListMenuProfile*)mProfile;
|
|
|
|
+
|
|
|
|
+ F32 xScale = (float)getWidth() / profile->getRowWidth();
|
|
|
|
+ S32 rowHeight = profile->getRowHeight();
|
|
|
|
+
|
|
|
|
+ Point2I currentOffset = Point2I::Zero;
|
|
|
|
+ Point2I extent = getExtent();
|
|
|
|
+ Point2I rowExtent(extent.x, rowHeight);
|
|
|
|
+
|
|
|
|
+ for (U32 i = 1; i <= rowIndex; i++)
|
|
|
|
+ {
|
|
|
|
+ //the top row can't pad, so we'll ignore it
|
|
|
|
+ GuiGameListMenuCtrl::Row* row = mRows[i];
|
|
|
|
+
|
|
|
|
+ // rows other than the first can have padding above them
|
|
|
|
+ currentOffset.y += row->mHeightPad;
|
|
|
|
+ currentOffset.y += rowHeight;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return RectI(currentOffset, rowExtent);
|
|
|
|
+}
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// Console stuff (GuiGameListMenuCtrl)
|
|
// Console stuff (GuiGameListMenuCtrl)
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
+StringTableEntry GuiGameListMenuCtrl::getCurrentOption(S32 rowIndex) const
|
|
|
|
+{
|
|
|
|
+ if (isValidRowIndex(rowIndex))
|
|
|
|
+ {
|
|
|
|
+ Row* row = (Row*)mRows[rowIndex];
|
|
|
|
+ if (row->mSelectedOption != NO_OPTION)
|
|
|
|
+ {
|
|
|
|
+ return row->mOptions[row->mSelectedOption];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return StringTable->insert("", false);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool GuiGameListMenuCtrl::selectOption(S32 rowIndex, const char* theOption)
|
|
|
|
+{
|
|
|
|
+ if (!isValidRowIndex(rowIndex))
|
|
|
|
+ {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Row* row = (Row*)mRows[rowIndex];
|
|
|
|
+
|
|
|
|
+ for (Vector<StringTableEntry>::iterator anOption = row->mOptions.begin(); anOption < row->mOptions.end(); ++anOption)
|
|
|
|
+ {
|
|
|
|
+ if (dStrcmp(*anOption, theOption) == 0)
|
|
|
|
+ {
|
|
|
|
+ S32 newIndex = anOption - row->mOptions.begin();
|
|
|
|
+ row->mSelectedOption = newIndex;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void GuiGameListMenuCtrl::setOptions(S32 rowIndex, const char* optionsList)
|
|
|
|
+{
|
|
|
|
+ static StringTableEntry DELIM = StringTable->insert("\t", true);
|
|
|
|
+
|
|
|
|
+ if (!isValidRowIndex(rowIndex))
|
|
|
|
+ {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Row* row = (Row*)mRows[rowIndex];
|
|
|
|
+
|
|
|
|
+ S32 count = StringUnit::getUnitCount(optionsList, DELIM);
|
|
|
|
+ row->mOptions.setSize(count);
|
|
|
|
+ for (S32 i = 0; i < count; ++i)
|
|
|
|
+ {
|
|
|
|
+ const char* option = StringUnit::getUnit(optionsList, i, DELIM);
|
|
|
|
+ row->mOptions[i] = StringTable->insert(option, true);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (row->mSelectedOption >= row->mOptions.size())
|
|
|
|
+ {
|
|
|
|
+ row->mSelectedOption = row->mOptions.size() - 1;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void GuiGameListMenuCtrl::clickOption(Row* row, S32 xPos)
|
|
|
|
+{
|
|
|
|
+ GuiGameListMenuProfile* profile = (GuiGameListMenuProfile*)mProfile;
|
|
|
|
+ if (!profile->hasArrows())
|
|
|
|
+ {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ F32 xScale = (float)getWidth() / profile->getRowWidth();
|
|
|
|
+
|
|
|
|
+ S32 bitmapArrowWidth = mProfile->getBitmapArrayRect(Profile::TEX_FIRST_ARROW).extent.x;
|
|
|
|
+
|
|
|
|
+ S32 leftArrowX1 = profile->mColumnSplit * xScale;
|
|
|
|
+ S32 leftArrowX2 = leftArrowX1 + bitmapArrowWidth;
|
|
|
|
+
|
|
|
|
+ S32 rightArrowX2 = (profile->mHitAreaLowerRight.x - profile->mRightPad) * xScale;
|
|
|
|
+ S32 rightArrowX1 = rightArrowX2 - bitmapArrowWidth;
|
|
|
|
+
|
|
|
|
+ if ((leftArrowX1 <= xPos) && (xPos <= leftArrowX2))
|
|
|
|
+ {
|
|
|
|
+ changeOption(row, -1);
|
|
|
|
+ }
|
|
|
|
+ else if ((rightArrowX1 <= xPos) && (xPos <= rightArrowX2))
|
|
|
|
+ {
|
|
|
|
+ changeOption(row, 1);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void GuiGameListMenuCtrl::changeOption(S32 delta)
|
|
|
|
+{
|
|
|
|
+ if (getSelected() != NO_ROW)
|
|
|
|
+ {
|
|
|
|
+ Row* row = (Row*)mRows[getSelected()];
|
|
|
|
+ changeOption(row, delta);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void GuiGameListMenuCtrl::changeOption(Row* row, S32 delta)
|
|
|
|
+{
|
|
|
|
+ S32 optionCount = row->mOptions.size();
|
|
|
|
+
|
|
|
|
+ S32 newSelection = row->mSelectedOption + delta;
|
|
|
|
+ if (optionCount == 0)
|
|
|
|
+ {
|
|
|
|
+ newSelection = NO_OPTION;
|
|
|
|
+ }
|
|
|
|
+ else if (!row->mWrapOptions)
|
|
|
|
+ {
|
|
|
|
+ newSelection = mClamp(newSelection, 0, optionCount - 1);
|
|
|
|
+ }
|
|
|
|
+ else if (newSelection < 0)
|
|
|
|
+ {
|
|
|
|
+ newSelection = optionCount - 1;
|
|
|
|
+ }
|
|
|
|
+ else if (newSelection >= optionCount)
|
|
|
|
+ {
|
|
|
|
+ newSelection = 0;
|
|
|
|
+ }
|
|
|
|
+ row->mSelectedOption = newSelection;
|
|
|
|
+
|
|
|
|
+ static StringTableEntry LEFT = StringTable->insert("LEFT", true);
|
|
|
|
+ static StringTableEntry RIGHT = StringTable->insert("RIGHT", true);
|
|
|
|
+
|
|
|
|
+ if (row->mScriptCallback != NULL)
|
|
|
|
+ {
|
|
|
|
+ setThisControl();
|
|
|
|
+ StringTableEntry direction = NULL;
|
|
|
|
+ if (delta < 0)
|
|
|
|
+ {
|
|
|
|
+ direction = LEFT;
|
|
|
|
+ }
|
|
|
|
+ else if (delta > 0)
|
|
|
|
+ {
|
|
|
|
+ direction = RIGHT;
|
|
|
|
+ }
|
|
|
|
+ if ((direction != NULL) && (Con::isFunction(row->mScriptCallback)))
|
|
|
|
+ {
|
|
|
|
+ Con::executef(row->mScriptCallback, direction);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
IMPLEMENT_CONOBJECT(GuiGameListMenuCtrl);
|
|
IMPLEMENT_CONOBJECT(GuiGameListMenuCtrl);
|
|
|
|
|
|
ConsoleDocClass( GuiGameListMenuCtrl,
|
|
ConsoleDocClass( GuiGameListMenuCtrl,
|
|
@@ -622,6 +989,20 @@ ConsoleDocClass( GuiGameListMenuCtrl,
|
|
IMPLEMENT_CALLBACK( GuiGameListMenuCtrl, onChange, void, (), (),
|
|
IMPLEMENT_CALLBACK( GuiGameListMenuCtrl, onChange, void, (), (),
|
|
"Called when the selected row changes." );
|
|
"Called when the selected row changes." );
|
|
|
|
|
|
|
|
+IMPLEMENT_CALLBACK(GuiGameListMenuCtrl, onInputEvent, void, (const char* device, const char* action, bool state),
|
|
|
|
+ (device, action, state),
|
|
|
|
+ "@brief Callback that occurs when an input is triggered on this control\n\n"
|
|
|
|
+ "@param device The device type triggering the input, such as keyboard, mouse, etc\n"
|
|
|
|
+ "@param action The actual event occuring, such as a key or button\n"
|
|
|
|
+ "@param state True if the action is being pressed, false if it is being release\n\n");
|
|
|
|
+
|
|
|
|
+IMPLEMENT_CALLBACK(GuiGameListMenuCtrl, onAxisEvent, void, (const char* device, const char* action, F32 axisValue),
|
|
|
|
+ (device, action, axisValue),
|
|
|
|
+ "@brief Callback that occurs when an axis event is triggered on this control\n\n"
|
|
|
|
+ "@param device The device type triggering the input, such as mouse, joystick, gamepad, etc\n"
|
|
|
|
+ "@param action The ActionMap code for the axis\n"
|
|
|
|
+ "@param axisValue The current value of the axis\n\n");
|
|
|
|
+
|
|
void GuiGameListMenuCtrl::initPersistFields()
|
|
void GuiGameListMenuCtrl::initPersistFields()
|
|
{
|
|
{
|
|
addField("debugRender", TypeBool, Offset(mDebugRender, GuiGameListMenuCtrl),
|
|
addField("debugRender", TypeBool, Offset(mDebugRender, GuiGameListMenuCtrl),
|
|
@@ -639,21 +1020,25 @@ void GuiGameListMenuCtrl::initPersistFields()
|
|
addField("callbackOnY", TypeString, Offset(mCallbackOnY, GuiGameListMenuCtrl),
|
|
addField("callbackOnY", TypeString, Offset(mCallbackOnY, GuiGameListMenuCtrl),
|
|
"Script callback when the 'Y' button is pressed. 'Y' inputs are Keyboard: Y; Gamepad: Y" );
|
|
"Script callback when the 'Y' button is pressed. 'Y' inputs are Keyboard: Y; Gamepad: Y" );
|
|
|
|
|
|
|
|
+ addField("callbackOnInputs", TypeBool, Offset(mCallbackOnInputs, GuiGameListMenuCtrl),
|
|
|
|
+ "Script callback when any inputs are detected, even if they aren't the regular 4 face buttons. Useful for secondary/speciality handling of menu navigation.");
|
|
|
|
+
|
|
Parent::initPersistFields();
|
|
Parent::initPersistFields();
|
|
}
|
|
}
|
|
|
|
|
|
DefineEngineMethod( GuiGameListMenuCtrl, addRow, void,
|
|
DefineEngineMethod( GuiGameListMenuCtrl, addRow, void,
|
|
- ( const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled ),
|
|
|
|
- ( -1, 0, true, true ),
|
|
|
|
|
|
+ ( const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled, int mode ),
|
|
|
|
+ ( -1, 0, true, true, 0 ),
|
|
"Add a row to the list control.\n\n"
|
|
"Add a row to the list control.\n\n"
|
|
"@param label The text to display on the row as a label.\n"
|
|
"@param label The text to display on the row as a label.\n"
|
|
"@param callback Name of a script function to use as a callback when this row is activated.\n"
|
|
"@param callback Name of a script function to use as a callback when this row is activated.\n"
|
|
"@param icon [optional] Index of the icon to use as a marker.\n"
|
|
"@param icon [optional] Index of the icon to use as a marker.\n"
|
|
"@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n"
|
|
"@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n"
|
|
"@param useHighlightIcon [optional] Does this row use the highlight icon?.\n"
|
|
"@param useHighlightIcon [optional] Does this row use the highlight icon?.\n"
|
|
- "@param enabled [optional] If this row is initially enabled." )
|
|
|
|
|
|
+ "@param enabled [optional] If this row is initially enabled.\n"
|
|
|
|
+ "@param mode [optional] What option mode the row is in. 0 = Default, 1 = OptionList, 2 == Keybind")
|
|
{
|
|
{
|
|
- object->addRow( label, callback, icon, yPad, useHighlightIcon, enabled );
|
|
|
|
|
|
+ object->addRow( label, callback, icon, yPad, useHighlightIcon, enabled, mode);
|
|
}
|
|
}
|
|
|
|
|
|
DefineEngineMethod( GuiGameListMenuCtrl, isRowEnabled, bool, ( S32 row ),,
|
|
DefineEngineMethod( GuiGameListMenuCtrl, isRowEnabled, bool, ( S32 row ),,
|
|
@@ -715,16 +1100,65 @@ DefineEngineMethod( GuiGameListMenuCtrl, getSelectedRow, S32, (),,
|
|
return object->getSelected();
|
|
return object->getSelected();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+DefineEngineMethod(GuiGameListMenuCtrl, clearRows, void, (), ,
|
|
|
|
+ "Gets the index of the currently selected row.\n\n"
|
|
|
|
+ "@return Index of the selected row.")
|
|
|
|
+{
|
|
|
|
+ return object->clearRows();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+DefineEngineMethod(GuiGameListMenuCtrl, addOptionRow, void,
|
|
|
|
+ (const char* label, const char* options, bool wrapOptions, const char* callback, S32 icon, S32 yPad, bool enabled),
|
|
|
|
+ (-1, 0, true),
|
|
|
|
+ "Add a row to the list control.\n\n"
|
|
|
|
+ "@param label The text to display on the row as a label.\n"
|
|
|
|
+ "@param options A tab separated list of options.\n"
|
|
|
|
+ "@param wrapOptions Specify true to allow options to wrap at each end or false to prevent wrapping.\n"
|
|
|
|
+ "@param callback Name of a script function to use as a callback when this row is activated.\n"
|
|
|
|
+ "@param icon [optional] Index of the icon to use as a marker.\n"
|
|
|
|
+ "@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n"
|
|
|
|
+ "@param enabled [optional] If this row is initially enabled.")
|
|
|
|
+{
|
|
|
|
+ object->addRow(label, options, wrapOptions, callback, icon, yPad, enabled);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+DefineEngineMethod(GuiGameListMenuCtrl, getCurrentOption, const char*, (S32 row), ,
|
|
|
|
+ "Gets the text for the currently selected option of the given row.\n\n"
|
|
|
|
+ "@param row Index of the row to get the option from.\n"
|
|
|
|
+ "@return A string representing the text currently displayed as the selected option on the given row. If there is no such displayed text then the empty string is returned.")
|
|
|
|
+{
|
|
|
|
+ return object->getCurrentOption(row);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+DefineEngineMethod(GuiGameListMenuCtrl, selectOption, bool, (S32 row, const char* option), ,
|
|
|
|
+ "Set the row's current option to the one specified\n\n"
|
|
|
|
+ "@param row Index of the row to set an option on.\n"
|
|
|
|
+ "@param option The option to be made active.\n"
|
|
|
|
+ "@return True if the row contained the option and was set, false otherwise.")
|
|
|
|
+{
|
|
|
|
+ return object->selectOption(row, option);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+DefineEngineMethod(GuiGameListMenuCtrl, setOptions, void, (S32 row, const char* optionsList), ,
|
|
|
|
+ "Sets the list of options on the given row.\n\n"
|
|
|
|
+ "@param row Index of the row to set options on."
|
|
|
|
+ "@param optionsList A tab separated list of options for the control.")
|
|
|
|
+{
|
|
|
|
+ object->setOptions(row, optionsList);
|
|
|
|
+}
|
|
|
|
+
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// GuiGameListMenuProfile
|
|
// GuiGameListMenuProfile
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
GuiGameListMenuProfile::GuiGameListMenuProfile()
|
|
GuiGameListMenuProfile::GuiGameListMenuProfile()
|
|
-: mHitAreaUpperLeft(0, 0),
|
|
|
|
- mHitAreaLowerRight(0, 0),
|
|
|
|
- mIconOffset(0, 0),
|
|
|
|
- mRowSize(0, 0),
|
|
|
|
- mRowScale(1.0f, 1.0f)
|
|
|
|
|
|
+ : mHitAreaUpperLeft(0, 0),
|
|
|
|
+ mHitAreaLowerRight(0, 0),
|
|
|
|
+ mIconOffset(0, 0),
|
|
|
|
+ mRowSize(0, 0),
|
|
|
|
+ mRowScale(1.0f, 1.0f),
|
|
|
|
+ mColumnSplit(0),
|
|
|
|
+ mRightPad(0)
|
|
{
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
@@ -775,6 +1209,12 @@ void GuiGameListMenuProfile::enforceConstraints()
|
|
Point2I rowTexExtent = getBitmapArrayRect(TEX_NORMAL).extent;
|
|
Point2I rowTexExtent = getBitmapArrayRect(TEX_NORMAL).extent;
|
|
mRowScale.x = (float) getRowWidth() / rowTexExtent.x;
|
|
mRowScale.x = (float) getRowWidth() / rowTexExtent.x;
|
|
mRowScale.y = (float) getRowHeight() / rowTexExtent.y;
|
|
mRowScale.y = (float) getRowHeight() / rowTexExtent.y;
|
|
|
|
+
|
|
|
|
+ if (mHitAreaUpperLeft.x > mColumnSplit || mColumnSplit > mHitAreaLowerRight.x)
|
|
|
|
+ Con::errorf("GuiGameListOptionsProfile: You can't create %s with a ColumnSplit outside the hit area. You set the split to %d. Please change the ColumnSplit to be in the range [%d, %d].",
|
|
|
|
+ getName(), mColumnSplit, mHitAreaUpperLeft.x, mHitAreaLowerRight.x);
|
|
|
|
+
|
|
|
|
+ mColumnSplit = mClamp(mColumnSplit, mHitAreaUpperLeft.x, mHitAreaLowerRight.x);
|
|
}
|
|
}
|
|
|
|
|
|
Point2I GuiGameListMenuProfile::getIconExtent()
|
|
Point2I GuiGameListMenuProfile::getIconExtent()
|
|
@@ -827,6 +1267,8 @@ ConsoleDocClass( GuiGameListMenuProfile,
|
|
" hitAreaLowerRight = \"190 18\";\n"
|
|
" hitAreaLowerRight = \"190 18\";\n"
|
|
" iconOffset = \"10 2\";\n"
|
|
" iconOffset = \"10 2\";\n"
|
|
" rowSize = \"200 20\";\n"
|
|
" rowSize = \"200 20\";\n"
|
|
|
|
+ " columnSplit = \"100\";\n"
|
|
|
|
+ " rightPad = \"4\";\n"
|
|
" //Properties not specific to this control have been omitted from this example.\n"
|
|
" //Properties not specific to this control have been omitted from this example.\n"
|
|
"};\n"
|
|
"};\n"
|
|
"@endtsexample\n\n"
|
|
"@endtsexample\n\n"
|
|
@@ -848,6 +1290,12 @@ void GuiGameListMenuProfile::initPersistFields()
|
|
addField( "rowSize", TypePoint2I, Offset(mRowSize, GuiGameListMenuProfile),
|
|
addField( "rowSize", TypePoint2I, Offset(mRowSize, GuiGameListMenuProfile),
|
|
"The base size (\"width height\") of a row" );
|
|
"The base size (\"width height\") of a row" );
|
|
|
|
|
|
|
|
+ addField("columnSplit", TypeS32, Offset(mColumnSplit, GuiGameListMenuProfile),
|
|
|
|
+ "Padding between the leftmost edge of the control, and the row's left arrow.");
|
|
|
|
+
|
|
|
|
+ addField("rightPad", TypeS32, Offset(mRightPad, GuiGameListMenuProfile),
|
|
|
|
+ "Padding between the rightmost edge of the control and the row's right arrow.");
|
|
|
|
+
|
|
Parent::initPersistFields();
|
|
Parent::initPersistFields();
|
|
|
|
|
|
removeField("tab");
|
|
removeField("tab");
|