TextEditComponent is now fixed-height and supports cursor-based editing.

Fixed a text-wrapping bug with consecutive newlines in Font::wrapText.
This commit is contained in:
Aloshi 2013-09-07 17:43:36 -05:00
parent 044619a2d3
commit f9571b9389
7 changed files with 193 additions and 35 deletions

View file

@ -383,6 +383,7 @@ std::string Font::wrapText(std::string text, float xLen) const
newline = word.find('\n', 0); newline = word.find('\n', 0);
if(newline != std::string::npos) if(newline != std::string::npos)
{ {
//get everything up to the newline
word = word.substr(0, newline); word = word.substr(0, newline);
text.erase(0, newline + 1); text.erase(0, newline + 1);
}else{ }else{
@ -404,7 +405,6 @@ std::string Font::wrapText(std::string text, float xLen) const
if(textSize.x() > xLen || text.length() == 0 || newline != std::string::npos) if(textSize.x() > xLen || text.length() == 0 || newline != std::string::npos)
{ {
//output line now //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 //move the word we skipped to the next line
@ -427,6 +427,48 @@ Eigen::Vector2f Font::sizeWrappedText(std::string text, float xLen) const
return sizeText(text); 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 //TextCache
//============================================================================================================= //=============================================================================================================

View file

@ -57,6 +57,7 @@ public:
void drawWrappedText(std::string text, const Eigen::Vector2f& offset, float xLen, unsigned int color); 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 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); void drawCenteredText(std::string text, float xOffset, float y, unsigned int color);

View file

@ -107,8 +107,16 @@ GuiComponent* MetaDataList::makeEditor(Window* window, MetaDataType as)
{ {
switch(as) switch(as)
{ {
case MD_MULTILINE_STRING:
{
TextEditComponent* comp = new TextEditComponent(window);
comp->setSize(comp->getSize().x(), comp->getSize().y() * 3);
return comp;
}
default: default:
{
TextEditComponent* comp = new TextEditComponent(window); TextEditComponent* comp = new TextEditComponent(window);
return comp; return comp;
} }
}
} }

View file

@ -3,7 +3,9 @@
#include "../Window.h" #include "../Window.h"
ButtonComponent::ButtonComponent(Window* window) : GuiComponent(window), 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); setSize(64, 48);
} }
@ -30,17 +32,28 @@ bool ButtonComponent::input(InputConfig* config, Input input)
return GuiComponent::input(config, 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; mText = text;
mTextColorFocused = focusedColor;
mTextColorUnfocused = unfocusedColor;
std::shared_ptr<Font> f = getFont(); std::shared_ptr<Font> f = getFont();
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(mText, 0, 0, color)); mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(mText, 0, 0, getCurTextColor()));
mOpacity = color & 0x000000FF;
setSize(mTextCache->metrics.size + Eigen::Vector2f(12, 12)); 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) void ButtonComponent::render(const Eigen::Affine3f& parentTrans)
{ {
Eigen::Affine3f trans = parentTrans * getTransform(); Eigen::Affine3f trans = parentTrans * getTransform();
@ -53,6 +66,7 @@ void ButtonComponent::render(const Eigen::Affine3f& parentTrans)
trans = trans.translate(centerOffset); trans = trans.translate(centerOffset);
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
mTextCache->setColor(getCurTextColor());
getFont()->renderTextCache(mTextCache.get()); getFont()->renderTextCache(mTextCache.get());
trans = trans.translate(-centerOffset); trans = trans.translate(-centerOffset);
} }
@ -64,3 +78,11 @@ std::shared_ptr<Font> ButtonComponent::getFont()
{ {
return Font::get(*mWindow->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_SMALL); return Font::get(*mWindow->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_SMALL);
} }
unsigned int ButtonComponent::getCurTextColor() const
{
if(!mFocused)
return mTextColorUnfocused;
else
return mTextColorFocused;
}

View file

@ -15,14 +15,23 @@ public:
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
void render(const Eigen::Affine3f& parentTrans) 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 onSizeChanged() override;
void onFocusGained() override;
void onFocusLost() override;
private: private:
std::shared_ptr<Font> getFont(); std::shared_ptr<Font> getFont();
std::function<void()> mPressedFunc; std::function<void()> mPressedFunc;
bool mFocused;
unsigned int mTextColorFocused;
unsigned int mTextColorUnfocused;
int mTextPulseTime;
unsigned int getCurTextColor() const;
std::string mText; std::string mText;
std::unique_ptr<TextCache> mTextCache; std::unique_ptr<TextCache> mTextCache;
NinePatchComponent mBox; NinePatchComponent mBox;

View file

@ -6,7 +6,8 @@
#include "ComponentListComponent.h" #include "ComponentListComponent.h"
TextEditComponent::TextEditComponent(Window* window) : GuiComponent(window), 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); addChild(&mBox);
@ -53,18 +54,79 @@ std::string TextEditComponent::getValue() const
void TextEditComponent::textInput(const char* text) void TextEditComponent::textInput(const char* text)
{ {
if(mFocused) if(mEditing)
{ {
if(text[0] == '\b') if(text[0] == '\b')
{ {
if(mText.length() > 0) if(mCursor > 0)
mText.erase(mText.end() - 1, mText.end()); {
mText.erase(mText.begin() + mCursor - 1, mText.begin() + mCursor);
mCursor--;
}
}else{ }else{
mText += text; mText.insert(mCursor, text);
mCursor++;
} }
} }
onTextChanged(); 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() void TextEditComponent::onTextChanged()
@ -73,25 +135,20 @@ void TextEditComponent::onTextChanged()
std::string wrappedText = f->wrapText(mText, mSize.x()); std::string wrappedText = f->wrapText(mText, mSize.x());
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(wrappedText, 0, 0, 0x00000000 | getOpacity())); mTextCache = std::unique_ptr<TextCache>(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<ComponentListComponent*>(getParent());
if(cmp)
cmp->updateComponent(this);
} }
void TextEditComponent::setAllowResize(bool allow) void TextEditComponent::onCursorChanged()
{ {
mAllowResize = allow; std::shared_ptr<Font> font = getFont();
onTextChanged(); 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) void TextEditComponent::render(const Eigen::Affine3f& parentTrans)
@ -99,13 +156,29 @@ void TextEditComponent::render(const Eigen::Affine3f& parentTrans)
Eigen::Affine3f trans = getTransform() * parentTrans; Eigen::Affine3f trans = getTransform() * parentTrans;
renderChildren(trans); 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); Renderer::setMatrix(trans);
std::shared_ptr<Font> f = getFont();
if(mTextCache != NULL) if(mTextCache != NULL)
{ {
std::shared_ptr<Font> f = getFont();
f->renderTextCache(mTextCache.get()); 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<Font> TextEditComponent::getFont() std::shared_ptr<Font> TextEditComponent::getFont()

View file

@ -12,6 +12,7 @@ public:
TextEditComponent(Window* window); TextEditComponent(Window* window);
void textInput(const char* text) override; void textInput(const char* text) override;
bool input(InputConfig* config, Input input) override;
void render(const Eigen::Affine3f& parentTrans) override; void render(const Eigen::Affine3f& parentTrans) override;
void onFocusGained() override; void onFocusGained() override;
@ -22,14 +23,16 @@ public:
void setValue(const std::string& val) override; void setValue(const std::string& val) override;
std::string getValue() const override; std::string getValue() const override;
void setAllowResize(bool allow); //Allow automatic resizing of height to accomodate more text.
private: private:
void onTextChanged(); void onTextChanged();
void onCursorChanged();
std::string mText; std::string mText;
bool mFocused; bool mFocused;
bool mAllowResize;
bool mEditing;
float mScrollOffset;
int mCursor;
std::shared_ptr<Font> getFont(); std::shared_ptr<Font> getFont();