Torque3D/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp

589 lines
17 KiB
C++
Raw Normal View History

2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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/buttons/guiButtonBaseCtrl.h"
#include "console/console.h"
#include "console/engineAPI.h"
#include "gfx/gfxDevice.h"
#include "gui/core/guiCanvas.h"
#include "i18n/lang.h"
#include "sfx/sfxSystem.h"
#include "sfx/sfxTrack.h"
IMPLEMENT_CONOBJECT(GuiButtonBaseCtrl);
2012-09-19 15:15:01 +00:00
ConsoleDocClass(GuiButtonBaseCtrl,
2012-09-19 15:15:01 +00:00
"@brief The base class for the various button controls.\n\n"
2012-09-19 15:15:01 +00:00
"This is the base class for the various types of button controls. If no more specific functionality is required than "
"offered by this class, then it can be instantiated and used directly. Otherwise, its subclasses should be used:\n"
2012-09-19 15:15:01 +00:00
"- GuiRadioCtrl (radio buttons)\n"
"- GuiCheckBoxCtrl (checkboxes)\n"
"- GuiButtonCtrl (push buttons with text labels)\n"
"- GuiBitmapButtonCtrl (bitmapped buttons)\n"
"- GuiBitmapButtonTextCtrl (bitmapped buttons with a text label)\n"
"- GuiToggleButtonCtrl (toggle buttons, i.e. push buttons with \"sticky\" behavior)\n"
"- GuiSwatchButtonCtrl (color swatch buttons)\n"
"- GuiBorderButtonCtrl (push buttons for surrounding child controls)\n\n"
2012-09-19 15:15:01 +00:00
"@ingroup GuiButtons"
);
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onMouseDown, void, (), (),
2012-09-19 15:15:01 +00:00
"If #useMouseEvents is true, this is called when the left mouse button is pressed on an (active) "
"button.");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onMouseUp, void, (), (),
2012-09-19 15:15:01 +00:00
"If #useMouseEvents is true, this is called when the left mouse button is release over an (active) "
"button.\n\n"
"@note To trigger actions, better use onClick() since onMouseUp() will also be called when the mouse was "
"not originally pressed on the button.");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onClick, void, (), (),
"Called when the primary action of the button is triggered (e.g. by a left mouse click).");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onDoubleClick, void, (), (),
"Called when the left mouse button is double-clicked on the button.");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onRightClick, void, (), (),
"Called when the right mouse button is clicked on the button.");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onMouseEnter, void, (), (),
2012-09-19 15:15:01 +00:00
"If #useMouseEvents is true, this is called when the mouse cursor moves over the button (only if the button "
"is the front-most visible control, though).");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onMouseLeave, void, (), (),
2012-09-19 15:15:01 +00:00
"If #useMouseEvents is true, this is called when the mouse cursor moves off the button (only if the button "
"had previously received an onMouseEvent() event).");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onMouseDragged, void, (), (),
2012-09-19 15:15:01 +00:00
"If #useMouseEvents is true, this is called when a left mouse button drag is detected, i.e. when the user "
"pressed the left mouse button on the control and then moves the mouse over a certain distance threshold with "
"the mouse button still pressed.");
2012-09-19 15:15:01 +00:00
IMPLEMENT_CALLBACK(GuiButtonBaseCtrl, onHighlighted, void, (bool highlighted), (highlighted),
"This is called when the highlighted state of the button is changed.");
2012-09-19 15:15:01 +00:00
ImplementEnumType(GuiButtonType,
2012-09-19 15:15:01 +00:00
"Type of button control.\n\n"
"@ingroup GuiButtons")
{
GuiButtonBaseCtrl::ButtonTypePush, "PushButton", "A button that triggers an action when clicked."
},
{ GuiButtonBaseCtrl::ButtonTypeCheck, "ToggleButton", "A button that is toggled between on and off state." },
{ GuiButtonBaseCtrl::ButtonTypeRadio, "RadioButton", "A button placed in groups for presenting choices." },
2012-09-19 15:15:01 +00:00
EndImplementEnumType;
//-----------------------------------------------------------------------------
GuiButtonBaseCtrl::GuiButtonBaseCtrl()
{
mDepressed = false;
2022-02-18 00:04:31 +00:00
mHighlighted = false;
2012-09-19 15:15:01 +00:00
mActive = true;
static StringTableEntry sButton = StringTable->insert("Button");
2012-09-19 15:15:01 +00:00
mButtonText = sButton;
mButtonTextID = StringTable->EmptyString();
mStateOn = false;
mRadioGroup = -1;
mButtonType = ButtonTypePush;
mUseMouseEvents = false;
mMouseDragged = false;
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::initPersistFields()
{
docsURL;
addGroup("Button");
addField("text", TypeCaseString, Offset(mButtonText, GuiButtonBaseCtrl),
"Text label to display on button (if button class supports text labels).");
addField("textID", TypeString, Offset(mButtonTextID, GuiButtonBaseCtrl),
"ID of string in string table to use for text label on button.\n\n"
"@see setTextID\n"
"@see GuiControl::langTableMod\n"
"@see LangTable\n\n");
addField("groupNum", TypeS32, Offset(mRadioGroup, GuiButtonBaseCtrl),
"Radio button toggle group number. All radio buttons that are assigned the same #groupNum and that "
"are parented to the same control will synchronize their toggle state, i.e. if one radio button is toggled on "
"all other radio buttons in its group will be toggled off.\n\n"
"The default group is -1.");
addField("buttonType", TYPEID< ButtonType >(), Offset(mButtonType, GuiButtonBaseCtrl),
"Button behavior type.\n");
addField("useMouseEvents", TypeBool, Offset(mUseMouseEvents, GuiButtonBaseCtrl),
"If true, mouse events will be passed on to script. Default is false.\n");
endGroup("Button");
2012-09-19 15:15:01 +00:00
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
bool GuiButtonBaseCtrl::onWake()
{
if (!Parent::onWake())
2012-09-19 15:15:01 +00:00
return false;
// is we have a script variable, make sure we're in sync
if (mConsoleVariable[0])
mStateOn = Con::getBoolVariable(mConsoleVariable);
if (mButtonTextID && *mButtonTextID != 0)
setTextID(mButtonTextID);
2012-09-19 15:15:01 +00:00
return true;
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::setText(const char* text)
2012-09-19 15:15:01 +00:00
{
mButtonText = StringTable->insert(text, true);
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::setTextID(const char* id)
2012-09-19 15:15:01 +00:00
{
S32 n = Con::getIntVariable(id, -1);
if (n != -1)
{
mButtonTextID = StringTable->insert(id);
setTextID(n);
}
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::setTextID(S32 id)
{
const UTF8* str = getGUIString(id);
if (str)
setText((const char*)str);
//mButtonTextID = id;
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
const char* GuiButtonBaseCtrl::getText()
2012-09-19 15:15:01 +00:00
{
return mButtonText;
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::setStateOn(bool bStateOn)
2012-09-19 15:15:01 +00:00
{
if (!mActive)
2012-09-19 15:15:01 +00:00
return;
if (mButtonType == ButtonTypeCheck)
2012-09-19 15:15:01 +00:00
{
mStateOn = bStateOn;
}
else if (mButtonType == ButtonTypeRadio)
2012-09-19 15:15:01 +00:00
{
messageSiblings(mRadioGroup);
mStateOn = bStateOn;
}
2012-09-19 15:15:01 +00:00
setUpdate();
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::acceleratorKeyPress(U32)
{
if (!mActive)
2012-09-19 15:15:01 +00:00
return;
//set the bool
mDepressed = true;
if (mProfile->mTabable)
setFirstResponder();
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::acceleratorKeyRelease(U32)
{
if (!mActive)
2012-09-19 15:15:01 +00:00
return;
if (mDepressed)
{
//set the bool
mDepressed = false;
//perform the action
onAction();
}
//update
setUpdate();
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMouseDown(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
if (!mActive)
2012-09-19 15:15:01 +00:00
return;
if (mProfile->mCanKeyFocus)
setFirstResponder();
2022-08-30 02:18:20 +00:00
if (mProfile->isSoundButtonDownValid())
SFX->playOnce(mProfile->getSoundButtonDownProfile());
2012-09-19 15:15:01 +00:00
mMouseDownPoint = event.mousePoint;
mMouseDragged = false;
if (mUseMouseEvents)
onMouseDown_callback();
2012-09-19 15:15:01 +00:00
//lock the mouse
mouseLock();
mDepressed = true;
// If we have a double click then execute the alt command.
if (event.mouseClickCount == 2)
2012-09-19 15:15:01 +00:00
{
onDoubleClick_callback();
execAltConsoleCallback();
}
//update
setUpdate();
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMouseEnter(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
setUpdate();
if (mUseMouseEvents)
2012-09-19 15:15:01 +00:00
onMouseEnter_callback();
if (isMouseLocked())
2012-09-19 15:15:01 +00:00
{
mDepressed = true;
2022-02-18 00:04:31 +00:00
mHighlighted = true;
onHighlighted_callback(mHighlighted);
2012-09-19 15:15:01 +00:00
}
else
{
2022-08-30 02:18:20 +00:00
if (mProfile->isSoundButtonOverValid())
SFX->playOnce(mProfile->getSoundButtonOverProfile());
2012-09-19 15:15:01 +00:00
2022-02-18 00:04:31 +00:00
mHighlighted = true;
messageSiblings(mRadioGroup);
onHighlighted_callback(mHighlighted);
2012-09-19 15:15:01 +00:00
}
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMouseLeave(const GuiEvent&)
2012-09-19 15:15:01 +00:00
{
setUpdate();
if (mUseMouseEvents)
2012-09-19 15:15:01 +00:00
onMouseLeave_callback();
if (isMouseLocked())
2012-09-19 15:15:01 +00:00
mDepressed = false;
2022-02-18 00:04:31 +00:00
mHighlighted = false;
onHighlighted_callback(mHighlighted);
messageSiblings(mRadioGroup);
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMouseUp(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
mouseUnlock();
if (!mActive)
2012-09-19 15:15:01 +00:00
return;
2012-09-19 15:15:01 +00:00
setUpdate();
if (mUseMouseEvents)
2012-09-19 15:15:01 +00:00
onMouseUp_callback();
//if we released the mouse within this control, perform the action
if (mDepressed)
2012-09-19 15:15:01 +00:00
onAction();
mDepressed = false;
mMouseDragged = false;
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onRightMouseUp(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
onRightClick_callback();
Parent::onRightMouseUp(event);
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMouseDragged(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
if (mUseMouseEvents)
2012-09-19 15:15:01 +00:00
{
// If we haven't started a drag yet, find whether we have moved past
// the tolerance value.
if (!mMouseDragged)
2012-09-19 15:15:01 +00:00
{
Point2I delta = mMouseDownPoint - event.mousePoint;
if (mAbs(delta.x) > 2 || mAbs(delta.y) > 2)
2012-09-19 15:15:01 +00:00
mMouseDragged = true;
}
if (mMouseDragged)
2012-09-19 15:15:01 +00:00
onMouseDragged_callback();
}
Parent::onMouseDragged(event);
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
bool GuiButtonBaseCtrl::onKeyDown(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
//if the control is a dead end, kill the event
if (!mActive)
return true;
//see if the key down is a return or space or not
if ((event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE)
&& event.modifier == 0)
2012-09-19 15:15:01 +00:00
{
2022-08-30 02:18:20 +00:00
if (mProfile->isSoundButtonDownValid())
SFX->playOnce(mProfile->getSoundButtonDownProfile());
2012-09-19 15:15:01 +00:00
return true;
}
//otherwise, pass the event to it's parent
return Parent::onKeyDown(event);
}
//-----------------------------------------------------------------------------
bool GuiButtonBaseCtrl::onKeyUp(const GuiEvent& event)
2012-09-19 15:15:01 +00:00
{
//if the control is a dead end, kill the event
if (!mActive)
return true;
//see if the key down is a return or space or not
if (mDepressed &&
(event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE) &&
event.modifier == 0)
{
onAction();
return true;
}
//otherwise, pass the event to it's parent
return Parent::onKeyUp(event);
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::setScriptValue(const char* value)
2012-09-19 15:15:01 +00:00
{
mStateOn = dAtob(value);
2012-09-19 15:15:01 +00:00
// Update the console variable:
if (mConsoleVariable[0])
Con::setBoolVariable(mConsoleVariable, mStateOn);
2012-09-19 15:15:01 +00:00
setUpdate();
}
//-----------------------------------------------------------------------------
const char* GuiButtonBaseCtrl::getScriptValue()
2012-09-19 15:15:01 +00:00
{
return mStateOn ? "1" : "0";
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onAction()
{
if (!mActive)
return;
2012-09-19 15:15:01 +00:00
if (mButtonType == ButtonTypeCheck)
{
mStateOn = mStateOn ? false : true;
}
else if (mButtonType == ButtonTypeRadio)
{
mStateOn = true;
messageSiblings(mRadioGroup);
2012-09-19 15:15:01 +00:00
}
setUpdate();
2012-09-19 15:15:01 +00:00
// Update the console variable:
if (mConsoleVariable[0])
Con::setBoolVariable(mConsoleVariable, mStateOn);
2012-09-19 15:15:01 +00:00
onClick_callback();
Parent::onAction();
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
void GuiButtonBaseCtrl::onMessage(GuiControl* sender, S32 msg)
2012-09-19 15:15:01 +00:00
{
Parent::onMessage(sender, msg);
if (mRadioGroup == msg)
{
if (mButtonType == ButtonTypeRadio)
{
setUpdate();
mStateOn = (sender == this);
2012-09-19 15:15:01 +00:00
// Update the console variable:
if (mConsoleVariable[0])
Con::setBoolVariable(mConsoleVariable, mStateOn);
}
else if (mButtonType == ButtonTypePush)
{
mHighlighted = (sender == this);
onHighlighted_callback(mHighlighted);
}
}
}
void GuiButtonBaseCtrl::setHighlighted(bool highlighted)
{
mHighlighted = highlighted;
onHighlighted_callback(mHighlighted);
if (mRadioGroup != -1)
{
messageSiblings(mRadioGroup);
}
2012-09-19 15:15:01 +00:00
}
//=============================================================================
// Console Methods.
//=============================================================================
// MARK: ---- Console Methods ----
//-----------------------------------------------------------------------------
DefineEngineMethod(GuiButtonBaseCtrl, performClick, void, (), ,
2012-09-19 15:15:01 +00:00
"Simulate a click on the button.\n"
"This method will trigger the button's action just as if the button had been pressed by the "
"user.\n\n")
2012-09-19 15:15:01 +00:00
{
object->onAction();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(GuiButtonBaseCtrl, setText, void, (const char* text), ,
2012-09-19 15:15:01 +00:00
"Set the text displayed on the button's label.\n"
"@param text The text to display as the button's text label.\n"
"@note Not all buttons render text labels.\n\n"
"@see getText\n"
"@see setTextID\n")
2012-09-19 15:15:01 +00:00
{
object->setText(text);
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
DefineEngineMethod(GuiButtonBaseCtrl, setTextID, void, (const char* id), ,
2012-09-19 15:15:01 +00:00
"Set the text displayed on the button's label using a string from the string table "
"assigned to the control.\n\n"
"@param id Name of the variable that contains the integer string ID. Used to look up "
"string in table.\n\n"
2012-09-19 15:15:01 +00:00
"@note Not all buttons render text labels.\n\n"
"@see setText\n"
"@see getText\n"
"@see GuiControl::langTableMod\n"
"@see LangTable\n\n"
"@ref Gui_i18n")
2012-09-19 15:15:01 +00:00
{
object->setTextID(id);
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
DefineEngineMethod(GuiButtonBaseCtrl, getText, const char*, (), ,
2012-09-19 15:15:01 +00:00
"Get the text display on the button's label (if any).\n\n"
"@return The button's label.")
2012-09-19 15:15:01 +00:00
{
return object->getText();
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
DefineEngineMethod(GuiButtonBaseCtrl, setStateOn, void, (bool isOn), (true),
2012-09-19 15:15:01 +00:00
"For toggle or radio buttons, set whether the button is currently activated or not. For radio buttons, "
"toggling a button on will toggle all other radio buttons in its group to off.\n\n"
"@param isOn If true, the button will be toggled on (if not already); if false, it will be toggled off.\n\n"
"@note Toggling the state of a button with this method will <em>not</em> not trigger the action associated with the "
"button. To do that, use performClick().")
2012-09-19 15:15:01 +00:00
{
object->setStateOn(isOn);
2012-09-19 15:15:01 +00:00
}
//-----------------------------------------------------------------------------
DefineEngineMethod(GuiButtonBaseCtrl, resetState, void, (), ,
2012-09-19 15:15:01 +00:00
"Reset the mousing state of the button.\n\n"
"This method should not generally be called.")
2012-09-19 15:15:01 +00:00
{
object->resetState();
}
2022-02-18 00:04:31 +00:00
DefineEngineMethod(GuiButtonBaseCtrl, setHighlighted, void, (bool highlighted), (false),
"Reset the mousing state of the button.\n\n"
"This method should not generally be called.")
{
object->setHighlighted(highlighted);
}
DefineEngineMethod(GuiButtonBaseCtrl, isHighlighted, bool, (), ,
2022-02-18 00:04:31 +00:00
"Reset the mousing state of the button.\n\n"
"This method should not generally be called.")
{
return object->isHighlighted();
}