mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 23:55:38 +00:00
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:
parent
044619a2d3
commit
f9571b9389
44
src/Font.cpp
44
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,7 +405,6 @@ 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';
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
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
|
||||
//=============================================================================================================
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Font> f = getFont();
|
||||
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(mText, 0, 0, color));
|
||||
mOpacity = color & 0x000000FF;
|
||||
mTextCache = std::unique_ptr<TextCache>(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<Font> ButtonComponent::getFont()
|
|||
{
|
||||
return Font::get(*mWindow->getResourceManager(), Font::getDefaultPath(), FONT_SIZE_SMALL);
|
||||
}
|
||||
|
||||
unsigned int ButtonComponent::getCurTextColor() const
|
||||
{
|
||||
if(!mFocused)
|
||||
return mTextColorUnfocused;
|
||||
else
|
||||
return mTextColorFocused;
|
||||
}
|
||||
|
|
|
@ -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<Font> getFont();
|
||||
std::function<void()> mPressedFunc;
|
||||
|
||||
bool mFocused;
|
||||
unsigned int mTextColorFocused;
|
||||
unsigned int mTextColorUnfocused;
|
||||
int mTextPulseTime;
|
||||
|
||||
unsigned int getCurTextColor() const;
|
||||
|
||||
std::string mText;
|
||||
std::unique_ptr<TextCache> mTextCache;
|
||||
NinePatchComponent mBox;
|
||||
|
|
|
@ -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<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;
|
||||
onTextChanged();
|
||||
std::shared_ptr<Font> 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<Font> f = getFont();
|
||||
if(mTextCache != NULL)
|
||||
{
|
||||
std::shared_ptr<Font> 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<Font> TextEditComponent::getFont()
|
||||
|
|
|
@ -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<Font> getFont();
|
||||
|
||||
|
|
Loading…
Reference in a new issue