//============================================================================== // 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 */ //============================================================================== /// Summary: /// 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 /// /// \param %device (string) The name of the device the input event is coming fromt /// \param %action (string) The specific key/input action /// \param %state (bool) The down/up state of the event sent function UIMenuButtonList::onInputEvent(%this, %device, %action, %state) { if(%state) $activeMenuButtonContainer.processInputs(%device, %action); } //============================================================================== /// Summary: /// 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 /// /// \param %device (string) The name of the device the input event is coming fromt /// \param %action (string) The specific key/input action /// \param %axisVal (float) The float value of the axis event 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); } //============================================================================== /// Summary: /// 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(); } //============================================================================== /// Summary: /// Disables the MenuInputButton, marking it as not to consume inputs or display function MenuInputButton::disable(%this) { %this.setText(""); %this.Command = ""; %this.setActive(false); %this.setVisible(false); } //============================================================================== /// Summary: /// 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; } //============================================================================== /// Summary: /// 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(); } } //============================================================================== /// Summary: /// 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(); } //============================================================================== /// Summary: /// 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"; } //============================================================================== /// Summary: /// 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. /// /// \param %device (string) The device that is causing the input event /// \param %action (string) The name of the input action 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); } } } } //============================================================================== /// Summary: /// 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 /// /// \param %device (string) The name of the device the input event is coming fromt /// \param %action (string) The specific key/input action /// \param %axisVal (float) The float value of the axis event 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 //============================================================================== //============================================================================== /// Summary: /// 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 /// /// \param %device (string) The name of the device the input event is coming fromt /// \param %action (string) The specific key/input action /// \param %axisVal (float) The float value of the axis event 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(); } } } //============================================================================== /// Summary: /// 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 /// /// \param %device (string) The name of the device the input event is coming fromt /// \param %action (string) The specific key/input action /// \param %state (bool) The down/up state of the event sent 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 //============================================================================== //============================================================================== /// Summary: /// Is the GUIContainer with this MenuList namespace the 'active' menulist as far /// as UI interfaces is concerned? function MenuList::isActiveMenuList(%this) { if($activeMenuList == %this) return true; return false; } //============================================================================== /// Summary: /// Sets the GUIContainer with this MenuList namespace as the active menulist. /// This means that any input events caught in MenuInputHandlers is directed at /// this menu list to navigate it /// /// \param %startPosition (Point2F) The X and Y starting positions of the selection for this menuList /// \param %menuMode (string) Indicates the mode/type of menuList, allowing for special behaviors depending on type 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(); } //============================================================================== /// Summary: /// Activates the currently highlighted child object 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(); } } //============================================================================== /// Summary: /// refreshes the menuList, updating children highlight status and if there is /// a button pointer control defined on our list, we update it's position as /// needed 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); } } //============================================================================== /// Summary: /// Selects the next 'up' child item in the menuList. If the current is the topmost /// then nothing happens function MenuList::navigateUp(%this) { $activeMenuList.ListPosition.y -= 1; if($activeMenuList.ListPosition.y < 0) $activeMenuList.ListPosition.y = 0; %this.refresh(); } //============================================================================== /// Summary: /// Selects the next 'down' child item in the menuList. If the current is the bottommost /// then nothing happens function MenuList::navigateDown(%this) { $activeMenuList.ListPosition.y += 1; if($activeMenuList.ListPosition.y >= $activeMenuList.getCount()) $activeMenuList.ListPosition.y = $activeMenuList.getCount()-1; %this.refresh(); } //============================================================================== /// Summary: /// Selects the next 'left' child item in the menuList. If the current item is the leftmost /// then nothing happens 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(); } } } //============================================================================== /// Summary: /// Selects the next 'right' child item in the menuList. If the current item is the rightmost /// then nothing happens 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(); } } } //============================================================================== /// Summary: /// Gets the current vertical positionally selected child object function MenuList::getActiveRow(%this) { return $activeMenuList.ListPosition.y; } //============================================================================== /// Summary: /// Called from the engine when a GUIButtonBase-derived class with MenuListButton namespace class /// has its highlighting status changed. Allows us to react to this change of state and trigger refreshse /// or other events to keep the navigation tracking up to date /// /// \param %state (bool) The on/off state of the button being highlighted function MenuListButton::onHighlighted(%this, %state) { echo("MenuListButton::onHighlighted() - " @ %this.internalName @ " was " @ %state @ " highlighted"); %parentContainer = %this.getParent(); if(%parentContainer.class $= "MenuList" || %parentContainer.superClass $= "MenuList") { if(isObject(%parentContainer.buttonPointerCtrl)) { if(%state) { %parentContainer.buttonPointerCtrl.setHidden(false); %buttonCenter = %this.getGlobalCenter(); echo(" - button center:" @ %buttonCenter); 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); } echo(" - pointer position:" @ %parentContainer.buttonPointerCtrl.getPosition()); } /*else { %parentContainer.buttonPointerCtrl.setHidden(true); }*/ } } }