From b88e99b9bf05ebef5bad70111e7a225b61341832 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sat, 12 Apr 2014 21:09:54 -0500 Subject: [PATCH] GuiInputConfig now supports hold-to-skip for certain inputs. InputManager now properly sends Backspace keydown input events. InputConfig now supports unmapping particular inputs by name. --- src/InputConfig.cpp | 7 ++ src/InputConfig.h | 1 + src/InputManager.cpp | 2 - src/guis/GuiInputConfig.cpp | 156 +++++++++++++++++++++++++++--------- src/guis/GuiInputConfig.h | 21 ++++- 5 files changed, 142 insertions(+), 45 deletions(-) diff --git a/src/InputConfig.cpp b/src/InputConfig.cpp index 47d976ad9..1c6035fe3 100644 --- a/src/InputConfig.cpp +++ b/src/InputConfig.cpp @@ -68,6 +68,13 @@ void InputConfig::mapInput(const std::string& name, Input input) mNameMap[toLower(name)] = input; } +void InputConfig::unmapInput(const std::string& name) +{ + auto it = mNameMap.find(toLower(name)); + if(it != mNameMap.end()) + mNameMap.erase(it); +} + Input InputConfig::getInputByName(const std::string& name) { return mNameMap[toLower(name)]; diff --git a/src/InputConfig.h b/src/InputConfig.h index e26844acd..e498edba5 100644 --- a/src/InputConfig.h +++ b/src/InputConfig.h @@ -87,6 +87,7 @@ public: void clear(); void mapInput(const std::string& name, Input input); + void unmapInput(const std::string& name); // unmap all Inputs mapped to this name inline int getDeviceId() const { return mDeviceId; }; inline const std::string& getDeviceName() { return mDeviceName; } diff --git a/src/InputManager.cpp b/src/InputManager.cpp index 462275397..de676e988 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -194,8 +194,6 @@ bool InputManager::parseEvent(const SDL_Event& ev) { if(mWindow->peekGui() != NULL) mWindow->peekGui()->textInput("\b"); - - return true; } if(ev.key.repeat) diff --git a/src/guis/GuiInputConfig.cpp b/src/guis/GuiInputConfig.cpp index a96868f91..656e78c27 100644 --- a/src/guis/GuiInputConfig.cpp +++ b/src/guis/GuiInputConfig.cpp @@ -8,8 +8,9 @@ #include "../Util.h" static const int inputCount = 10; -static const char* inputName[inputCount] = { "Up", "Down", "Left", "Right", "A", "B", "Start", "Select", "PageUp", "PageDown"}; -static const char* inputDispName[inputCount] = { "UP", "DOWN", "LEFT", "RIGHT", "A", "B", "START", "SELECT", "PAGE UP", "PAGE DOWN"}; +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" }; @@ -19,9 +20,11 @@ static const char* inputIcon[inputCount] = { ":/help/dpad_up.svg", ":/help/dpad_ using namespace Eigen; +#define HOLD_TO_SKIP_MS 5000 + GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, const std::function& okCallback) : GuiComponent(window), mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 7)), - mTargetConfig(target) + mTargetConfig(target), mHoldingInput(false) { LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " (" << target->getDeviceName() << ")."; @@ -48,7 +51,7 @@ GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfi mSubtitle1 = std::make_shared(mWindow, strToUpper(ss.str()), Font::get(FONT_SIZE_MEDIUM), 0x555555FF, TextComponent::ALIGN_CENTER); mGrid.setEntry(mSubtitle1, Vector2i(0, 2), false, true); - mSubtitle2 = std::make_shared(mWindow, "HOLD ANY BUTTON TO SKIP", Font::get(FONT_SIZE_SMALL), 0x999999FF, TextComponent::ALIGN_CENTER); + mSubtitle2 = std::make_shared(mWindow, "HOLD ANY BUTTON TO SKIP", Font::get(FONT_SIZE_SMALL), 0x99999900, TextComponent::ALIGN_CENTER); mGrid.setEntry(mSubtitle2, Vector2i(0, 3), false, true); // 4 is a spacer row @@ -80,46 +83,63 @@ GuiInputConfig::GuiInputConfig(Window* window, InputConfig* target, bool reconfi row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool { - if(!input.value) + // ignore input not from our target device + if(config != mTargetConfig) return false; - if(mConfiguringRow) + // if we're not configuring, start configuring when A is pressed + if(!mConfiguringRow) { - if(!process(config, input, i, mapping)) // button press invalid; try again - return true; - if(mConfiguringAll) - { - if(!mList->moveCursor(1)) // try to move to the next one - { - // at bottom of list - mConfiguringAll = false; - mConfiguringRow = false; - mGrid.moveCursor(Vector2i(0, 1)); - }else{ - // on another one - setPress(mMappings.at(mList->getCursorId())); - } - }else{ - mConfiguringRow = false; // we only wanted to configure one row - } - return true; - }else{ - // not configuring, start configuring when A is pressed if(config->isMappedTo("a", input) && input.value) { mConfiguringRow = true; setPress(mapping); return true; } + + // we're not configuring and they didn't press A to start, so ignore this return false; } - - return false; + + // 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; + + if(assign(input, i)) + rowDone(); // if successful, move cursor/stop configuring - if not, we'll just try again + + return true; + } }; + mList->addRow(row); } - // make the first one say "NOT DEFINED" if we're re-configuring everything + // 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 if(mConfiguringAll) setPress(mMappings.front()); @@ -154,6 +174,56 @@ void GuiInputConfig::onSizeChanged() mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y() / mSize.y()); } +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; + } +} + void GuiInputConfig::setPress(const std::shared_ptr& text) { text->setText("PRESS ANYTHING"); @@ -166,33 +236,41 @@ void GuiInputConfig::setNotDefined(const std::shared_ptr& text) text->setColor(0x999999FF); } +void GuiInputConfig::setAssignedTo(const std::shared_ptr& text, Input input) +{ + text->setText(strToUpper(input.string())); + text->setColor(0x777777FF); +} + void GuiInputConfig::error(const std::shared_ptr& text, const std::string& msg) { text->setText("ALREADY TAKEN"); text->setColor(0x656565FF); } -bool GuiInputConfig::process(InputConfig* config, Input input, int inputId, const std::shared_ptr& text) +bool GuiInputConfig::assign(Input input, int inputId) { - // from some other input source - if(config != mTargetConfig) - return false; + // input is from InputConfig* mTargetConfig // 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) - if(config->getMappedTo(input).size() > 0 && !config->isMappedTo(inputName[inputId], input)) + if(mTargetConfig->getMappedTo(input).size() > 0 && !mTargetConfig->isMappedTo(inputName[inputId], input)) { - error(text, "Already mapped!"); + error(mMappings.at(inputId), "Already mapped!"); return false; } - text->setText(strToUpper(input.string())); - text->setColor(0x777777FF); - + setAssignedTo(mMappings.at(inputId), input); + input.configured = true; - config->mapInput(inputName[inputId], input); + mTargetConfig->mapInput(inputName[inputId], input); LOG(LogInfo) << " Mapping [" << input.string() << "] -> " << inputName[inputId]; return true; } + +void GuiInputConfig::clearAssignment(int inputId) +{ + mTargetConfig->unmapInput(inputName[inputId]); +} \ No newline at end of file diff --git a/src/guis/GuiInputConfig.h b/src/guis/GuiInputConfig.h index e5c28a376..490d69f47 100644 --- a/src/guis/GuiInputConfig.h +++ b/src/guis/GuiInputConfig.h @@ -12,13 +12,21 @@ class GuiInputConfig : public GuiComponent public: GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll, const std::function& okCallback); + void update(int deltaTime) override; + void onSizeChanged() override; private: - void error(const std::shared_ptr& text, const std::string& msg); - void setPress(const std::shared_ptr& text); - void setNotDefined(const std::shared_ptr& text); - bool process(InputConfig* config, Input input, int inputId, const std::shared_ptr& text); + void error(const std::shared_ptr& text, const std::string& msg); // set text to "msg" + not greyed out + + void setPress(const std::shared_ptr& text); // set text to "PRESS ANYTHING" + not greyed out + void setNotDefined(const std::shared_ptr& text); // set text to -NOT DEFINED- + greyed out + void setAssignedTo(const std::shared_ptr& text, Input input); // set text to "BUTTON 2"/"AXIS 2+", etc. + + bool assign(Input input, int inputId); + void clearAssignment(int inputId); + + void rowDone(); NinePatchComponent mBackground; ComponentGrid mGrid; @@ -33,4 +41,9 @@ private: InputConfig* mTargetConfig; bool mConfiguringRow; // next input captured by mList will be interpretted as a remap bool mConfiguringAll; // move the cursor down after configuring a row and start configuring the next row until we reach the bottom + + bool mHoldingInput; + Input mHeldInput; + int mHeldTime; + int mHeldInputId; };