From ada4f83089140bb68b4498ba3c239db86764cfb8 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Thu, 20 Mar 2014 21:47:45 -0500 Subject: [PATCH] Fixed ComponentList not passing focus to entries. Some improvements to TextEditComponent: - cursor now repeats if you hold left/right - use FONT_PATH_LIGHT instead of regular to match UI concepts - added some horizontal and vertical padding to the text area - cursor is a little thinner and not as freakishly tall --- src/components/ComponentGrid.cpp | 2 +- src/components/ComponentList.cpp | 17 +++ src/components/ComponentList.h | 1 + src/components/TextEditComponent.cpp | 169 ++++++++++++++++----------- src/components/TextEditComponent.h | 21 +++- 5 files changed, 139 insertions(+), 71 deletions(-) diff --git a/src/components/ComponentGrid.cpp b/src/components/ComponentGrid.cpp index e73b45629..d51f4e654 100644 --- a/src/components/ComponentGrid.cpp +++ b/src/components/ComponentGrid.cpp @@ -376,7 +376,7 @@ void ComponentGrid::render(const Eigen::Affine3f& parentTrans) void ComponentGrid::textInput(const char* text) { GridEntry* selectedEntry = getCellAt(mCursor); - if(selectedEntry != NULL) + if(selectedEntry != NULL && selectedEntry->canFocus) selectedEntry->component->textInput(text); } diff --git a/src/components/ComponentList.cpp b/src/components/ComponentList.cpp index c6085ea0a..e0068f5b0 100644 --- a/src/components/ComponentList.cpp +++ b/src/components/ComponentList.cpp @@ -121,6 +121,15 @@ void ComponentList::onCursorChanged(const CursorState& state) mCameraOffset = 0; } + // this is terribly inefficient but we don't know what we came from so... + if(size()) + { + for(auto it = mEntries.begin(); it != mEntries.end(); it++) + it->data.elements.back().component->onFocusLost(); + + mEntries.at(mCursor).data.elements.back().component->onFocusGained(); + } + updateHelpPrompts(); } @@ -244,6 +253,14 @@ void ComponentList::updateElementSize(const ComponentListRow& row) } } +void ComponentList::textInput(const char* text) +{ + if(!size()) + return; + + mEntries.at(mCursor).data.elements.back().component->textInput(text); +} + std::vector ComponentList::getHelpPrompts() { if(!size()) diff --git a/src/components/ComponentList.h b/src/components/ComponentList.h index baf736e10..c7596bb51 100644 --- a/src/components/ComponentList.h +++ b/src/components/ComponentList.h @@ -47,6 +47,7 @@ public: void addRow(const ComponentListRow& row, bool setCursorHere = false); + void textInput(const char* text) override; bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; void render(const Eigen::Affine3f& parentTrans) override; diff --git a/src/components/TextEditComponent.cpp b/src/components/TextEditComponent.cpp index ed85aef23..49a0b6308 100644 --- a/src/components/TextEditComponent.cpp +++ b/src/components/TextEditComponent.cpp @@ -3,39 +3,39 @@ #include "../resources/Font.h" #include "../Window.h" #include "../Renderer.h" +#include "../Util.h" + +#define TEXT_PADDING_HORIZ 10 +#define TEXT_PADDING_VERT 2 + +#define CURSOR_REPEAT_START_DELAY 500 +#define CURSOR_REPEAT_SPEED 28 // lower is faster TextEditComponent::TextEditComponent(Window* window) : GuiComponent(window), - mBox(window, ":/textbox.png"), mFocused(false), - mScrollOffset(0.0f, 0.0f), mCursor(0), mEditing(false) + mBox(window, ":/button.png"), mFocused(false), + mScrollOffset(0.0f, 0.0f), mCursor(0), mEditing(false), mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)) { addChild(&mBox); onFocusLost(); - setSize(256, (float)getFont()->getHeight()); + setSize(256, mFont->getHeight() + TEXT_PADDING_VERT); } void TextEditComponent::onFocusGained() { - mBox.setImagePath(":/textbox_glow.png"); - mBox.setEdgeColor(0x51CCFF00 | getOpacity()); - - SDL_StartTextInput(); mFocused = true; } void TextEditComponent::onFocusLost() { - mBox.setImagePath(":/textbox.png"); - mBox.setEdgeColor(0xFFFFFF00 | getOpacity()); - - SDL_StopTextInput(); mFocused = false; } void TextEditComponent::onSizeChanged() { - mBox.fitTo(getSize()); + mBox.fitTo(mSize, Eigen::Vector3f::Zero(), Eigen::Vector2f(-34, -32 - TEXT_PADDING_VERT)); + onTextChanged(); // wrap point probably changed } void TextEditComponent::setValue(const std::string& val) @@ -53,6 +53,7 @@ void TextEditComponent::textInput(const char* text) { if(mEditing) { + mCursorRepeatDir = 0; if(text[0] == '\b') { if(mCursor > 0) @@ -70,15 +71,33 @@ void TextEditComponent::textInput(const char* text) onCursorChanged(); } +void TextEditComponent::startEditing() +{ + SDL_StartTextInput(); + mEditing = true; + updateHelpPrompts(); +} + +void TextEditComponent::stopEditing() +{ + SDL_StopTextInput(); + mEditing = false; + updateHelpPrompts(); +} + bool TextEditComponent::input(InputConfig* config, Input input) { if(input.value == 0) + { + if(config->isMappedTo("left", input) || config->isMappedTo("right", input)) + mCursorRepeatDir = 0; + return false; + } if(config->isMappedTo("a", input) && mFocused && !mEditing) { - mEditing = true; - updateHelpPrompts(); + startEditing(); return true; } @@ -90,8 +109,7 @@ bool TextEditComponent::input(InputConfig* config, Input input) { textInput("\n"); }else{ - mEditing = false; - updateHelpPrompts(); + stopEditing(); } return true; @@ -99,33 +117,21 @@ bool TextEditComponent::input(InputConfig* config, Input input) if((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) || (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedTo("b", input))) { - mEditing = false; - updateHelpPrompts(); + stopEditing(); return true; } if(config->isMappedTo("up", input)) { - + // TODO }else if(config->isMappedTo("down", input)) { - - }else if(config->isMappedTo("left", input)) + // TODO + }else if(config->isMappedTo("left", input) || config->isMappedTo("right", input)) { - mCursor--; - if(mCursor < 0) - mCursor = 0; - - onCursorChanged(); - }else if(config->isMappedTo("right", input)) - { - mCursor++; - if(mText.length() == 0) - mCursor = 0; - if(mCursor >= (int)mText.length()) - mCursor = mText.length(); - - onCursorChanged(); + mCursorRepeatDir = config->isMappedTo("left", input) ? -1 : 1; + mCursorRepeatTimer = -(CURSOR_REPEAT_START_DELAY - CURSOR_REPEAT_SPEED); + moveCursor(mCursorRepeatDir); } //consume all input when editing text @@ -135,12 +141,41 @@ bool TextEditComponent::input(InputConfig* config, Input input) return false; } +void TextEditComponent::update(int deltaTime) +{ + updateCursorRepeat(deltaTime); + GuiComponent::update(deltaTime); +} + +void TextEditComponent::updateCursorRepeat(int deltaTime) +{ + if(mCursorRepeatDir == 0) + return; + + mCursorRepeatTimer += deltaTime; + while(mCursorRepeatTimer >= CURSOR_REPEAT_SPEED) + { + moveCursor(mCursorRepeatDir); + mCursorRepeatTimer -= CURSOR_REPEAT_SPEED; + } +} + +void TextEditComponent::moveCursor(int amt) +{ + mCursor += amt; + + if(mCursor < 0) + mCursor = 0; + if(mCursor >= (int)mText.length()) + mCursor = mText.length(); + + onCursorChanged(); +} + void TextEditComponent::onTextChanged() { - std::shared_ptr f = getFont(); - - std::string wrappedText = (isMultiline() ? f->wrapText(mText, mSize.x()) : mText); - mTextCache = std::unique_ptr(f->buildTextCache(wrappedText, 0, 0, 0x00000000 | getOpacity())); + std::string wrappedText = (isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x()) : mText); + mTextCache = std::unique_ptr(mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity())); if(mCursor > (int)mText.length()) mCursor = mText.length(); @@ -148,25 +183,23 @@ void TextEditComponent::onTextChanged() void TextEditComponent::onCursorChanged() { - std::shared_ptr font = getFont(); - if(isMultiline()) { - Eigen::Vector2f textSize = font->getWrappedTextCursorOffset(mText, mSize.x(), mCursor); + Eigen::Vector2f textSize = mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor); - if(mScrollOffset.y() + mSize.y() < textSize.y() + font->getHeight()) //need to scroll down? + if(mScrollOffset.y() + getTextAreaSize().y() < textSize.y() + mFont->getHeight()) //need to scroll down? { - mScrollOffset[1] = textSize.y() - mSize.y() + font->getHeight(); + mScrollOffset[1] = textSize.y() - getTextAreaSize().y() + mFont->getHeight(); }else if(mScrollOffset.y() > textSize.y()) //need to scroll up? { mScrollOffset[1] = textSize.y(); } }else{ - Eigen::Vector2f cursorPos = font->sizeText(mText.substr(0, mCursor)); + Eigen::Vector2f cursorPos = mFont->sizeText(mText.substr(0, mCursor)); - if(mScrollOffset.x() + mSize.x() < cursorPos.x()) + if(mScrollOffset.x() + getTextAreaSize().x() < cursorPos.x()) { - mScrollOffset[0] = cursorPos.x() - mSize.x(); + mScrollOffset[0] = cursorPos.x() - getTextAreaSize().x(); }else if(mScrollOffset.x() > cursorPos.x()) { mScrollOffset[0] = cursorPos.x(); @@ -179,52 +212,58 @@ void TextEditComponent::render(const Eigen::Affine3f& parentTrans) Eigen::Affine3f trans = getTransform() * parentTrans; renderChildren(trans); + // text + cursor rendering + // offset into our "text area" (padding) + trans.translation() += Eigen::Vector3f(getTextAreaPos().x(), getTextAreaPos().y(), 0); + Eigen::Vector2i clipPos((int)trans.translation().x(), (int)trans.translation().y()); - Eigen::Vector3f dimScaled = trans * Eigen::Vector3f(mSize.x(), mSize.y(), 0); + Eigen::Vector3f dimScaled = trans * Eigen::Vector3f(getTextAreaSize().x(), getTextAreaSize().y(), 0); // use "text area" size for clipping Eigen::Vector2i clipDim((int)dimScaled.x() - trans.translation().x(), (int)dimScaled.y() - trans.translation().y()); Renderer::pushClipRect(clipPos, clipDim); trans.translate(Eigen::Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0)); + trans = roundMatrix(trans); Renderer::setMatrix(trans); - std::shared_ptr f = getFont(); - if(mTextCache != NULL) + if(mTextCache) { - f->renderTextCache(mTextCache.get()); + mFont->renderTextCache(mTextCache.get()); } - //draw cursor + // pop the clip early to allow the cursor to be drawn outside of the "text area" + Renderer::popClipRect(); + + // draw cursor if(mEditing) { Eigen::Vector2f cursorPos; if(isMultiline()) { - cursorPos = f->getWrappedTextCursorOffset(mText, mSize.x(), mCursor); + cursorPos = mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor); }else{ - cursorPos = f->sizeText(mText.substr(0, mCursor)); + cursorPos = mFont->sizeText(mText.substr(0, mCursor)); cursorPos[1] = 0; } - Renderer::drawRect((int)cursorPos.x(), (int)cursorPos.y(), 3, (int)f->getHeight(), 0x000000FF); + float cursorHeight = mFont->getHeight() * 0.8f; + Renderer::drawRect(cursorPos.x(), cursorPos.y() + (mFont->getHeight() - cursorHeight) / 2, 2.0f, cursorHeight, 0x000000FF); } - - Renderer::popClipRect(); -} - -std::shared_ptr TextEditComponent::getFont() -{ - return Font::get(FONT_SIZE_SMALL); } bool TextEditComponent::isMultiline() { - return (getSize().y() > (float)getFont()->getHeight()); + return (getSize().y() > mFont->getHeight() * 1.25f); } -bool TextEditComponent::isEditing() const +Eigen::Vector2f TextEditComponent::getTextAreaPos() const { - return mEditing; + return Eigen::Vector2f(TEXT_PADDING_HORIZ / 2.0f, TEXT_PADDING_VERT / 2.0f); +} + +Eigen::Vector2f TextEditComponent::getTextAreaSize() const +{ + return Eigen::Vector2f(mSize.x() - TEXT_PADDING_HORIZ, mSize.y() - TEXT_PADDING_VERT); } std::vector TextEditComponent::getHelpPrompts() diff --git a/src/components/TextEditComponent.h b/src/components/TextEditComponent.h index f1875dfba..62c24688f 100644 --- a/src/components/TextEditComponent.h +++ b/src/components/TextEditComponent.h @@ -14,6 +14,7 @@ public: void textInput(const char* text) override; bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; void render(const Eigen::Affine3f& parentTrans) override; void onFocusGained() override; @@ -24,26 +25,36 @@ public: void setValue(const std::string& val) override; std::string getValue() const override; - bool isEditing() const; + inline bool isEditing() const { return mEditing; }; virtual std::vector getHelpPrompts() override; private: + void startEditing(); + void stopEditing(); + void onTextChanged(); void onCursorChanged(); + void updateCursorRepeat(int deltaTime); + void moveCursor(int amt); + bool isMultiline(); + Eigen::Vector2f getTextAreaPos() const; + Eigen::Vector2f getTextAreaSize() const; std::string mText; bool mFocused; - bool mEditing; - Eigen::Vector2f mScrollOffset; - int mCursor; + int mCursor; // cursor position in characters - std::shared_ptr getFont(); + int mCursorRepeatTimer; + int mCursorRepeatDir; + + Eigen::Vector2f mScrollOffset; NinePatchComponent mBox; + std::shared_ptr mFont; std::unique_ptr mTextCache; };