Removed direct use of Font::wrapText() from OptionListComponent, TextEditComponent and TextListComponent

This commit is contained in:
Leon Styhre 2024-08-12 20:51:52 +02:00
parent 0723ae8364
commit 1d3b2f8066
10 changed files with 85 additions and 52 deletions

View file

@ -352,12 +352,10 @@ private:
// Display the selected entry and left/right option arrows. // Display the selected entry and left/right option arrows.
for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) { for (auto it = mEntries.cbegin(); it != mEntries.cend(); ++it) {
if (it->selected) { if (it->selected) {
if (it->maxNameLength > 0.0f && if (it->maxNameLength > 0.0f) {
Font::get(FONT_SIZE_MEDIUM)->sizeText(it->name).x > it->maxNameLength) { // A maximum length parameter is passed to make sure the text is
// A maximum length parameter has been passed and the "name" size surpasses // abbreviated if it doesn't fit.
// this value, so abbreviate the string inside the arrows. mText.setText(it->name, true, it->maxNameLength);
auto font = Font::get(FONT_SIZE_MEDIUM);
mText.setText(font->wrapText(it->name, it->maxNameLength));
} }
else { else {
mText.setText(it->name); mText.setText(it->name);

View file

@ -46,6 +46,7 @@ TextComponent::TextComponent()
, mScrollOffset1 {0.0f} , mScrollOffset1 {0.0f}
, mScrollOffset2 {0.0f} , mScrollOffset2 {0.0f}
, mScrollTime {0.0f} , mScrollTime {0.0f}
, mMaxLength {0.0f}
{ {
} }
@ -62,7 +63,8 @@ TextComponent::TextComponent(const std::string& text,
bool horizontalScrolling, bool horizontalScrolling,
float scrollSpeedMultiplier, float scrollSpeedMultiplier,
float scrollDelay, float scrollDelay,
float scrollGap) float scrollGap,
float maxLength)
: mFont {nullptr} : mFont {nullptr}
, mRenderer {Renderer::getInstance()} , mRenderer {Renderer::getInstance()}
, mColor {0x000000FF} , mColor {0x000000FF}
@ -94,14 +96,18 @@ TextComponent::TextComponent(const std::string& text,
, mScrollOffset1 {0.0f} , mScrollOffset1 {0.0f}
, mScrollOffset2 {0.0f} , mScrollOffset2 {0.0f}
, mScrollTime {0.0f} , mScrollTime {0.0f}
, mMaxLength {maxLength}
{ {
setFont(font); setFont(font);
setColor(color); setColor(color);
setBackgroundColor(bgcolor); setBackgroundColor(bgcolor);
setHorizontalScrolling(mHorizontalScrolling); setHorizontalScrolling(mHorizontalScrolling);
setText(text, false); setText(text, false, mMaxLength);
setPosition(pos); setPosition(pos);
if (mMaxLength == 0.0f)
setSize(size); setSize(size);
else
setSize(glm::vec2 {mMaxLength, size.y});
} }
void TextComponent::onSizeChanged() void TextComponent::onSizeChanged()
@ -172,12 +178,13 @@ void TextComponent::setDimming(float dimming)
mTextCache->setDimming(dimming); mTextCache->setDimming(dimming);
} }
void TextComponent::setText(const std::string& text, bool update) void TextComponent::setText(const std::string& text, bool update, float maxLength)
{ {
if (mText == text) if (mText == text && mMaxLength == maxLength)
return; return;
mText = text; mText = text;
mMaxLength = maxLength;
if (update) if (update)
onTextChanged(); onTextChanged();
@ -475,7 +482,9 @@ void TextComponent::onTextChanged()
if (mFont && mAutoCalcExtent.x) { if (mFont && mAutoCalcExtent.x) {
mSize = mFont->sizeText(text, mLineSpacing); mSize = mFont->sizeText(text, mLineSpacing);
if (mSize.x == 0.0f) if (mMaxLength > 0.0f && mSize.x > mMaxLength)
mSize.x = std::round(mMaxLength);
else if (mSize.x == 0.0f)
return; return;
} }
@ -486,7 +495,8 @@ void TextComponent::onTextChanged()
const float lineHeight {mFont->getHeight(mLineSpacing)}; const float lineHeight {mFont->getHeight(mLineSpacing)};
const bool isScrollable {mParent && mParent->isScrollable()}; const bool isScrollable {mParent && mParent->isScrollable()};
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y * mRelativeScale > lineHeight}; // Add one extra pixel to lineHeight as the font may be fractional in size.
const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y * mRelativeScale > lineHeight + 1};
float offsetY {0.0f}; float offsetY {0.0f};
if (mHorizontalScrolling) { if (mHorizontalScrolling) {

View file

@ -35,14 +35,15 @@ public:
bool horizontalScrolling = false, bool horizontalScrolling = false,
float scrollSpeedMultiplier = 1.0f, float scrollSpeedMultiplier = 1.0f,
float scrollDelay = 1500.0f, float scrollDelay = 1500.0f,
float scrollGap = 1.5f); float scrollGap = 1.5f,
float maxLength = 0.0f);
void setFont(const std::shared_ptr<Font>& font); void setFont(const std::shared_ptr<Font>& font);
void setUppercase(bool uppercase); void setUppercase(bool uppercase);
void setLowercase(bool lowercase); void setLowercase(bool lowercase);
void setCapitalize(bool capitalize); void setCapitalize(bool capitalize);
void onSizeChanged() override; void onSizeChanged() override;
void setText(const std::string& text, bool update = true); void setText(const std::string& text, bool update = true, float maxLength = 0.0f);
void setHiddenText(const std::string& text) { mHiddenText = text; } void setHiddenText(const std::string& text) { mHiddenText = text; }
void setColor(unsigned int color) override; void setColor(unsigned int color) override;
void setHorizontalAlignment(Alignment align); void setHorizontalAlignment(Alignment align);
@ -188,6 +189,7 @@ private:
float mScrollOffset1; float mScrollOffset1;
float mScrollOffset2; float mScrollOffset2;
float mScrollTime; float mScrollTime;
float mMaxLength;
}; };
#endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H #endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H

View file

@ -28,6 +28,7 @@ TextEditComponent::TextEditComponent()
, mFocused {false} , mFocused {false}
, mEditing {false} , mEditing {false}
, mMaskInput {true} , mMaskInput {true}
, mMultiLine {false}
, mCursor {0} , mCursor {0}
, mBlinkTime {0} , mBlinkTime {0}
, mCursorRepeatDir {0} , mCursorRepeatDir {0}
@ -76,10 +77,10 @@ void TextEditComponent::onSizeChanged()
onTextChanged(); // Wrap point probably changed. onTextChanged(); // Wrap point probably changed.
} }
void TextEditComponent::setValue(const std::string& val) void TextEditComponent::setValue(const std::string& val, bool multiLine, bool update)
{ {
mText = val; mText = val;
mTextOrig = val; mMultiLine = multiLine;
onTextChanged(); onTextChanged();
onCursorChanged(); onCursorChanged();
} }
@ -92,7 +93,7 @@ void TextEditComponent::textInput(const std::string& text, const bool pasting)
#endif #endif
// Allow pasting up to a reasonable max clipboard size. // Allow pasting up to a reasonable max clipboard size.
if (pasting && text.length() > (isMultiline() ? 16384 : 300)) if (pasting && text.length() > (mMultiLine ? 16384 : 300))
return; return;
if (mEditing) { if (mEditing) {
@ -106,12 +107,10 @@ void TextEditComponent::textInput(const std::string& text, const bool pasting)
} }
} }
else { else {
mText.insert( mText.insert(mCursor,
mCursor, (pasting && !mMultiLine ? Utils::String::replace(text, "\n", " ") : text));
(pasting && !isMultiline() ? Utils::String::replace(text, "\n", " ") : text));
mCursor += static_cast<unsigned int>( mCursor += static_cast<unsigned int>(
(pasting && !isMultiline() ? Utils::String::replace(text, "\n", " ") : text) (pasting && !mMultiLine ? Utils::String::replace(text, "\n", " ") : text).size());
.size());
} }
} }
@ -191,7 +190,7 @@ bool TextEditComponent::input(InputConfig* config, Input input)
if (config->getDeviceId() == DEVICE_KEYBOARD) { if (config->getDeviceId() == DEVICE_KEYBOARD) {
// Special handling for keyboard input as the "A" and "B" buttons are overridden. // Special handling for keyboard input as the "A" and "B" buttons are overridden.
if (input.id == SDLK_RETURN || input.id == SDLK_KP_ENTER) { if (input.id == SDLK_RETURN || input.id == SDLK_KP_ENTER) {
if (isMultiline()) { if (mMultiLine) {
const bool maskValue {mMaskInput}; const bool maskValue {mMaskInput};
mMaskInput = false; mMaskInput = false;
textInput("\n"); textInput("\n");
@ -311,11 +310,11 @@ void TextEditComponent::setCursor(size_t pos)
void TextEditComponent::onTextChanged() void TextEditComponent::onTextChanged()
{ {
mWrappedText = if (mMultiLine)
(isMultiline() ? getFont()->wrapText(mText, getTextAreaSize().x, 0.0f, 1.5f, true) : mText); mEditText->setText(mText, true, mSize.x);
mEditText->setText(mWrappedText); else
// Setting the Y size to zero makes the text area expand vertically as needed. mEditText->setText(mText);
mEditText->setSize(mEditText->getSize().x, 0.0f);
mEditText->setColor(mMenuColorKeyboardText | static_cast<unsigned char>(mOpacity * 255.0f)); mEditText->setColor(mMenuColorKeyboardText | static_cast<unsigned char>(mOpacity * 255.0f));
if (mCursor > static_cast<int>(mText.length())) if (mCursor > static_cast<int>(mText.length()))
@ -324,8 +323,8 @@ void TextEditComponent::onTextChanged()
void TextEditComponent::onCursorChanged() void TextEditComponent::onCursorChanged()
{ {
if (isMultiline()) { if (mMultiLine) {
mCursorPos = getFont()->getWrappedTextCursorOffset(mWrappedText, mCursor); mCursorPos = getFont()->getWrappedTextCursorOffset(mText, mCursor);
// Need to scroll down? // Need to scroll down?
if (mScrollOffset.y + getTextAreaSize().y < mCursorPos.y + getFont()->getHeight()) if (mScrollOffset.y + getTextAreaSize().y < mCursorPos.y + getFont()->getHeight())

View file

@ -29,7 +29,7 @@ public:
void onSizeChanged() override; void onSizeChanged() override;
void setValue(const std::string& val) override; void setValue(const std::string& val, bool multiLine, bool update = true);
std::string getValue() const override; std::string getValue() const override;
void startEditing(); void startEditing();
@ -50,17 +50,15 @@ private:
void updateCursorRepeat(int deltaTime); void updateCursorRepeat(int deltaTime);
void moveCursor(int amt); void moveCursor(int amt);
bool isMultiline() { return (getSize().y > getFont()->getHeight() * 1.25f); }
glm::vec2 getTextAreaPos() const; glm::vec2 getTextAreaPos() const;
glm::vec2 getTextAreaSize() const; glm::vec2 getTextAreaSize() const;
Renderer* mRenderer; Renderer* mRenderer;
std::string mText; std::string mText;
std::string mWrappedText;
std::string mTextOrig;
bool mFocused; bool mFocused;
bool mEditing; bool mEditing;
bool mMaskInput; bool mMaskInput;
bool mMultiLine;
int mCursor; // Cursor position in characters. int mCursor; // Cursor position in characters.
int mBlinkTime; int mBlinkTime;

View file

@ -201,11 +201,17 @@ TextListComponent<T>::TextListComponent()
template <typename T> template <typename T>
void TextListComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme) void TextListComponent<T>::addEntry(Entry& entry, const std::shared_ptr<ThemeData>& theme)
{ {
if (mHorizontalScrolling) {
entry.data.entryName = std::make_shared<TextComponent>( entry.data.entryName = std::make_shared<TextComponent>(
mHorizontalScrolling ? entry.name : entry.name, mFont, 0x000000FF, ALIGN_LEFT, ALIGN_CENTER, glm::vec3 {0.0f, 0.0f, 0.0f},
mFont->wrapText(entry.name, mSize.x - mHorizontalMargin * 2.0f),
mFont, 0x000000FF, ALIGN_LEFT, ALIGN_CENTER, glm::vec3 {0.0f, 0.0f, 0.0f},
glm::vec2 {mFont->sizeText(entry.name).x, mFont->getSize() * 1.5f}); glm::vec2 {mFont->sizeText(entry.name).x, mFont->getSize() * 1.5f});
}
else {
entry.data.entryName = std::make_shared<TextComponent>(
entry.name, mFont, 0x000000FF, ALIGN_LEFT, ALIGN_CENTER, glm::vec3 {0.0f, 0.0f, 0.0f},
glm::vec2 {mFont->sizeText(entry.name).x, mFont->getSize() * 1.5f}, 0x00000000, 1.5f,
1.0f, false, 1.0f, 1500.0f, 1.5f, mSize.x - (mHorizontalMargin * 2.0f));
}
if (mHorizontalScrolling) { if (mHorizontalScrolling) {
glm::vec2 textSize {entry.data.entryName->getSize()}; glm::vec2 textSize {entry.data.entryName->getSize()};

View file

@ -135,7 +135,7 @@ GuiTextEditKeyboardPopup::GuiTextEditKeyboardPopup(
glm::ivec2 {mHorizontalKeyCount, static_cast<int>(kbLayout.size()) / 3}); glm::ivec2 {mHorizontalKeyCount, static_cast<int>(kbLayout.size()) / 3});
mText = std::make_shared<TextEditComponent>(); mText = std::make_shared<TextEditComponent>();
mText->setValue(initValue); mText->setValue(initValue, mMultiLine, false);
// Header. // Header.
mGrid.setEntry(mTitle, glm::ivec2 {0, 0}, false, true); mGrid.setEntry(mTitle, glm::ivec2 {0, 0}, false, true);
@ -685,12 +685,12 @@ std::shared_ptr<ButtonComponent> GuiTextEditKeyboardPopup::makeButton(
return; return;
} }
else if (key == _("LOAD")) { else if (key == _("LOAD")) {
mText->setValue(mDefaultValue->getValue()); mText->setValue(mDefaultValue->getValue(), mMultiLine);
mText->setCursor(mDefaultValue->getValue().size()); mText->setCursor(mDefaultValue->getValue().size());
return; return;
} }
else if (key == _("CLEAR")) { else if (key == _("CLEAR")) {
mText->setValue(""); mText->setValue("", mMultiLine);
return; return;
} }
else if (key == _("CANCEL")) { else if (key == _("CANCEL")) {

View file

@ -56,7 +56,7 @@ GuiTextEditPopup::GuiTextEditPopup(const HelpStyle& helpstyle,
} }
mText = std::make_shared<TextEditComponent>(); mText = std::make_shared<TextEditComponent>();
mText->setValue(initValue); mText->setValue(initValue, mMultiLine, false);
std::vector<std::shared_ptr<ButtonComponent>> buttons; std::vector<std::shared_ptr<ButtonComponent>> buttons;
buttons.push_back( buttons.push_back(
@ -67,14 +67,14 @@ GuiTextEditPopup::GuiTextEditPopup(const HelpStyle& helpstyle,
if (mComplexMode) { if (mComplexMode) {
buttons.push_back( buttons.push_back(
std::make_shared<ButtonComponent>(_("LOAD"), loadBtnHelpText, [this, defaultValue] { std::make_shared<ButtonComponent>(_("LOAD"), loadBtnHelpText, [this, defaultValue] {
mText->setValue(defaultValue); mText->setValue(defaultValue, mMultiLine);
mText->setCursor(0); mText->setCursor(0);
mText->setCursor(defaultValue.size()); mText->setCursor(defaultValue.size());
})); }));
} }
buttons.push_back(std::make_shared<ButtonComponent>(_("CLEAR"), clearBtnHelpText, buttons.push_back(std::make_shared<ButtonComponent>(
[this] { mText->setValue(""); })); _("CLEAR"), clearBtnHelpText, [this] { mText->setValue("", mMultiLine); }));
buttons.push_back(std::make_shared<ButtonComponent>(_("CANCEL"), _("discard changes"), buttons.push_back(std::make_shared<ButtonComponent>(_("CANCEL"), _("discard changes"),
[this] { delete this; })); [this] { delete this; }));

View file

@ -22,6 +22,9 @@ Font::Font(float size, const std::string& path)
, mFontSize {size} , mFontSize {size}
, mLetterHeight {0.0f} , mLetterHeight {0.0f}
, mMaxGlyphHeight {static_cast<int>(std::round(size))} , mMaxGlyphHeight {static_cast<int>(std::round(size))}
, mWrapMaxLength {0.0f}
, mWrapMaxHeight {0.0f}
, mWrapLineSpacing {1.5f}
{ {
if (mFontSize < 3.0f) { if (mFontSize < 3.0f) {
mFontSize = 3.0f; mFontSize = 3.0f;
@ -195,6 +198,14 @@ std::string Font::wrapText(const std::string& text,
bool addEllipsis {false}; bool addEllipsis {false};
float totalWidth {0.0f}; float totalWidth {0.0f};
mWrapMaxLength = maxLength;
mWrapMaxHeight = maxHeight;
mWrapLineSpacing = lineSpacing;
// TODO: Fix this rounding issue properly elsewhere.
if (mWrapMaxHeight < 1.0f)
mWrapMaxHeight = 0.0f;
std::vector<ShapeSegment> segmentsHB; std::vector<ShapeSegment> segmentsHB;
shapeText(text, segmentsHB); shapeText(text, segmentsHB);
@ -230,7 +241,7 @@ std::string Font::wrapText(const std::string& text,
break; break;
} }
accumHeight += lineHeight; accumHeight += lineHeight;
if (maxHeight != 0.0f && accumHeight > maxHeight) { if (mWrapMaxHeight != 0.0f && accumHeight > mWrapMaxHeight) {
addEllipsis = true; addEllipsis = true;
break; break;
} }
@ -272,7 +283,7 @@ std::string Font::wrapText(const std::string& text,
break; break;
} }
else { else {
if (maxHeight == 0.0f || accumHeight < maxHeight) { if (mWrapMaxHeight == 0.0f || accumHeight < mWrapMaxHeight) {
// New row. // New row.
float spaceOffset {0.0f}; float spaceOffset {0.0f};
if (lastSpace == wrappedText.size()) { if (lastSpace == wrappedText.size()) {
@ -336,7 +347,7 @@ std::string Font::wrapText(const std::string& text,
return wrappedText; return wrappedText;
} }
glm::vec2 Font::getWrappedTextCursorOffset(const std::string& wrappedText, glm::vec2 Font::getWrappedTextCursorOffset(const std::string& text,
const size_t stop, const size_t stop,
const float lineSpacing) const float lineSpacing)
{ {
@ -344,6 +355,9 @@ glm::vec2 Font::getWrappedTextCursorOffset(const std::string& wrappedText,
float yPos {0.0f}; float yPos {0.0f};
size_t cursor {0}; size_t cursor {0};
const std::string wrappedText {
wrapText(text, mWrapMaxLength, mWrapMaxHeight, mWrapLineSpacing, true)};
// TODO: Enable this code when shaped text is properly wrapped in wrapText(). // TODO: Enable this code when shaped text is properly wrapped in wrapText().
// std::vector<ShapeSegment> segmentsHB; // std::vector<ShapeSegment> segmentsHB;
// shapeText(wrappedText, segmentsHB); // shapeText(wrappedText, segmentsHB);
@ -753,9 +767,12 @@ void Font::shapeText(const std::string& text, std::vector<ShapeSegment>& segment
segment.length = static_cast<unsigned int>(textCursor - lastFlushPos); segment.length = static_cast<unsigned int>(textCursor - lastFlushPos);
segment.fontHB = (lastFont == nullptr ? currGlyph->fontHB : lastFont); segment.fontHB = (lastFont == nullptr ? currGlyph->fontHB : lastFont);
segment.doShape = shapeSegment; segment.doShape = shapeSegment;
#if !defined(NDEBUG)
segment.substring = text.substr(lastFlushPos, textCursor - lastFlushPos);
#else
if (!shapeSegment) if (!shapeSegment)
segment.substring = text.substr(lastFlushPos, textCursor - lastFlushPos); segment.substring = text.substr(lastFlushPos, textCursor - lastFlushPos);
#endif
segmentsHB.emplace_back(std::move(segment)); segmentsHB.emplace_back(std::move(segment));
lastFlushPos = textCursor; lastFlushPos = textCursor;

View file

@ -95,7 +95,7 @@ public:
const bool multiLine = false); const bool multiLine = false);
// Returns the position of the cursor after moving it to the stop position. // Returns the position of the cursor after moving it to the stop position.
glm::vec2 getWrappedTextCursorOffset(const std::string& wrappedText, glm::vec2 getWrappedTextCursorOffset(const std::string& text,
const size_t stop, const size_t stop,
const float lineSpacing = 1.5f); const float lineSpacing = 1.5f);
@ -248,6 +248,9 @@ private:
float mFontSize; float mFontSize;
float mLetterHeight; float mLetterHeight;
int mMaxGlyphHeight; int mMaxGlyphHeight;
float mWrapMaxLength;
float mWrapMaxHeight;
float mWrapLineSpacing;
}; };
// Caching of shaped and rendered text. // Caching of shaped and rendered text.