mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-02-06 07:15:39 +00:00
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
This commit is contained in:
parent
a82ef25886
commit
ada4f83089
|
@ -376,7 +376,7 @@ void ComponentGrid::render(const Eigen::Affine3f& parentTrans)
|
||||||
void ComponentGrid::textInput(const char* text)
|
void ComponentGrid::textInput(const char* text)
|
||||||
{
|
{
|
||||||
GridEntry* selectedEntry = getCellAt(mCursor);
|
GridEntry* selectedEntry = getCellAt(mCursor);
|
||||||
if(selectedEntry != NULL)
|
if(selectedEntry != NULL && selectedEntry->canFocus)
|
||||||
selectedEntry->component->textInput(text);
|
selectedEntry->component->textInput(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,15 @@ void ComponentList::onCursorChanged(const CursorState& state)
|
||||||
mCameraOffset = 0;
|
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();
|
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<HelpPrompt> ComponentList::getHelpPrompts()
|
std::vector<HelpPrompt> ComponentList::getHelpPrompts()
|
||||||
{
|
{
|
||||||
if(!size())
|
if(!size())
|
||||||
|
|
|
@ -47,6 +47,7 @@ public:
|
||||||
|
|
||||||
void addRow(const ComponentListRow& row, bool setCursorHere = false);
|
void addRow(const ComponentListRow& row, bool setCursorHere = false);
|
||||||
|
|
||||||
|
void textInput(const char* text) override;
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
void render(const Eigen::Affine3f& parentTrans) override;
|
void render(const Eigen::Affine3f& parentTrans) override;
|
||||||
|
|
|
@ -3,39 +3,39 @@
|
||||||
#include "../resources/Font.h"
|
#include "../resources/Font.h"
|
||||||
#include "../Window.h"
|
#include "../Window.h"
|
||||||
#include "../Renderer.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),
|
TextEditComponent::TextEditComponent(Window* window) : GuiComponent(window),
|
||||||
mBox(window, ":/textbox.png"), mFocused(false),
|
mBox(window, ":/button.png"), mFocused(false),
|
||||||
mScrollOffset(0.0f, 0.0f), mCursor(0), mEditing(false)
|
mScrollOffset(0.0f, 0.0f), mCursor(0), mEditing(false), mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT))
|
||||||
{
|
{
|
||||||
addChild(&mBox);
|
addChild(&mBox);
|
||||||
|
|
||||||
onFocusLost();
|
onFocusLost();
|
||||||
|
|
||||||
setSize(256, (float)getFont()->getHeight());
|
setSize(256, mFont->getHeight() + TEXT_PADDING_VERT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditComponent::onFocusGained()
|
void TextEditComponent::onFocusGained()
|
||||||
{
|
{
|
||||||
mBox.setImagePath(":/textbox_glow.png");
|
|
||||||
mBox.setEdgeColor(0x51CCFF00 | getOpacity());
|
|
||||||
|
|
||||||
SDL_StartTextInput();
|
|
||||||
mFocused = true;
|
mFocused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditComponent::onFocusLost()
|
void TextEditComponent::onFocusLost()
|
||||||
{
|
{
|
||||||
mBox.setImagePath(":/textbox.png");
|
|
||||||
mBox.setEdgeColor(0xFFFFFF00 | getOpacity());
|
|
||||||
|
|
||||||
SDL_StopTextInput();
|
|
||||||
mFocused = false;
|
mFocused = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEditComponent::onSizeChanged()
|
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)
|
void TextEditComponent::setValue(const std::string& val)
|
||||||
|
@ -53,6 +53,7 @@ void TextEditComponent::textInput(const char* text)
|
||||||
{
|
{
|
||||||
if(mEditing)
|
if(mEditing)
|
||||||
{
|
{
|
||||||
|
mCursorRepeatDir = 0;
|
||||||
if(text[0] == '\b')
|
if(text[0] == '\b')
|
||||||
{
|
{
|
||||||
if(mCursor > 0)
|
if(mCursor > 0)
|
||||||
|
@ -70,15 +71,33 @@ void TextEditComponent::textInput(const char* text)
|
||||||
onCursorChanged();
|
onCursorChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextEditComponent::startEditing()
|
||||||
|
{
|
||||||
|
SDL_StartTextInput();
|
||||||
|
mEditing = true;
|
||||||
|
updateHelpPrompts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextEditComponent::stopEditing()
|
||||||
|
{
|
||||||
|
SDL_StopTextInput();
|
||||||
|
mEditing = false;
|
||||||
|
updateHelpPrompts();
|
||||||
|
}
|
||||||
|
|
||||||
bool TextEditComponent::input(InputConfig* config, Input input)
|
bool TextEditComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if(input.value == 0)
|
if(input.value == 0)
|
||||||
|
{
|
||||||
|
if(config->isMappedTo("left", input) || config->isMappedTo("right", input))
|
||||||
|
mCursorRepeatDir = 0;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(config->isMappedTo("a", input) && mFocused && !mEditing)
|
if(config->isMappedTo("a", input) && mFocused && !mEditing)
|
||||||
{
|
{
|
||||||
mEditing = true;
|
startEditing();
|
||||||
updateHelpPrompts();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +109,7 @@ bool TextEditComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
textInput("\n");
|
textInput("\n");
|
||||||
}else{
|
}else{
|
||||||
mEditing = false;
|
stopEditing();
|
||||||
updateHelpPrompts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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)))
|
if((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) || (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedTo("b", input)))
|
||||||
{
|
{
|
||||||
mEditing = false;
|
stopEditing();
|
||||||
updateHelpPrompts();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->isMappedTo("up", input))
|
if(config->isMappedTo("up", input))
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
}else if(config->isMappedTo("down", input))
|
}else if(config->isMappedTo("down", input))
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
}else if(config->isMappedTo("left", input))
|
}else if(config->isMappedTo("left", input) || config->isMappedTo("right", input))
|
||||||
{
|
{
|
||||||
mCursor--;
|
mCursorRepeatDir = config->isMappedTo("left", input) ? -1 : 1;
|
||||||
if(mCursor < 0)
|
mCursorRepeatTimer = -(CURSOR_REPEAT_START_DELAY - CURSOR_REPEAT_SPEED);
|
||||||
mCursor = 0;
|
moveCursor(mCursorRepeatDir);
|
||||||
|
|
||||||
onCursorChanged();
|
|
||||||
}else if(config->isMappedTo("right", input))
|
|
||||||
{
|
|
||||||
mCursor++;
|
|
||||||
if(mText.length() == 0)
|
|
||||||
mCursor = 0;
|
|
||||||
if(mCursor >= (int)mText.length())
|
|
||||||
mCursor = mText.length();
|
|
||||||
|
|
||||||
onCursorChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//consume all input when editing text
|
//consume all input when editing text
|
||||||
|
@ -135,12 +141,41 @@ bool TextEditComponent::input(InputConfig* config, Input input)
|
||||||
return false;
|
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()
|
void TextEditComponent::onTextChanged()
|
||||||
{
|
{
|
||||||
std::shared_ptr<Font> f = getFont();
|
std::string wrappedText = (isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x()) : mText);
|
||||||
|
mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity()));
|
||||||
std::string wrappedText = (isMultiline() ? f->wrapText(mText, mSize.x()) : mText);
|
|
||||||
mTextCache = std::unique_ptr<TextCache>(f->buildTextCache(wrappedText, 0, 0, 0x00000000 | getOpacity()));
|
|
||||||
|
|
||||||
if(mCursor > (int)mText.length())
|
if(mCursor > (int)mText.length())
|
||||||
mCursor = mText.length();
|
mCursor = mText.length();
|
||||||
|
@ -148,25 +183,23 @@ void TextEditComponent::onTextChanged()
|
||||||
|
|
||||||
void TextEditComponent::onCursorChanged()
|
void TextEditComponent::onCursorChanged()
|
||||||
{
|
{
|
||||||
std::shared_ptr<Font> font = getFont();
|
|
||||||
|
|
||||||
if(isMultiline())
|
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?
|
}else if(mScrollOffset.y() > textSize.y()) //need to scroll up?
|
||||||
{
|
{
|
||||||
mScrollOffset[1] = textSize.y();
|
mScrollOffset[1] = textSize.y();
|
||||||
}
|
}
|
||||||
}else{
|
}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())
|
}else if(mScrollOffset.x() > cursorPos.x())
|
||||||
{
|
{
|
||||||
mScrollOffset[0] = cursorPos.x();
|
mScrollOffset[0] = cursorPos.x();
|
||||||
|
@ -179,52 +212,58 @@ void TextEditComponent::render(const Eigen::Affine3f& parentTrans)
|
||||||
Eigen::Affine3f trans = getTransform() * parentTrans;
|
Eigen::Affine3f trans = getTransform() * parentTrans;
|
||||||
renderChildren(trans);
|
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::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());
|
Eigen::Vector2i clipDim((int)dimScaled.x() - trans.translation().x(), (int)dimScaled.y() - trans.translation().y());
|
||||||
Renderer::pushClipRect(clipPos, clipDim);
|
Renderer::pushClipRect(clipPos, clipDim);
|
||||||
|
|
||||||
trans.translate(Eigen::Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0));
|
trans.translate(Eigen::Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0));
|
||||||
|
trans = roundMatrix(trans);
|
||||||
|
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
std::shared_ptr<Font> f = getFont();
|
if(mTextCache)
|
||||||
if(mTextCache != NULL)
|
|
||||||
{
|
{
|
||||||
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)
|
if(mEditing)
|
||||||
{
|
{
|
||||||
Eigen::Vector2f cursorPos;
|
Eigen::Vector2f cursorPos;
|
||||||
if(isMultiline())
|
if(isMultiline())
|
||||||
{
|
{
|
||||||
cursorPos = f->getWrappedTextCursorOffset(mText, mSize.x(), mCursor);
|
cursorPos = mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor);
|
||||||
}else{
|
}else{
|
||||||
cursorPos = f->sizeText(mText.substr(0, mCursor));
|
cursorPos = mFont->sizeText(mText.substr(0, mCursor));
|
||||||
cursorPos[1] = 0;
|
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<Font> TextEditComponent::getFont()
|
|
||||||
{
|
|
||||||
return Font::get(FONT_SIZE_SMALL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextEditComponent::isMultiline()
|
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<HelpPrompt> TextEditComponent::getHelpPrompts()
|
std::vector<HelpPrompt> TextEditComponent::getHelpPrompts()
|
||||||
|
|
|
@ -14,6 +14,7 @@ public:
|
||||||
|
|
||||||
void textInput(const char* text) override;
|
void textInput(const char* text) override;
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
|
void update(int deltaTime) override;
|
||||||
void render(const Eigen::Affine3f& parentTrans) override;
|
void render(const Eigen::Affine3f& parentTrans) override;
|
||||||
|
|
||||||
void onFocusGained() override;
|
void onFocusGained() override;
|
||||||
|
@ -24,26 +25,36 @@ 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;
|
||||||
|
|
||||||
bool isEditing() const;
|
inline bool isEditing() const { return mEditing; };
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void startEditing();
|
||||||
|
void stopEditing();
|
||||||
|
|
||||||
void onTextChanged();
|
void onTextChanged();
|
||||||
void onCursorChanged();
|
void onCursorChanged();
|
||||||
|
|
||||||
|
void updateCursorRepeat(int deltaTime);
|
||||||
|
void moveCursor(int amt);
|
||||||
|
|
||||||
bool isMultiline();
|
bool isMultiline();
|
||||||
|
Eigen::Vector2f getTextAreaPos() const;
|
||||||
|
Eigen::Vector2f getTextAreaSize() const;
|
||||||
|
|
||||||
std::string mText;
|
std::string mText;
|
||||||
bool mFocused;
|
bool mFocused;
|
||||||
|
|
||||||
bool mEditing;
|
bool mEditing;
|
||||||
Eigen::Vector2f mScrollOffset;
|
int mCursor; // cursor position in characters
|
||||||
int mCursor;
|
|
||||||
|
|
||||||
std::shared_ptr<Font> getFont();
|
int mCursorRepeatTimer;
|
||||||
|
int mCursorRepeatDir;
|
||||||
|
|
||||||
|
Eigen::Vector2f mScrollOffset;
|
||||||
|
|
||||||
NinePatchComponent mBox;
|
NinePatchComponent mBox;
|
||||||
|
|
||||||
|
std::shared_ptr<Font> mFont;
|
||||||
std::unique_ptr<TextCache> mTextCache;
|
std::unique_ptr<TextCache> mTextCache;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue