2014-06-20 01:30:09 +00:00
# include "guis/GuiInputConfig.h"
2016-12-02 23:00:10 +00:00
# include "guis/GuiMsgBox.h"
2014-06-20 01:30:09 +00:00
# include "Window.h"
# include "Log.h"
# include "components/TextComponent.h"
# include "components/ImageComponent.h"
# include "components/MenuComponent.h"
# include "components/ButtonComponent.h"
# include "Util.h"
2012-12-20 18:29:05 +00:00
2015-04-19 09:57:10 +00:00
// static const int inputCount = 10;
// static const char* inputName[inputCount] = { "Up", "Down", "Left", "Right", "A", "B", "Start", "Select", "PageUp", "PageDown" };
// static const bool inputSkippable[inputCount] = { false, false, false, false, false, false, false, false, true, true };
// static const char* inputDispName[inputCount] = { "UP", "DOWN", "LEFT", "RIGHT", "A", "B", "START", "SELECT", "PAGE UP", "PAGE DOWN" };
// static const char* inputIcon[inputCount] = { ":/help/dpad_up.svg", ":/help/dpad_down.svg", ":/help/dpad_left.svg", ":/help/dpad_right.svg",
// ":/help/button_a.svg", ":/help/button_b.svg", ":/help/button_start.svg", ":/help/button_select.svg",
// ":/help/button_l.svg", ":/help/button_r.svg" };
2016-12-02 23:00:10 +00:00
static const int inputCount = 25 ;
2015-04-19 09:57:10 +00:00
static const char * inputName [ inputCount ] =
{
2016-08-10 01:48:04 +00:00
" Up " ,
" Down " ,
" Left " ,
" Right " ,
" Start " ,
" Select " ,
" A " ,
" B " ,
" X " ,
" Y " ,
" LeftShoulder " ,
" RightShoulder " ,
" LeftTrigger " ,
" RightTrigger " ,
" LeftThumb " ,
" RightThumb " ,
" LeftAnalogUp " ,
" LeftAnalogDown " ,
" LeftAnalogLeft " ,
" LeftAnalogRight " ,
" RightAnalogUp " ,
" RightAnalogDown " ,
" RightAnalogLeft " ,
2016-12-02 23:00:10 +00:00
" RightAnalogRight " ,
" HotKeyEnable "
2015-04-19 09:57:10 +00:00
} ;
static const bool inputSkippable [ inputCount ] =
{
2016-08-10 01:48:04 +00:00
false ,
false ,
false ,
false ,
true ,
true ,
false ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
true ,
2016-12-02 23:00:10 +00:00
true ,
2016-08-10 01:48:04 +00:00
true
2015-04-19 09:57:10 +00:00
} ;
static const char * inputDispName [ inputCount ] =
{
2016-08-10 01:48:04 +00:00
" D-PAD UP " ,
" D-PAD DOWN " ,
" D-PAD LEFT " ,
" D-PAD RIGHT " ,
" START " ,
" SELECT " ,
" A " ,
" B " ,
" X " ,
" Y " ,
" LEFT SHOULDER " ,
" RIGHT SHOULDER " ,
" LEFT TRIGGER " ,
" RIGHT TRIGGER " ,
" LEFT THUMB " ,
" RIGHT THUMB " ,
" LEFT ANALOG UP " ,
" LEFT ANALOG DOWN " ,
" LEFT ANALOG LEFT " ,
" LEFT ANALOG RIGHT " ,
" RIGHT ANALOG UP " ,
" RIGHT ANALOG DOWN " ,
" RIGHT ANALOG LEFT " ,
2016-12-02 23:00:10 +00:00
" RIGHT ANALOG RIGHT " ,
" HOTKEY ENABLE "
2015-04-19 09:57:10 +00:00
} ;
static const char * inputIcon [ inputCount ] =
{
2016-08-10 01:48:04 +00:00
" :/help/dpad_up.svg " ,
" :/help/dpad_down.svg " ,
" :/help/dpad_left.svg " ,
" :/help/dpad_right.svg " ,
" :/help/button_start.svg " ,
" :/help/button_select.svg " ,
" :/help/button_a.svg " ,
" :/help/button_b.svg " ,
" :/help/button_x.svg " ,
" :/help/button_y.svg " ,
" :/help/button_l.svg " ,
" :/help/button_r.svg " ,
" :/help/button_l.svg " ,
" :/help/button_r.svg " ,
" :/help/analog_thumb.svg " ,
" :/help/analog_thumb.svg " ,
" :/help/analog_up.svg " ,
" :/help/analog_down.svg " ,
" :/help/analog_left.svg " ,
" :/help/analog_right.svg " ,
" :/help/analog_up.svg " ,
" :/help/analog_down.svg " ,
" :/help/analog_left.svg " ,
2016-12-02 23:00:10 +00:00
" :/help/analog_right.svg " ,
" :/help/button_hotkey.svg "
2015-04-19 09:57:10 +00:00
} ;
2013-06-19 21:07:12 +00:00
//MasterVolUp and MasterVolDown are also hooked up, but do not appear on this screen.
//If you want, you can manually add them to es_input.cfg.
2012-07-22 21:15:55 +00:00
2015-10-03 19:20:33 +00:00
# define HOLD_TO_SKIP_MS 1000
2014-04-13 02:09:54 +00:00
2014-03-22 01:12:57 +00:00
GuiInputConfig : : GuiInputConfig ( Window * window , InputConfig * target , bool reconfigureAll , const std : : function < void ( ) > & okCallback ) : GuiComponent ( window ) ,
2014-03-23 00:48:48 +00:00
mBackground ( window , " :/frame.png " ) , mGrid ( window , Vector2i ( 1 , 7 ) ) ,
2015-04-19 09:57:10 +00:00
mTargetConfig ( target ) , mHoldingInput ( false ) , mBusyAnim ( window )
2012-07-22 21:15:55 +00:00
{
2014-03-22 22:37:40 +00:00
LOG ( LogInfo ) < < " Configuring device " < < target - > getDeviceId ( ) < < " ( " < < target - > getDeviceName ( ) < < " ). " ;
2012-07-22 21:15:55 +00:00
2014-03-22 01:12:57 +00:00
if ( reconfigureAll )
target - > clear ( ) ;
2013-04-09 18:13:47 +00:00
2014-03-22 01:12:57 +00:00
mConfiguringAll = reconfigureAll ;
mConfiguringRow = mConfiguringAll ;
2012-07-22 21:15:55 +00:00
2014-03-22 01:12:57 +00:00
addChild ( & mBackground ) ;
addChild ( & mGrid ) ;
2014-03-23 00:48:48 +00:00
// 0 is a spacer row
mGrid . setEntry ( std : : make_shared < GuiComponent > ( mWindow ) , Vector2i ( 0 , 0 ) , false ) ;
2014-05-01 19:47:33 +00:00
mTitle = std : : make_shared < TextComponent > ( mWindow , " CONFIGURING " , Font : : get ( FONT_SIZE_LARGE ) , 0x555555FF , ALIGN_CENTER ) ;
2014-03-23 00:48:48 +00:00
mGrid . setEntry ( mTitle , Vector2i ( 0 , 1 ) , false , true ) ;
2014-03-22 01:12:57 +00:00
2014-03-23 00:48:48 +00:00
std : : stringstream ss ;
if ( target - > getDeviceId ( ) = = DEVICE_KEYBOARD )
ss < < " KEYBOARD " ;
else
ss < < " GAMEPAD " < < ( target - > getDeviceId ( ) + 1 ) ;
2014-05-01 19:47:33 +00:00
mSubtitle1 = std : : make_shared < TextComponent > ( mWindow , strToUpper ( ss . str ( ) ) , Font : : get ( FONT_SIZE_MEDIUM ) , 0x555555FF , ALIGN_CENTER ) ;
2014-03-23 00:48:48 +00:00
mGrid . setEntry ( mSubtitle1 , Vector2i ( 0 , 2 ) , false , true ) ;
2014-05-01 19:47:33 +00:00
mSubtitle2 = std : : make_shared < TextComponent > ( mWindow , " HOLD ANY BUTTON TO SKIP " , Font : : get ( FONT_SIZE_SMALL ) , 0x99999900 , ALIGN_CENTER ) ;
2014-03-23 00:48:48 +00:00
mGrid . setEntry ( mSubtitle2 , Vector2i ( 0 , 3 ) , false , true ) ;
2013-04-09 18:13:47 +00:00
2014-03-23 00:48:48 +00:00
// 4 is a spacer row
2014-03-22 01:12:57 +00:00
mList = std : : make_shared < ComponentList > ( mWindow ) ;
2014-03-23 00:48:48 +00:00
mGrid . setEntry ( mList , Vector2i ( 0 , 5 ) , true , true ) ;
2014-03-22 01:12:57 +00:00
for ( int i = 0 ; i < inputCount ; i + + )
2013-04-09 18:13:47 +00:00
{
2014-03-22 01:12:57 +00:00
ComponentListRow row ;
// icon
auto icon = std : : make_shared < ImageComponent > ( mWindow ) ;
icon - > setImage ( inputIcon [ i ] ) ;
2014-05-29 21:27:18 +00:00
icon - > setColorShift ( 0x777777FF ) ;
2014-03-25 23:41:50 +00:00
icon - > setResize ( 0 , Font : : get ( FONT_SIZE_MEDIUM ) - > getLetterHeight ( ) * 1.25f ) ;
2014-03-22 01:12:57 +00:00
row . addElement ( icon , false ) ;
2014-03-22 01:38:16 +00:00
// spacer between icon and text
auto spacer = std : : make_shared < GuiComponent > ( mWindow ) ;
spacer - > setSize ( 16 , 0 ) ;
row . addElement ( spacer , false ) ;
2014-03-22 01:12:57 +00:00
auto text = std : : make_shared < TextComponent > ( mWindow , inputDispName [ i ] , Font : : get ( FONT_SIZE_MEDIUM ) , 0x777777FF ) ;
row . addElement ( text , true ) ;
2014-05-01 19:47:33 +00:00
auto mapping = std : : make_shared < TextComponent > ( mWindow , " -NOT DEFINED- " , Font : : get ( FONT_SIZE_MEDIUM , FONT_PATH_LIGHT ) , 0x999999FF , ALIGN_RIGHT ) ;
2014-03-22 01:38:16 +00:00
setNotDefined ( mapping ) ; // overrides text and color set above
2014-03-22 01:12:57 +00:00
row . addElement ( mapping , true ) ;
2014-03-22 01:38:16 +00:00
mMappings . push_back ( mapping ) ;
2014-03-22 01:12:57 +00:00
row . input_handler = [ this , i , mapping ] ( InputConfig * config , Input input ) - > bool
2013-04-09 18:13:47 +00:00
{
2014-04-13 02:09:54 +00:00
// ignore input not from our target device
if ( config ! = mTargetConfig )
2014-03-22 01:12:57 +00:00
return false ;
2014-04-13 02:09:54 +00:00
// if we're not configuring, start configuring when A is pressed
if ( ! mConfiguringRow )
2013-04-09 18:13:47 +00:00
{
2014-03-22 01:12:57 +00:00
if ( config - > isMappedTo ( " a " , input ) & & input . value )
{
2014-04-14 00:30:24 +00:00
mList - > stopScrolling ( ) ;
2014-03-22 01:12:57 +00:00
mConfiguringRow = true ;
2014-03-22 01:38:16 +00:00
setPress ( mapping ) ;
2014-03-22 01:12:57 +00:00
return true ;
}
2014-04-13 02:09:54 +00:00
// we're not configuring and they didn't press A to start, so ignore this
2014-03-22 01:12:57 +00:00
return false ;
2013-04-09 18:13:47 +00:00
}
2014-04-13 02:09:54 +00:00
// we are configuring
if ( input . value ! = 0 )
{
// input down
// if we're already holding something, ignore this, otherwise plan to map this input
if ( mHoldingInput )
return true ;
mHoldingInput = true ;
mHeldInput = input ;
mHeldTime = 0 ;
mHeldInputId = i ;
return true ;
} else {
// input up
// make sure we were holding something and we let go of what we were previously holding
if ( ! mHoldingInput | | mHeldInput . device ! = input . device | | mHeldInput . id ! = input . id | | mHeldInput . type ! = input . type )
return true ;
mHoldingInput = false ;
2014-04-14 00:30:24 +00:00
if ( assign ( mHeldInput , i ) )
2014-04-13 02:09:54 +00:00
rowDone ( ) ; // if successful, move cursor/stop configuring - if not, we'll just try again
return true ;
}
2014-03-22 01:12:57 +00:00
} ;
2014-04-13 02:09:54 +00:00
2014-03-22 01:12:57 +00:00
mList - > addRow ( row ) ;
}
2013-04-09 18:13:47 +00:00
2014-04-13 02:09:54 +00:00
// only show "HOLD TO SKIP" if this input is skippable
mList - > setCursorChangedCallback ( [ this ] ( CursorState state ) {
bool skippable = inputSkippable [ mList - > getCursorId ( ) ] ;
mSubtitle2 - > setOpacity ( skippable * 255 ) ;
} ) ;
// make the first one say "PRESS ANYTHING" if we're re-configuring everything
2014-03-22 01:38:16 +00:00
if ( mConfiguringAll )
setPress ( mMappings . front ( ) ) ;
2014-03-22 01:12:57 +00:00
// buttons
std : : vector < std : : shared_ptr < ButtonComponent > > buttons ;
2016-12-02 23:00:10 +00:00
std : : function < void ( ) > okFunction = [ this , okCallback ] {
2014-04-18 18:07:32 +00:00
InputManager : : getInstance ( ) - > writeDeviceConfig ( mTargetConfig ) ; // save
2014-03-22 01:12:57 +00:00
if ( okCallback )
okCallback ( ) ;
delete this ;
2016-12-02 23:00:10 +00:00
} ;
buttons . push_back ( std : : make_shared < ButtonComponent > ( mWindow , " OK " , " ok " , [ this , okFunction ] {
// check if the hotkey enable button is set. if not prompt the user to use select or nothing.
Input input ;
if ( ! mTargetConfig - > getInputByName ( " HotKeyEnable " , & input ) ) {
mWindow - > pushGui ( new GuiMsgBox ( mWindow ,
" YOU DIDN'T CHOOSE A HOTKEY ENABLE BUTTON. THIS IS REQUIRED FOR EXITING GAMES WITH A CONTROLLER. DO YOU WANT TO USE THE SELECT BUTTON DEFAULT ? PLEASE ANSWER YES TO USE SELECT OR NO TO NOT SET A HOTKEY ENABLE BUTTON. " ,
" YES " , [ this , okFunction ] {
Input input ;
mTargetConfig - > getInputByName ( " Select " , & input ) ;
mTargetConfig - > mapInput ( " HotKeyEnable " , input ) ;
okFunction ( ) ;
} ,
" NO " , [ this , okFunction ] {
// for a disabled hotkey enable button, set to a key with id 0,
// so the input configuration script can be backwards compatible.
mTargetConfig - > mapInput ( " HotKeyEnable " , Input ( DEVICE_KEYBOARD , TYPE_KEY , 0 , 1 , true ) ) ;
okFunction ( ) ;
}
) ) ;
} else {
okFunction ( ) ;
}
2014-03-22 01:12:57 +00:00
} ) ) ;
mButtonGrid = makeButtonGrid ( mWindow , buttons ) ;
2014-03-23 00:48:48 +00:00
mGrid . setEntry ( mButtonGrid , Vector2i ( 0 , 6 ) , true , false ) ;
2014-03-22 01:12:57 +00:00
2014-03-23 00:48:48 +00:00
setSize ( Renderer : : getScreenWidth ( ) * 0.6f , Renderer : : getScreenHeight ( ) * 0.75f ) ;
2014-03-22 01:12:57 +00:00
setPosition ( ( Renderer : : getScreenWidth ( ) - mSize . x ( ) ) / 2 , ( Renderer : : getScreenHeight ( ) - mSize . y ( ) ) / 2 ) ;
}
2013-04-09 18:13:47 +00:00
2014-03-22 01:12:57 +00:00
void GuiInputConfig : : onSizeChanged ( )
{
mBackground . fitTo ( mSize , Vector3f : : Zero ( ) , Vector2f ( - 32 , - 32 ) ) ;
2013-04-13 23:10:23 +00:00
2014-03-22 01:12:57 +00:00
// update grid
mGrid . setSize ( mSize ) ;
2013-06-02 15:08:32 +00:00
2014-03-23 00:48:48 +00:00
//mGrid.setRowHeightPerc(0, 0.025f);
mGrid . setRowHeightPerc ( 1 , mTitle - > getFont ( ) - > getHeight ( ) * 0.75f / mSize . y ( ) ) ;
mGrid . setRowHeightPerc ( 2 , mSubtitle1 - > getFont ( ) - > getHeight ( ) / mSize . y ( ) ) ;
mGrid . setRowHeightPerc ( 3 , mSubtitle2 - > getFont ( ) - > getHeight ( ) / mSize . y ( ) ) ;
//mGrid.setRowHeightPerc(4, 0.03f);
mGrid . setRowHeightPerc ( 5 , ( mList - > getRowHeight ( 0 ) * 5 + 2 ) / mSize . y ( ) ) ;
mGrid . setRowHeightPerc ( 6 , mButtonGrid - > getSize ( ) . y ( ) / mSize . y ( ) ) ;
2015-04-19 09:57:10 +00:00
2016-08-10 01:48:04 +00:00
mBusyAnim . setSize ( mSize ) ;
2012-07-22 21:15:55 +00:00
}
2014-04-13 02:09:54 +00:00
void GuiInputConfig : : update ( int deltaTime )
{
if ( mConfiguringRow & & mHoldingInput & & inputSkippable [ mHeldInputId ] )
{
int prevSec = mHeldTime / 1000 ;
mHeldTime + = deltaTime ;
int curSec = mHeldTime / 1000 ;
if ( mHeldTime > = HOLD_TO_SKIP_MS )
{
setNotDefined ( mMappings . at ( mHeldInputId ) ) ;
clearAssignment ( mHeldInputId ) ;
mHoldingInput = false ;
rowDone ( ) ;
} else {
if ( prevSec ! = curSec )
{
// crossed the second boundary, update text
const auto & text = mMappings . at ( mHeldInputId ) ;
std : : stringstream ss ;
ss < < " HOLD FOR " < < HOLD_TO_SKIP_MS / 1000 - curSec < < " S TO SKIP " ;
text - > setText ( ss . str ( ) ) ;
text - > setColor ( 0x777777FF ) ;
}
}
}
}
// move cursor to the next thing if we're configuring all,
// or come out of "configure mode" if we were only configuring one row
void GuiInputConfig : : rowDone ( )
{
if ( mConfiguringAll )
{
if ( ! mList - > moveCursor ( 1 ) ) // try to move to the next one
{
// at bottom of list, done
mConfiguringAll = false ;
mConfiguringRow = false ;
mGrid . moveCursor ( Vector2i ( 0 , 1 ) ) ;
} else {
// on another one
setPress ( mMappings . at ( mList - > getCursorId ( ) ) ) ;
}
} else {
// only configuring one row, so stop
mConfiguringRow = false ;
}
}
2014-03-22 01:38:16 +00:00
void GuiInputConfig : : setPress ( const std : : shared_ptr < TextComponent > & text )
{
text - > setText ( " PRESS ANYTHING " ) ;
text - > setColor ( 0x656565FF ) ;
}
void GuiInputConfig : : setNotDefined ( const std : : shared_ptr < TextComponent > & text )
{
text - > setText ( " -NOT DEFINED- " ) ;
text - > setColor ( 0x999999FF ) ;
}
2014-04-13 02:09:54 +00:00
void GuiInputConfig : : setAssignedTo ( const std : : shared_ptr < TextComponent > & text , Input input )
{
text - > setText ( strToUpper ( input . string ( ) ) ) ;
text - > setColor ( 0x777777FF ) ;
}
2014-03-22 01:38:16 +00:00
void GuiInputConfig : : error ( const std : : shared_ptr < TextComponent > & text , const std : : string & msg )
2012-07-22 21:15:55 +00:00
{
2014-03-22 01:38:16 +00:00
text - > setText ( " ALREADY TAKEN " ) ;
text - > setColor ( 0x656565FF ) ;
2014-03-22 01:12:57 +00:00
}
2013-04-09 18:13:47 +00:00
2014-04-13 02:09:54 +00:00
bool GuiInputConfig : : assign ( Input input , int inputId )
2014-03-22 01:12:57 +00:00
{
2014-04-13 02:09:54 +00:00
// input is from InputConfig* mTargetConfig
2013-04-09 18:13:47 +00:00
2014-03-22 01:12:57 +00:00
// if this input is mapped to something other than "nothing" or the current row, error
// (if it's the same as what it was before, allow it)
2017-07-28 07:57:37 +00:00
if ( mTargetConfig - > getMappedTo ( input ) . size ( ) > 0 & & ! mTargetConfig - > isMappedTo ( inputName [ inputId ] , input ) & & strcmp ( inputName [ inputId ] , " HotKeyEnable " ) ! = 0 )
2013-04-09 18:13:47 +00:00
{
2014-04-13 02:09:54 +00:00
error ( mMappings . at ( inputId ) , " Already mapped! " ) ;
2014-03-22 01:12:57 +00:00
return false ;
2013-04-09 18:13:47 +00:00
}
2014-04-13 02:09:54 +00:00
setAssignedTo ( mMappings . at ( inputId ) , input ) ;
2014-03-22 01:12:57 +00:00
input . configured = true ;
2014-04-13 02:09:54 +00:00
mTargetConfig - > mapInput ( inputName [ inputId ] , input ) ;
2013-06-03 00:18:26 +00:00
2014-03-22 01:12:57 +00:00
LOG ( LogInfo ) < < " Mapping [ " < < input . string ( ) < < " ] -> " < < inputName [ inputId ] ;
2013-04-09 18:13:47 +00:00
2014-03-22 01:12:57 +00:00
return true ;
2012-07-22 21:15:55 +00:00
}
2014-04-13 02:09:54 +00:00
void GuiInputConfig : : clearAssignment ( int inputId )
{
mTargetConfig - > unmapInput ( inputName [ inputId ] ) ;
2016-07-02 05:14:41 +00:00
}