Added a simple text area UI node (#277)

This commit is contained in:
SMJS 2023-10-01 23:59:44 +02:00 committed by GitHub
parent 275a87aece
commit 7f277a77e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 288 additions and 0 deletions

View file

@ -0,0 +1,60 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <cocos2d.h>
namespace geode {
/**
* A class which provides a textarea with proper alignment and some extra features like:
*
* - Max lines
* - Changing all aspects after creation
* - Custom text alignment
* - Automatic line wrapping and cutoff
* - Line padding
*
* Contact me on Discord (\@smjs) if you have any questions, suggestions or bugs.
*/
class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
cocos2d::CCMenu* m_container;
std::string m_font;
std::string m_text;
std::vector<cocos2d::CCLabelBMFont*> m_lines;
cocos2d::CCTextAlignment m_alignment;
size_t m_maxLines;
float m_scale;
float m_lineHeight;
float m_linePadding;
bool m_artificialWidth;
public:
static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale);
static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale, const float width);
void setFont(const std::string& font);
std::string getFont();
void setAlignment(const cocos2d::CCTextAlignment alignment);
cocos2d::CCTextAlignment getAlignment();
void setText(const std::string& text);
std::string getText();
void setMaxLines(const size_t maxLines);
size_t getMaxLines();
void setWidth(const float width);
float getWidth();
void setScale(const float scale);
float getScale();
void setLinePadding(const float padding);
float getLinePadding();
std::vector<cocos2d::CCLabelBMFont*> getLines();
float getHeight();
float getLineHeight();
private:
static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth);
SimpleTextArea(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth);
cocos2d::CCLabelBMFont* createLabel(const std::string& text, const float top);
cocos2d::CCLabelBMFont* moveOverflow(cocos2d::CCLabelBMFont* line, const char c, const float top);
float calculateOffset(cocos2d::CCLabelBMFont* label);
void updateLines();
void updateContents();
};
}

View file

@ -0,0 +1,228 @@
#include <Geode/ui/TextArea.hpp>
using namespace geode::prelude;
SimpleTextArea* SimpleTextArea::create(const std::string& font, const std::string& text, const float scale = 1) {
return SimpleTextArea::create(font, text, scale, 500, false);
}
SimpleTextArea* SimpleTextArea::create(const std::string& font, const std::string& text, const float scale, const float width) {
return SimpleTextArea::create(font, text, scale, width, true);
}
SimpleTextArea* SimpleTextArea::create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth) {
SimpleTextArea* instance = new SimpleTextArea(font, text, scale, width, artificialWidth);
if (instance && instance->init()) {
instance->autorelease();
return instance;
} else {
CC_SAFE_DELETE(instance);
return nullptr;
}
}
SimpleTextArea::SimpleTextArea(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth) {
m_font = font;
m_text = text;
m_maxLines = 0;
m_scale = scale;
m_linePadding = 0;
m_artificialWidth = artificialWidth;
m_container = CCMenu::create();
this->setAnchorPoint({ 0.5f, 0.5f });
m_container->setPosition({ 0, 0 });
m_container->setAnchorPoint({ 0, 1 });
m_container->setContentSize({ width, 0 });
this->addChild(m_container);
this->updateContents();
}
void SimpleTextArea::setFont(const std::string& font) {
m_font = font;
this->updateContents();
}
std::string SimpleTextArea::getFont() {
return m_font;
}
void SimpleTextArea::setAlignment(const CCTextAlignment alignment) {
m_alignment = alignment;
this->updateContents();
}
CCTextAlignment SimpleTextArea::getAlignment() {
return m_alignment;
}
void SimpleTextArea::setText(const std::string& text) {
m_text = text;
this->updateContents();
}
std::string SimpleTextArea::getText() {
return m_text;
}
void SimpleTextArea::setMaxLines(const size_t maxLines) {
m_maxLines = maxLines;
this->updateContents();
}
size_t SimpleTextArea::getMaxLines() {
return m_maxLines;
}
void SimpleTextArea::setWidth(const float width) {
m_artificialWidth = true;
this->setContentSize({ width, this->getContentSize().height });
m_container->setContentSize(this->getContentSize());
}
float SimpleTextArea::getWidth() {
return m_container->getContentSize().width;
}
void SimpleTextArea::setScale(const float scale) {
m_scale = scale;
this->updateContents();
}
float SimpleTextArea::getScale() {
return m_scale;
}
void SimpleTextArea::setLinePadding(const float padding) {
m_linePadding = padding;
this->updateContents();
}
float SimpleTextArea::getLinePadding() {
return m_linePadding;
}
std::vector<CCLabelBMFont*> SimpleTextArea::getLines() {
return m_lines;
}
float SimpleTextArea::getHeight() {
return m_container->getContentSize().height;
}
float SimpleTextArea::getLineHeight() {
return m_lineHeight;
}
CCLabelBMFont* SimpleTextArea::createLabel(const std::string& text, const float top) {
CCLabelBMFont* label = CCLabelBMFont::create(text.c_str(), m_font.c_str());
label->setScale(m_scale);
label->setAnchorPoint({ 0, 0 });
label->setPosition({ 0, top });
return label;
}
CCLabelBMFont* SimpleTextArea::moveOverflow(CCLabelBMFont* line, const char c, const float top) {
const std::string text = line->getString();
const char back = text.back();
const bool lastIsSpace = back == ' ';
CCLabelBMFont* newLine = this->createLabel(std::string(!lastIsSpace, back).append(std::string(c != ' ', c)), top);
if (!lastIsSpace) {
if (text[text.size() - 2] == ' ') {
line->setString(text.substr(0, text.size() - 1).c_str());
} else {
line->setString((text.substr(0, text.size() - 1) + '-').c_str());
}
}
m_lines.push_back(newLine);
return newLine;
}
float SimpleTextArea::calculateOffset(CCLabelBMFont* label) {
return m_linePadding + label->getContentSize().height * m_scale;
}
void SimpleTextArea::updateLines() {
float top = 0;
CCLabelBMFont* line = this->createLabel("", top);
m_lines = { line };
for (const char c : m_text) {
if (m_maxLines && m_lines.size() > m_maxLines) {
CCLabelBMFont* last = m_lines.at(m_maxLines - 1);
const std::string text = last->getString();
m_lines.pop_back();
last->setString(text.substr(0, text.size() - 3).append("...").c_str());
break;
} else if (c == '\n') {
line = this->createLabel("", top -= this->calculateOffset(line));
m_lines.push_back(line);
} else if (m_artificialWidth && line->getContentSize().width >= this->getWidth()) {
line = this->moveOverflow(line, c, top -= this->calculateOffset(line));
} else {
const std::string text = line->getString();
line->setString((text + c).c_str());
}
}
}
void SimpleTextArea::updateContents() {
this->updateLines();
const size_t lineCount = m_lines.size();
const float width = this->getWidth();
if (lineCount > 0) {
m_lineHeight = m_lines.back()->getContentSize().height * m_scale;
} else {
m_lineHeight = 0;
}
float height = m_lineHeight * lineCount + m_linePadding * (lineCount - 1);
this->setContentSize({ width, height });
m_container->setContentSize(this->getContentSize());
m_container->removeAllChildren();
height -= m_lineHeight;
for (CCLabelBMFont* line : m_lines) {
const float y = height + line->getPositionY();
switch (m_alignment) {
case kCCTextAlignmentLeft: {
line->setAnchorPoint({ 0, 0 });
line->setPosition({ 0, y });
} break;
case kCCTextAlignmentCenter: {
line->setAnchorPoint({ 0.5f, 0 });
line->setPosition({ width / 2, y });
} break;
case kCCTextAlignmentRight: {
line->setAnchorPoint({ 1, 0 });
line->setPosition({ width, y });
} break;
}
m_container->addChild(line);
}
}