2014-06-20 01:30:09 +00:00
|
|
|
#include "guis/GuiInputConfig.h"
|
|
|
|
#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
|
|
|
|
2013-06-19 21:07:12 +00:00
|
|
|
static const int inputCount = 10;
|
2014-04-13 02:09:54 +00:00
|
|
|
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" };
|
2014-03-22 18:04:14 +00:00
|
|
|
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" };
|
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
|
|
|
|
2014-03-22 01:12:57 +00:00
|
|
|
using namespace Eigen;
|
|
|
|
|
2014-04-13 02:09:54 +00:00
|
|
|
#define HOLD_TO_SKIP_MS 5000
|
|
|
|
|
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)),
|
2014-04-13 02:09:54 +00:00
|
|
|
mTargetConfig(target), mHoldingInput(false)
|
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;
|
|
|
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "OK", "ok", [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;
|
|
|
|
}));
|
|
|
|
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());
|
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)
|
2014-04-13 02:09:54 +00:00
|
|
|
if(mTargetConfig->getMappedTo(input).size() > 0 && !mTargetConfig->isMappedTo(inputName[inputId], input))
|
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]);
|
|
|
|
}
|