Torque3D/Templates/BaseGame/game/data/UI/scripts/menuInputHandling.tscript
JeffR 41add628ad Implements a more standardized way to format usual UI pages by having the ability to utilize the UINavigation namespace for page stack navigation
Also fixes behavior handling of menu input buttons not refreshing reliably
Adds ability to define a control on a MenuList to act as a highlighter over the currently selected control
Cleaned up BaseUI pages to use UINavigation which reduced a lot of duplication of elements and code
2022-05-06 23:39:16 -05:00

623 lines
18 KiB
Plaintext

//==============================================================================
// Menu Input Buttons
// This file manages the Menu Input Buttons stuff
// Any time you have a GUI button that should be clickable AND map to a key input
// such as a gamepad button, or enter, etc, this stuff can be used
//==============================================================================
/*
Gamepad input reference for 360 controller
btn_a = A
btn_b = B
btn_x = X
btn_y = Y
btn_r = Right Bumper
btn_l = Right Bumper
upov = Dpad Up
dpov = Dpad Down
lpov = Dpad Left
rpov = Dpad Right
xaxis = Left Stick | + values = up, - values = down
yaxis = Left Stick | + values = up, - values = down
rxaxis = Right Stick | + values = up, - values = down
ryaxis = Right Stick | + values = up, - values = down
zaxis = Left Trigger
rzaxis = Right Trigger
btn_start = Start
btn_back = Back/Select
*/
/// This is used with the main UI menu lists, when a non-axis input event is called
/// such as pressing a button
/// It is called from the engine
function UIMenuButtonList::onInputEvent(%this, %device, %action, %state)
{
if(%state)
$activeMenuButtonContainer.processInputs(%device, %action);
}
/// This is used with the main UI menu lists, when an axis input event is called
/// such as moving a joystick
/// It is called from the engine
function UIMenuButtonList::onAxisEvent(%this, %device, %action, %axisVal)
{
//Skip out of the value is too low as it could just be noise or miscalibrated defaults
if(%axisVal < 0.02)
return;
$activeMenuButtonContainer.processAxisEvent(%device, %action);
}
/// Sets the command and text for the specified button. If %text and %command
/// are left empty, the button will be disabled and hidden.
///
/// \param %gamepadButton (string) The button to set for when using gamepad input. See the input map reference comment at the top of the file
/// \param %keyboardButton (string) The button to set for when using keyboard/mouse input.
/// \param %text (string) The text to display next to the A button graphic.
/// \param %command (string) The command executed when the A button is pressed.
function MenuInputButton::set(%this, %gamepadButton, %keyboardButton, %text, %command)
{
%this.setHidden(false);
%set = (! ((%text $= "") && (%command $= "")));
%this.gamepadButton = %gamepadButton;
%this.keyboardButton = %keyboardButton;
if(%gamepadButton $= "")
%this.gamepadValid = false;
else
%this.gamepadValid = true;
if(%keyboardButton $= "")
%this.kbmValid = false;
else
%this.kbmValid = true;
if((!%this.kbmValid && $activeControllerType !$= "gamepad") ||
(!%this.gamepadValid && $activeControllerType $= "gamepad"))
%set = false;
%this.setText(%text);
%this.Command = %command;
%this.refresh();
}
function MenuInputButton::disable(%this)
{
%this.setText("");
%this.Command = "";
%this.setActive(false);
%this.setVisible(false);
}
/// Refreshes the specific button, updating it's visbility status and the displayed input image
function MenuInputButton::refresh(%this)
{
%set = (! ((%this.text $= "") && (%this.command $= "")));
//Do a check so if a MenuInput is selectively bound and we're not using the
//matched input type, then we skip
if((!%this.kbmValid && $activeControllerType !$= "gamepad") ||
(!%this.gamepadValid && $activeControllerType $= "gamepad"))
%set = false;
%this.setActive(%set);
%this.setVisible(%set);
if(!%this.isActive())
return;
if($activeControllerType $= "gamepad")
{
if(%this.gamepadButton !$= "")
{
%assetId = "";
if($activeControllerName $= "PS4 Controller")
{
%assetId = "UI:PS4_";
if(%this.gamepadButton $= "btn_a")
%assetId = %assetId @ "Cross";
else if(%this.gamepadButton $= "btn_b")
%assetId = %assetId @ "Circle";
else if(%this.gamepadButton $= "btn_x")
%assetId = %assetId @ "Square";
else if(%this.gamepadButton $= "btn_y")
%assetId = %assetId @ "Triangle";
else if(%this.gamepadButton $= "btn_l")
%assetId = %assetId @ "L1";
else if(%this.gamepadButton $= "zaxis")
%assetId = %assetId @ "L2";
else if(%this.gamepadButton $= "btn_r")
%assetId = %assetId @ "R1";
else if(%this.gamepadButton $= "rzaxis")
%assetId = %assetId @ "R2";
else if(%this.gamepadButton $= "btn_start")
%assetId = %assetId @ "Options";
else if(%this.gamepadButton $= "btn_back")
%assetId = %assetId @ "Share";
}
else if($activeControllerName $= "Nintendo Switch Pro Controller")
{
%assetId = "UI:Switch_";
if(%this.gamepadButton $= "btn_a")
%assetId = %assetId @ "B";
else if(%this.gamepadButton $= "btn_b")
%assetId = %assetId @ "A";
else if(%this.gamepadButton $= "btn_x")
%assetId = %assetId @ "Y";
else if(%this.gamepadButton $= "btn_y")
%assetId = %assetId @ "X";
else if(%this.gamepadButton $= "btn_l")
%assetId = %assetId @ "LB";
else if(%this.gamepadButton $= "zaxis")
%assetId = %assetId @ "LT";
else if(%this.gamepadButton $= "btn_r")
%assetId = %assetId @ "RB";
else if(%this.gamepadButton $= "rzaxis")
%assetId = %assetId @ "RT";
else if(%this.gamepadButton $= "btn_start")
%assetId = %assetId @ "Plus";
else if(%this.gamepadButton $= "btn_back")
%assetId = %assetId @ "Minus";
}
else if($activeControllerName !$= "")
{
%assetId = "UI:Xbox_";
if(%this.gamepadButton $= "btn_a")
%assetId = %assetId @ "A";
else if(%this.gamepadButton $= "btn_b")
%assetId = %assetId @ "B";
else if(%this.gamepadButton $= "btn_x")
%assetId = %assetId @ "X";
else if(%this.gamepadButton $= "btn_y")
%assetId = %assetId @ "Y";
else if(%this.gamepadButton $= "btn_l")
%assetId = %assetId @ "LB";
else if(%this.gamepadButton $= "zaxis")
%assetId = %assetId @ "LT";
else if(%this.gamepadButton $= "btn_r")
%assetId = %assetId @ "RB";
else if(%this.gamepadButton $= "rzaxis")
%assetId = %assetId @ "RT";
else if(%this.gamepadButton $= "btn_start")
%assetId = %assetId @ "Menu";
else if(%this.gamepadButton $= "btn_back")
%assetId = %assetId @ "Windows";
}
}
}
else
{
if(%this.keyboardButton !$= "")
{
%assetId = "UI:Keyboard_Black_" @ %this.keyboardButton;
}
}
%this.setBitmap(%assetId @ "_image");
return true;
}
/// Refreshes a menu input container, updating the buttons inside it
function MenuInputButtonContainer::refresh(%this)
{
%count = %this.getCount();
for(%i=0; %i < %count; %i++)
{
%btn = %this.getObject(%i);
%btn.refresh();
}
}
/// Sets the given MenuInputButtonContainer as the active one. This directs input events
/// to it's buttons, ensures it's visible, and auto-hides the old active container if it was set
function MenuInputButtonContainer::setActive(%this)
{
if(isObject($activeMenuButtonContainer))
$activeMenuButtonContainer.hidden = true;
$activeMenuButtonContainer = %this;
$activeMenuButtonContainer.hidden = false;
$activeMenuButtonContainer.refresh();
}
/// Checks the input manager for if we have a gamepad active and gets it's name
/// If we have one, also sets the active input type to gamepad
function MenuInputButtonContainer::checkGamepad(%this)
{
%controllerName = SDLInputManager::JoystickNameForIndex(0);
$activeControllerName = %controllerName;
if($activeControllerName $= "")
$activeControllerType = "K&M";
else
$activeControllerType = "gamepad";
}
/// This is called by the earlier inputs callback that comes from the menu list
/// this allows us to first check what the input type is, and if the device is different
/// (such as going from keyboard and mouse to gamepad) we can refresh the buttons to update
/// the display
/// Then we process the input to see if it matches to any of the button maps for our
/// MenuInputButtons. If we have a match, we execute it's command.
function MenuInputButtonContainer::processInputs(%this, %device, %action)
{
//check to see if our status has changed
%changed = false;
%oldDevice = $activeControllerName;
%deviceName = stripTrailingNumber(%device);
if(%deviceName $= "keyboard" || %deviceName $= "mouse")
{
if($activeControllerName !$= "K&M")
%changed = true;
$activeControllerName = "K&M";
$activeControllerType = "K&M";
Canvas.showCursor();
}
else
{
if(%this.checkGamepad())
{
Canvas.hideCursor();
}
if($activeControllerType !$= %oldDevice)
%changed = true;
}
if(%changed)
%this.refresh();
//Now process the input for the button accelerator, if applicable
//Set up our basic buttons
for(%i=0; %i < %this.getCount(); %i++)
{
%btn = %this.getObject(%i);
if(!%btn.isActive())
continue;
if($activeControllerType !$= "K&M")
{
if(%btn.gamepadButton $= %action)
{
eval(%btn.command);
}
}
else
{
if(%btn.keyboardButton $= %action)
{
eval(%btn.command);
}
}
}
}
/// This is called by the earlier inputs callback that comes from the menu list
/// this allows us to first check what the input type is, and if the device is different
/// (such as going from keyboard and mouse to gamepad) we can refresh the buttons to update
/// the display
function MenuInputButtonContainer::processAxisEvent(%this, %device, %action, %axisVal)
{
//check to see if our status has changed
%changed = false;
%oldDevice = $activeControllerName;
%deviceName = stripTrailingNumber(%device);
if(%deviceName $= "mouse")
{
if($activeControllerName !$= "K&M")
%changed = true;
$activeControllerName = "K&M";
$activeControllerType = "K&M";
Canvas.showCursor();
}
else
{
if(%this.checkGamepad())
{
Canvas.hideCursor();
}
if($activeControllerType !$= %oldDevice)
%changed = true;
}
if(%changed)
%this.refresh();
}
//
//
function onSDLDeviceConnected(%sdlIndex, %deviceName, %deviceType)
{
/*if(GamepadButtonsGui.checkGamepad())
{
GamepadButtonsGui.hidden = false;
}*/
}
function onSDLDeviceDisconnected(%sdlIndex)
{
/*if(!GamepadButtonsGui.checkGamepad())
{
GamepadButtonsGui.hidden = true;
}*/
}
//==============================================================================
// Menu Input processing
// These functions manage the Menu input processing in general
// Whenever a MenuInputHandler consumes an input event, it'll process them here
// This'll let the active menu list be navigated, as well as buttons be processed
// and ultimately handled by the Input Buttons above
//==============================================================================
function MenuInputHandler::onAxisEvent(%this, %device, %action, %value)
{
//this is to force a refresh of the menu
if(%value == 1 || %value == -1)
$activeMenuButtonContainer.processInputs(%device, %action);
if(startsWith(%device, "mouse"))
return;
if((%action $= "upov" && %value > 0) || (%action $= "yaxis" && %value == -1))
{
$activeMenuList.navigateUp();
}
if((%action $= "dpov" && %value > 0) || (%action $= "yaxis" && %value == 1))
{
$activeMenuList.navigateDown();
}
//How we deal with the left and right navigation is dependant on the mode of the
//menu list
if($activeMenuListMode $= "Settings")
{
if((%action $= "lpov" && %value > 0) || (%action $= "xaxis" && %value == -1))
{
echo("Options menu nudged left!");
//$activeMenuList.navigateLeft();
}
if((%action $= "rpov" && %value > 0) || (%action $= "xaxis" && %value == -1))
{
echo("Options menu nudged right!");
//$activeMenuList.navigateRight();
}
}
else
{
if((%action $= "lpov" && %value > 0) || (%action $= "xaxis" && %value == -1))
{
$activeMenuList.navigateLeft();
}
if((%action $= "rpov" && %value > 0) || (%action $= "xaxis" && %value == -1))
{
$activeMenuList.navigateRight();
}
}
}
function MenuInputHandler::onInputEvent(%this, %device, %action, %state)
{
if(%action $= "upov" || %action $= "dpov" || %action $= "lpov" || %action $= "rpov")
{
%this.onAxisEvent(%device, %action, %state);
return;
}
if(%state)
$activeMenuButtonContainer.processInputs(%device, %action);
}
//==============================================================================
// Menu List processing
// These functions manage the navigation and activation of the Menu Lists
//==============================================================================
function MenuList::isActiveMenuList(%this)
{
if($activeMenuList == %this)
return true;
return false;
}
function MenuList::setAsActiveMenuList(%this, %startPosition, %menuMode)
{
if(%startPosition $= "")
%startPosition = "0 0";
if(%menuMode $= "")
%menuMode = "Menu";
$activeMenuList = %this;
$activeMenuList.hidden = false;
$activeMenuList.ListPosition = %startPosition;
$activeMenuListMode = %menuMode;
%this.refresh();
}
function MenuList::activate(%this)
{
//check for a highlighted element
if($activeMenuList.ListPosition.y > -1 && $activeMenuList.ListPosition < $activeMenuList.getCount())
{
%btn = $activeMenuList.getObject($activeMenuList.ListPosition.y);
%btn.performClick();
}
}
function MenuList::refresh(%this)
{
%selectedObject = -1;
for(%i=0; %i < $activeMenuList.getCount(); %i++)
{
%btn = $activeMenuList.getObject(%i);
%isSelected = %i == $activeMenuList.ListPosition.y;
%btn.setHighlighted(%isSelected);
if(%isSelected)
%selectedObject = %i;
}
if(isObject(%this.buttonPointerCtrl))
{
if(%selectedObject != -1)
{
%this.buttonPointerCtrl.setHidden(false);
%buttonCenter = $activeMenuList.getObject(%selectedObject).getGlobalCenter();
if(%this.centerButtonPointerCtrl)
{
%this.buttonPointerCtrl.setCenter(%buttonCenter.x, %buttonCenter.y);
}
else
{
//if we're not centering, then left-justify
%this.buttonPointerCtrl.setCenter(%buttonCenter.x - $activeMenuList.getObject(%selectedObject).extent.x / 2, %buttonCenter.y);
}
}
else
{
%this.buttonPointerCtrl.setHidden(true);
}
}
if($activeMenuList.isMethod("onNavigate"))
$activeMenuList.onNavigate($activeMenuList.ListPosition.y);
%parent = $activeMenuList.getParent();
if(%parent.getClassName() $= "GuiScrollCtrl")
{
%parent.scrollToObject(%selectedObject);
}
}
function MenuList::navigateUp(%this)
{
$activeMenuList.ListPosition.y -= 1;
if($activeMenuList.ListPosition.y < 0)
$activeMenuList.ListPosition.y = 0;
%this.refresh();
}
function MenuList::navigateDown(%this)
{
$activeMenuList.ListPosition.y += 1;
if($activeMenuList.ListPosition.y >= $activeMenuList.getCount())
$activeMenuList.ListPosition.y = $activeMenuList.getCount()-1;
%this.refresh();
}
function MenuList::navigateLeft()
{
//Atm, we're only handling specific control types, namely options entries, but
//this could readily be expanded upon to handle grids like for inventory screens
//or the like
%btn = $activeMenuList.getObject($activeMenuList.ListPosition.y);
if(%btn.getClassName() $= "GuiGameSettingsCtrl" && %btn.isEnabled())
{
%mode = %btn.getMode();
if(%mode == 0) //options list
{
%optionId = %btn.getCurrentOptionIndex() - 1;
%btn.selectOptionByIndex(%optionId);
%btn.onChange();
}
else if(%mode == 1) //slider
{
%value = %btn.getValue();
%adjustedValue = %value - %btn.getIncrement();
%minValue = %btn.getRange().x;
if(%adjustedValue < %minValue)
%adjustedValue = %minValue;
%btn.setValue(%adjustedValue);
%btn.onChange();
}
}
}
function MenuList::navigateRight()
{
%btn = $activeMenuList.getObject($activeMenuList.ListPosition.y);
if(%btn.getClassName() $= "GuiGameSettingsCtrl" && %btn.isEnabled())
{
%mode = %btn.getMode();
if(%mode == 0) //options list
{
%optionId = %btn.getCurrentOptionIndex() + 1;
%btn.selectOptionByIndex(%optionId);
%btn.onChange();
}
else if(%mode == 1) //slider
{
%value = %btn.getValue();
%adjustedValue = %value + %btn.getIncrement();
%maxValue = %btn.getRange().y;
if(%adjustedValue > %maxValue)
%adjustedValue = %maxValue;
%btn.setValue(%adjustedValue);
%btn.onChange();
}
}
}
function MenuList::getActiveRow(%this)
{
return $activeMenuList.ListPosition.y;
}
function MenuListButton::onHighlighted(%this, %state)
{
%parentContainer = %this.getParent();
if(%parentContainer.class $= "MenuList" || %parentContainer.superClass $= "MenuList")
{
if(isObject(%parentContainer.buttonPointerCtrl))
{
if(%state)
{
%parentContainer.buttonPointerCtrl.setHidden(false);
%buttonCenter = %this.getGlobalCenter();
if(%parentContainer.centerButtonPointerCtrl)
{
%parentContainer.buttonPointerCtrl.setGlobalCenter(%buttonCenter.x, %buttonCenter.y);
}
else
{
//if we're not centering, then left-justify
%parentContainer.buttonPointerCtrl.setGlobalCenter(%buttonCenter.x - %this.extent.x / 2, %buttonCenter.y);
}
}
}
}
}