diff --git a/src/Font.cpp b/src/Font.cpp index ed972e99e..8269c3672 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -383,6 +383,7 @@ std::string Font::wrapText(std::string text, float xLen) const newline = word.find('\n', 0); if(newline != std::string::npos) { + //get everything up to the newline word = word.substr(0, newline); text.erase(0, newline + 1); }else{ @@ -404,8 +405,7 @@ std::string Font::wrapText(std::string text, float xLen) const if(textSize.x() > xLen || text.length() == 0 || newline != std::string::npos) { //output line now - if(textSize.x() > 0) //make sure it's not blank - out += line + '\n'; + out += line + '\n'; //move the word we skipped to the next line line = word; @@ -427,6 +427,48 @@ Eigen::Vector2f Font::sizeWrappedText(std::string text, float xLen) const return sizeText(text); } +Eigen::Vector2f Font::getWrappedTextCursorOffset(std::string text, float xLen, int cursor) const +{ + std::string wrappedText = wrapText(text, xLen); + + float lineWidth = 0.0f; + float y = 0.0f; + + unsigned int stop = (unsigned int)cursor; + unsigned int wrapOffset = 0; + for(unsigned int i = 0; i < stop; i++) + { + unsigned char wrappedLetter = wrappedText[i + wrapOffset]; + unsigned char letter = text[i]; + + if(wrappedLetter == '\n' && letter != '\n') + { + //this is where the wordwrap inserted a newline + //reset lineWidth and increment y, but don't consume a cursor character + lineWidth = 0.0f; + y += getHeight(); + + wrapOffset++; + i--; + continue; + } + + if(letter == '\n') + { + lineWidth = 0.0f; + y += getHeight(); + continue; + } + + if(letter < 32 || letter >= 128) + letter = 127; + + lineWidth += charData[letter].advX * fontScale; + } + + return Eigen::Vector2f(lineWidth, y); +} + //============================================================================================================= //TextCache //============================================================================================================= diff --git a/src/Font.h b/src/Font.h index 90002aa3d..a5e75d0ef 100644 --- a/src/Font.h +++ b/src/Font.h @@ -57,6 +57,7 @@ public: void drawWrappedText(std::string text, const Eigen::Vector2f& offset, float xLen, unsigned int color); Eigen::Vector2f sizeWrappedText(std::string text, float xLen) const; + Eigen::Vector2f getWrappedTextCursorOffset(std::string text, float xLen, int cursor) const; void drawCenteredText(std::string text, float xOffset, float y, unsigned int color); diff --git a/src/MetaData.cpp b/src/MetaData.cpp index 8fe267d7d..a9a5d02b2 100644 --- a/src/MetaData.cpp +++ b/src/MetaData.cpp @@ -107,8 +107,16 @@ GuiComponent* MetaDataList::makeEditor(Window* window, MetaDataType as) { switch(as) { + case MD_MULTILINE_STRING: + { + TextEditComponent* comp = new TextEditComponent(window); + comp->setSize(comp->getSize().x(), comp->getSize().y() * 3); + return comp; + } default: - TextEditComponent* comp = new TextEditComponent(window); - return comp; + { + TextEditComponent* comp = new TextEditComponent(window); + return comp; + } } } diff --git a/src/components/ButtonComponent.cpp b/src/components/ButtonComponent.cpp index 9d45ccd8c..555b25eb6 100644 --- a/src/components/ButtonComponent.cpp +++ b/src/components/ButtonComponent.cpp @@ -3,7 +3,9 @@ #include "../Window.h" ButtonComponent::ButtonComponent(Window* window) : GuiComponent(window), - mBox(window, ":/button.png") + mBox(window, ":/button.png"), + mFocused(false), + mTextColorFocused(0x000000FF), mTextColorUnfocused(0x333333FF), mTextPulseTime(0) { setSize(64, 48); } @@ -30,17 +32,28 @@ bool ButtonComponent::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } -void ButtonComponent::setText(const std::string& text, unsigned int color) +void ButtonComponent::setText(const std::string& text, unsigned int focusedColor, unsigned int unfocusedColor) { mText = text; + mTextColorFocused = focusedColor; + mTextColorUnfocused = unfocusedColor; std::shared_ptr f = getFont(); - mTextCache = std::unique_ptr(f->buildTextCache(mText, 0, 0, color)); - mOpacity = color & 0x000000FF; + mTextCache = std::unique_ptr(f->buildTextCache(mText, 0, 0, getCurTextColor())); setSize(mTextCache->metrics.size + Eigen::Vector2f(12, 12)); } +void ButtonComponent::onFocusGained() +{ + mFocused = true; +} + +void ButtonComponent::onFocusLost() +{ + mFocused = false; +} + void ButtonComponent::render(const Eigen::Affine3f& parentTrans) { Eigen::Affine3f trans = parentTrans * getTransform(); @@ -53,6 +66,7 @@ void ButtonComponent::render(const Eigen::Affine3f& parentTrans) trans = trans.translate(centerOffset); Renderer::setMatrix(trans); + mTextCache->setColor(getCurTextColor()); getFont()->renderTextCache(mTextCache.get()); trans = trans.translate(-centerOffset); } @@ -64,3 +78,11 @@ std::shared_ptr ButtonComponent::getFont() { return Font::get(*mWindow->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_SMALL); } + +unsigned int ButtonComponent::getCurTextColor() const +{ + if(!mFocused) + return mTextColorUnfocused; + else + return mTextColorFocused; +} diff --git a/src/components/ButtonComponent.h b/src/components/ButtonComponent.h index d7b4130c6..b0651b271 100644 --- a/src/components/ButtonComponent.h +++ b/src/components/ButtonComponent.h @@ -15,14 +15,23 @@ public: bool input(InputConfig* config, Input input) override; void render(const Eigen::Affine3f& parentTrans) override; - void setText(const std::string& text, unsigned int color); + void setText(const std::string& text, unsigned int focusedTextColor, unsigned int unfocusedTextColor = 0x555555FF); void onSizeChanged() override; + void onFocusGained() override; + void onFocusLost() override; private: std::shared_ptr getFont(); std::function mPressedFunc; + bool mFocused; + unsigned int mTextColorFocused; + unsigned int mTextColorUnfocused; + int mTextPulseTime; + + unsigned int getCurTextColor() const; + std::string mText; std::unique_ptr mTextCache; NinePatchComponent mBox; diff --git a/src/components/TextEditComponent.cpp b/src/components/TextEditComponent.cpp index 1efed94cd..dd31a833d 100644 --- a/src/components/TextEditComponent.cpp +++ b/src/components/TextEditComponent.cpp @@ -6,7 +6,8 @@ #include "ComponentListComponent.h" TextEditComponent::TextEditComponent(Window* window) : GuiComponent(window), - mBox(window, 0, 0, 0, 0), mFocused(false), mAllowResize(true) + mBox(window, 0, 0, 0, 0), mFocused(false), + mScrollOffset(0.0f), mCursor(0), mEditing(false) { addChild(&mBox); @@ -53,18 +54,79 @@ std::string TextEditComponent::getValue() const void TextEditComponent::textInput(const char* text) { - if(mFocused) + if(mEditing) { if(text[0] == '\b') { - if(mText.length() > 0) - mText.erase(mText.end() - 1, mText.end()); + if(mCursor > 0) + { + mText.erase(mText.begin() + mCursor - 1, mText.begin() + mCursor); + mCursor--; + } }else{ - mText += text; + mText.insert(mCursor, text); + mCursor++; } } onTextChanged(); + onCursorChanged(); +} + +bool TextEditComponent::input(InputConfig* config, Input input) +{ + if(input.value == 0) + return false; + + if(config->isMappedTo("a", input) && mFocused && !mEditing) + { + mEditing = true; + return true; + } + + if(mEditing) + { + if(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RETURN) + { + textInput("\n"); + return true; + } + + if(config->isMappedTo("b", input)) + { + mEditing = false; + return true; + } + + if(config->isMappedTo("up", input)) + { + + }else if(config->isMappedTo("down", input)) + { + + }else if(config->isMappedTo("left", 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() - 1) + mCursor = mText.length() - 1; + + onCursorChanged(); + } + + //consume all input when editing text + return true; + } + + return false; } void TextEditComponent::onTextChanged() @@ -73,25 +135,20 @@ void TextEditComponent::onTextChanged() std::string wrappedText = f->wrapText(mText, mSize.x()); mTextCache = std::unique_ptr(f->buildTextCache(wrappedText, 0, 0, 0x00000000 | getOpacity())); - - if(mAllowResize) - { - float y = f->sizeText(wrappedText).y(); - if(y == 0) - y = (float)f->getHeight(); - - setSize(mSize.x(), y); - } - - ComponentListComponent* cmp = dynamic_cast(getParent()); - if(cmp) - cmp->updateComponent(this); } -void TextEditComponent::setAllowResize(bool allow) +void TextEditComponent::onCursorChanged() { - mAllowResize = allow; - onTextChanged(); + std::shared_ptr font = getFont(); + Eigen::Vector2f textSize = font->getWrappedTextCursorOffset(mText, mSize.x(), mCursor); //font->sizeWrappedText(mText.substr(0, mCursor), mSize.x()); + + if(mScrollOffset + mSize.y() < textSize.y() + font->getHeight()) //need to scroll down? + { + mScrollOffset = textSize.y() - mSize.y() + font->getHeight(); + }else if(mScrollOffset > textSize.y()) //need to scroll up? + { + mScrollOffset = textSize.y(); + } } void TextEditComponent::render(const Eigen::Affine3f& parentTrans) @@ -99,13 +156,29 @@ void TextEditComponent::render(const Eigen::Affine3f& parentTrans) Eigen::Affine3f trans = getTransform() * parentTrans; renderChildren(trans); + Eigen::Vector2i clipPos((int)trans.translation().x(), (int)trans.translation().y()); + Eigen::Vector3f dimScaled = trans * Eigen::Vector3f(mSize.x(), mSize.y(), 0); + Eigen::Vector2i clipDim((int)dimScaled.x() - trans.translation().x(), (int)dimScaled.y() - trans.translation().y()); + Renderer::pushClipRect(clipPos, clipDim); + + trans.translate(Eigen::Vector3f(0, -mScrollOffset, 0)); + Renderer::setMatrix(trans); - + + std::shared_ptr f = getFont(); if(mTextCache != NULL) { - std::shared_ptr f = getFont(); f->renderTextCache(mTextCache.get()); } + + //draw cursor + if(mEditing) + { + Eigen::Vector2f cursorPos = f->getWrappedTextCursorOffset(mText, mSize.x(), mCursor); + Renderer::drawRect(cursorPos.x(), cursorPos.y(), 3, f->getHeight(), 0x000000FF); + } + + Renderer::popClipRect(); } std::shared_ptr TextEditComponent::getFont() diff --git a/src/components/TextEditComponent.h b/src/components/TextEditComponent.h index dd35f4cd2..0c00bd4c9 100644 --- a/src/components/TextEditComponent.h +++ b/src/components/TextEditComponent.h @@ -12,6 +12,7 @@ public: TextEditComponent(Window* window); void textInput(const char* text) override; + bool input(InputConfig* config, Input input) override; void render(const Eigen::Affine3f& parentTrans) override; void onFocusGained() override; @@ -22,14 +23,16 @@ public: void setValue(const std::string& val) override; std::string getValue() const override; - void setAllowResize(bool allow); //Allow automatic resizing of height to accomodate more text. - private: void onTextChanged(); + void onCursorChanged(); std::string mText; bool mFocused; - bool mAllowResize; + + bool mEditing; + float mScrollOffset; + int mCursor; std::shared_ptr getFont();