mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-29 17:45: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);
|
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
|
||||||
//=============================================================================================================
|
//=============================================================================================================
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue