UI Label Wrap
This commit is contained in:
@ -15,7 +15,13 @@ UILabel::UILabel(SceneItem *item) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UILabel::onStart() {
|
void UILabel::onStart() {
|
||||||
|
UIComponentRenderable::onStart();
|
||||||
|
|
||||||
this->shaderBuffer.init();
|
this->shaderBuffer.init();
|
||||||
|
|
||||||
|
useEvent([&]{
|
||||||
|
this->rebufferQuads(this->texts);
|
||||||
|
}, eventAlignmentUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<struct ShaderPassItem> UILabel::getUIRenderPasses() {
|
std::vector<struct ShaderPassItem> UILabel::getUIRenderPasses() {
|
||||||
@ -68,163 +74,179 @@ std::vector<struct ShaderPassItem> UILabel::getUIRenderPasses() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float_t UILabel::getContentWidth() {
|
float_t UILabel::getContentWidth() {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
float_t UILabel::getContentHeight() {
|
float_t UILabel::getContentHeight() {
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UILabel::rebufferQuads(std::vector<struct UILabelText> newTexts) {
|
void UILabel::rebufferQuads(const std::vector<struct UILabelText> newTexts) {
|
||||||
auto oldTexts = this->texts;
|
|
||||||
|
|
||||||
textureMap.clear();
|
|
||||||
glm::vec2 position(0, 0);
|
|
||||||
struct FontShaderBufferData fontData;
|
|
||||||
int32_t quadIndex = 0;
|
|
||||||
int32_t partIndex = 0;
|
|
||||||
quadCountTotal = 0;
|
|
||||||
int32_t nextTexture = 0;
|
int32_t nextTexture = 0;
|
||||||
|
glm::vec2 position(0, 0);
|
||||||
// Determine how many quads there are, and the texture indexes.
|
int32_t partIndex = 0;
|
||||||
|
std::vector<std::pair<glm::vec4, glm::vec4>> vertices;
|
||||||
|
struct FontShaderBufferData fontData;
|
||||||
|
quadCountTotal = 0;
|
||||||
|
std::vector<struct UILabelText> realNewTexts;
|
||||||
|
|
||||||
|
// Determine font dimensions.
|
||||||
auto itText = newTexts.begin();
|
auto itText = newTexts.begin();
|
||||||
while(itText != newTexts.end()) {
|
while(itText != newTexts.end()) {
|
||||||
quadCountTotal += itText->text.length();
|
position.y = mathMax<float_t>(position.y, itText->style.size);
|
||||||
|
|
||||||
// Determine font and lock it.
|
|
||||||
assertNotNull(itText->style.font);
|
|
||||||
itText->lockId = itText->style.font->lock(NewTrueTypeFaceTextureStyle{
|
|
||||||
itText->style.size,
|
|
||||||
itText->style.style
|
|
||||||
});
|
|
||||||
assertTrue(itText->lockId != -1);
|
|
||||||
itText->texture = itText->style.font->getTexture(itText->lockId);
|
|
||||||
|
|
||||||
// Check for existing texture, if not, map it.
|
|
||||||
if(textureMap.find(itText->texture) == textureMap.end()) {
|
|
||||||
assertTrue(nextTexture < FONT_SHADER_TEXTURE_MAX);
|
|
||||||
textureMap[itText->texture] = nextTexture++;
|
|
||||||
}
|
|
||||||
|
|
||||||
++itText;
|
++itText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup old texst, we do this second so we don't unlock, cleanup, and then
|
// Now generate quads
|
||||||
// lock the same font, causing it to have to re-load.
|
itText = newTexts.begin();
|
||||||
itText = oldTexts.begin();
|
while(itText != newTexts.end()) {
|
||||||
while(itText != oldTexts.end()) {
|
auto text = *itText;
|
||||||
|
struct UILabelText realText;
|
||||||
|
|
||||||
|
// Clone values
|
||||||
|
realText.style = text.style;
|
||||||
|
|
||||||
|
// Lock the font
|
||||||
|
assertNotNull(text.style.font);
|
||||||
|
realText.lockId = text.style.font->lock(NewTrueTypeFaceTextureStyle{
|
||||||
|
text.style.size,
|
||||||
|
text.style.style
|
||||||
|
});
|
||||||
|
assertTrue(realText.lockId != -1);
|
||||||
|
realText.texture = text.style.font->getTexture(realText.lockId);
|
||||||
|
|
||||||
|
// Map texture
|
||||||
|
if(textureMap.find(realText.texture) == textureMap.end()) {
|
||||||
|
assertTrue(nextTexture < FONT_SHADER_TEXTURE_MAX);
|
||||||
|
textureMap[realText.texture] = nextTexture++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer shader values
|
||||||
|
fontData.textures[partIndex] = textureMap[realText.texture];
|
||||||
|
fontData.colors[partIndex] = realText.style.color;
|
||||||
|
|
||||||
|
// Get some texture info
|
||||||
|
glm::vec2 wh = glm::vec2(
|
||||||
|
realText.texture->texture.getWidth(),
|
||||||
|
realText.texture->texture.getHeight()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now, iterate each character
|
||||||
|
auto len = text.text.length();
|
||||||
|
int32_t lastSpaceCharacter = -1;
|
||||||
|
for(int32_t i = 0; i < len; i++) {
|
||||||
|
char ch = text.text[i];
|
||||||
|
|
||||||
|
// Handle newline
|
||||||
|
if(ch == '\n') {
|
||||||
|
position.x = 0;
|
||||||
|
position.y += realText.style.size;
|
||||||
|
|
||||||
|
glm::vec4 uvs(0, 0, 1, 1);
|
||||||
|
glm::vec4 vert(0, 0, 0, 0);
|
||||||
|
vertices.push_back(std::make_pair(vert, uvs));
|
||||||
|
fontData.fontQuadMappings[quadCountTotal] = partIndex;
|
||||||
|
quadCountTotal++;
|
||||||
|
realText.text += ch;
|
||||||
|
lastSpaceCharacter = i;
|
||||||
|
continue;
|
||||||
|
} else if(ch == ' ') {
|
||||||
|
lastSpaceCharacter = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate characters
|
||||||
|
assertTrue(ch >= NEW_TRUETYPE_CHAR_BEGIN && ch < NEW_TRUETYPE_CHAR_END);
|
||||||
|
assertTrue(ch != '\r');
|
||||||
|
assertTrue(ch != '\t');
|
||||||
|
assertTrue(ch != '\n');
|
||||||
|
|
||||||
|
FT_ULong c = ch;
|
||||||
|
auto charInfo = realText.texture->getCharacterData(c);
|
||||||
|
|
||||||
|
// Word wrapping
|
||||||
|
if(
|
||||||
|
ch != ' ' &&
|
||||||
|
lastSpaceCharacter != -1 &&
|
||||||
|
this->width > charInfo.bitmapSize.x &&
|
||||||
|
(position.x + charInfo.advanceX) > this->width
|
||||||
|
) {
|
||||||
|
int32_t diff = i - lastSpaceCharacter;
|
||||||
|
for(int32_t k = 0; k < diff; k++) vertices.pop_back();
|
||||||
|
text.text[lastSpaceCharacter] = '\n';
|
||||||
|
i = lastSpaceCharacter - 1;
|
||||||
|
lastSpaceCharacter = -1;
|
||||||
|
quadCountTotal -= diff;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer coordinates
|
||||||
|
glm::vec4 uvs;
|
||||||
|
uvs.x = 0.0f;
|
||||||
|
uvs.y = charInfo.textureY / wh.y;
|
||||||
|
uvs.w = charInfo.bitmapSize.x / wh.x;
|
||||||
|
uvs.z = uvs.y + (charInfo.bitmapSize.y / wh.y);
|
||||||
|
|
||||||
|
glm::vec4 vert;
|
||||||
|
vert.x = position.x + charInfo.bitmapPosition.x;
|
||||||
|
vert.y = position.y + charInfo.bitmapPosition.y;
|
||||||
|
vert.w = vert.x + charInfo.bitmapSize.x;
|
||||||
|
vert.z = vert.y + charInfo.bitmapSize.y;
|
||||||
|
|
||||||
|
// TODO:Check wordwrap here
|
||||||
|
vertices.push_back(std::make_pair(vert, uvs));
|
||||||
|
|
||||||
|
// Move the current position along.
|
||||||
|
position.x += charInfo.advanceX;
|
||||||
|
position.y += charInfo.advanceY;
|
||||||
|
|
||||||
|
// Set the part index to the quad mappings
|
||||||
|
fontData.fontQuadMappings[quadCountTotal] = partIndex;
|
||||||
|
quadCountTotal++;
|
||||||
|
realText.text += ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next
|
||||||
|
++partIndex;
|
||||||
|
++itText;
|
||||||
|
realNewTexts.push_back(realText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mesh
|
||||||
|
this->mesh.createBuffers(
|
||||||
|
QUAD_VERTICE_COUNT * vertices.size(),
|
||||||
|
QUAD_INDICE_COUNT * vertices.size()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now buffer the quads.
|
||||||
|
int32_t j = 0;
|
||||||
|
auto itQuad = vertices.begin();
|
||||||
|
while(itQuad != vertices.end()) {
|
||||||
|
auto vert = itQuad->first;
|
||||||
|
auto uvs = itQuad->second;
|
||||||
|
|
||||||
|
QuadMesh::bufferQuadMeshWithZ(&this->mesh,
|
||||||
|
glm::vec2(vert.x, vert.y), glm::vec2(uvs.x, uvs.y),
|
||||||
|
glm::vec2(vert.w, vert.z), glm::vec2(uvs.w, uvs.z),
|
||||||
|
0.0f,
|
||||||
|
j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT
|
||||||
|
);
|
||||||
|
j++;
|
||||||
|
++itQuad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer data
|
||||||
|
shaderBuffer.buffer(&fontData);
|
||||||
|
|
||||||
|
// Finally, release the old locks
|
||||||
|
itText = textsBuffered.begin();
|
||||||
|
while(itText != textsBuffered.end()) {
|
||||||
assertTrue(itText->lockId != -1);
|
assertTrue(itText->lockId != -1);
|
||||||
assertNotNull(itText->style.font);
|
assertNotNull(itText->style.font);
|
||||||
itText->style.font->unlock(itText->lockId);
|
itText->style.font->unlock(itText->lockId);
|
||||||
++itText;
|
++itText;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update texts.
|
// Update
|
||||||
this->texts = newTexts;
|
textsBuffered = realNewTexts;
|
||||||
|
texts = newTexts;
|
||||||
// Create mesh
|
|
||||||
this->mesh.createBuffers(
|
|
||||||
QUAD_VERTICE_COUNT * quadCountTotal,
|
|
||||||
QUAD_INDICE_COUNT * quadCountTotal
|
|
||||||
);
|
|
||||||
|
|
||||||
// Buffer the text quads
|
|
||||||
itText = newTexts.begin();
|
|
||||||
while(itText != newTexts.end()) {
|
|
||||||
position.y += itText->style.size;
|
|
||||||
quadIndex += this->bufferQuads(
|
|
||||||
*itText,
|
|
||||||
fontData,
|
|
||||||
textureMap,
|
|
||||||
position,
|
|
||||||
quadIndex,
|
|
||||||
partIndex
|
|
||||||
);
|
|
||||||
// position.y -= itText->style.size;
|
|
||||||
++partIndex;
|
|
||||||
++itText;
|
|
||||||
}
|
|
||||||
|
|
||||||
shaderBuffer.buffer(&fontData);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t UILabel::bufferQuads(
|
|
||||||
struct UILabelText text,
|
|
||||||
struct FontShaderBufferData &bufferData,
|
|
||||||
std::map<NewTrueTypeFaceTexture*, int32_t> &textureMap,
|
|
||||||
glm::vec2 &position,
|
|
||||||
int32_t quadStart,
|
|
||||||
int32_t partIndex
|
|
||||||
) {
|
|
||||||
// Get string length
|
|
||||||
int32_t len = text.text.length();
|
|
||||||
|
|
||||||
glm::vec2 wh = glm::vec2(
|
|
||||||
text.texture->texture.getWidth(),
|
|
||||||
text.texture->texture.getHeight()
|
|
||||||
);
|
|
||||||
|
|
||||||
// For each char
|
|
||||||
int32_t lastSpaceCharacter = -1;
|
|
||||||
for(int32_t i = 0; i < len; i++) {
|
|
||||||
char ch = text.text[i];
|
|
||||||
|
|
||||||
if(ch == '\n') {
|
|
||||||
position.x = 0;
|
|
||||||
position.y += text.style.size;
|
|
||||||
ch = ' ';
|
|
||||||
lastSpaceCharacter = i;
|
|
||||||
} else if(ch == ' ') {
|
|
||||||
lastSpaceCharacter = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid/Unsupported chars
|
|
||||||
assertTrue(ch >= NEW_TRUETYPE_CHAR_BEGIN && ch < NEW_TRUETYPE_CHAR_END);
|
|
||||||
assertTrue(ch != '\r');
|
|
||||||
assertTrue(ch != '\t');
|
|
||||||
|
|
||||||
int32_t j = quadStart + i;
|
|
||||||
FT_ULong c = ch;
|
|
||||||
auto charInfo = text.texture->getCharacterData(c);
|
|
||||||
|
|
||||||
// Word wrapping
|
|
||||||
if(
|
|
||||||
lastSpaceCharacter != -1 &&
|
|
||||||
this->width > 0 &&
|
|
||||||
(position.x+charInfo.advanceX) > this->width
|
|
||||||
) {
|
|
||||||
text.text[lastSpaceCharacter] = '\n';
|
|
||||||
i = lastSpaceCharacter - 1;
|
|
||||||
lastSpaceCharacter = -1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine texture coordinates.
|
|
||||||
glm::vec2 uv0 = glm::vec2(0.0f, charInfo.textureY) / wh;
|
|
||||||
glm::vec2 uv1 = uv0 + (charInfo.bitmapSize / wh);
|
|
||||||
|
|
||||||
// Buffer the quad.
|
|
||||||
assertTrue(j < FONT_SHADER_QUADS_MAX);
|
|
||||||
QuadMesh::bufferQuadMeshWithZ(&this->mesh,
|
|
||||||
position + charInfo.bitmapPosition, uv0,
|
|
||||||
position + charInfo.bitmapPosition + charInfo.bitmapSize, uv1,
|
|
||||||
0.0f,
|
|
||||||
j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT
|
|
||||||
);
|
|
||||||
|
|
||||||
// Move the current position along.
|
|
||||||
position.x += charInfo.advanceX;
|
|
||||||
position.y += charInfo.advanceY;
|
|
||||||
|
|
||||||
// Set the part index to the quad mappings
|
|
||||||
bufferData.fontQuadMappings[j] = partIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map texture level values
|
|
||||||
auto textureId = textureMap.find(text.texture);
|
|
||||||
assertTrue(textureId != textureMap.end());
|
|
||||||
bufferData.textures[partIndex] = textureId->second;
|
|
||||||
bufferData.colors[partIndex] = text.style.color;
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
}
|
@ -22,6 +22,9 @@ namespace Dawn {
|
|||||||
struct UILabelText {
|
struct UILabelText {
|
||||||
std::string text;
|
std::string text;
|
||||||
struct UILabelStyle style;
|
struct UILabelStyle style;
|
||||||
|
|
||||||
|
glm::vec2 position;
|
||||||
|
glm::vec2 size;
|
||||||
|
|
||||||
// Part index?
|
// Part index?
|
||||||
// Quad start?
|
// Quad start?
|
||||||
@ -39,29 +42,9 @@ namespace Dawn {
|
|||||||
Mesh mesh;
|
Mesh mesh;
|
||||||
FontShaderBuffer shaderBuffer;
|
FontShaderBuffer shaderBuffer;
|
||||||
std::vector<struct UILabelText> texts;
|
std::vector<struct UILabelText> texts;
|
||||||
|
std::vector<struct UILabelText> textsBuffered;
|
||||||
std::map<NewTrueTypeFaceTexture*, int32_t> textureMap;
|
std::map<NewTrueTypeFaceTexture*, int32_t> textureMap;
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffers the quads for the given text and updates the progressing values
|
|
||||||
* as the buffer process continues.
|
|
||||||
*
|
|
||||||
* @param text Text information to buffer.
|
|
||||||
* @param bufferData The output quad mappings for the text.
|
|
||||||
* @param textureMap Texture map for the textures to map.
|
|
||||||
* @param position The 2D position to buffer the quads at.
|
|
||||||
* @param quadStart The starting quad index.
|
|
||||||
* @param partIndex The part index to store for each quad buffered.
|
|
||||||
* @return The number of quads buffered, not the string length.
|
|
||||||
*/
|
|
||||||
int32_t bufferQuads(
|
|
||||||
struct UILabelText text,
|
|
||||||
struct FontShaderBufferData &bufferData,
|
|
||||||
std::map<NewTrueTypeFaceTexture*, int32_t> &textureMap,
|
|
||||||
glm::vec2 &position,
|
|
||||||
int32_t quadStart,
|
|
||||||
int32_t partIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int32_t quadStart = 0;
|
int32_t quadStart = 0;
|
||||||
int32_t quadCount = -1;
|
int32_t quadCount = -1;
|
||||||
|
@ -23,10 +23,13 @@ namespace Dawn {
|
|||||||
|
|
||||||
auto canvasItem = this->createSceneItem();
|
auto canvasItem = this->createSceneItem();
|
||||||
auto canvas = canvasItem->addComponent<UICanvas>();
|
auto canvas = canvasItem->addComponent<UICanvas>();
|
||||||
|
canvas->camera = camera;
|
||||||
|
|
||||||
auto newLabelItem = this->createSceneItem();
|
auto newLabelItem = this->createSceneItem();
|
||||||
newLabelItem->transform.setParent(canvas->transform);
|
|
||||||
auto newLabel = newLabelItem->addComponent<UIRichTextLabel>();
|
auto newLabel = newLabelItem->addComponent<UIRichTextLabel>();
|
||||||
|
newLabel->alignment = glm::vec4(0, 0, 0, 0);
|
||||||
|
newLabel->alignX = UI_COMPONENT_ALIGN_STRETCH;
|
||||||
|
newLabel->alignY = UI_COMPONENT_ALIGN_STRETCH;
|
||||||
// newLabel->maxWidth = 300.0f;
|
// newLabel->maxWidth = 300.0f;
|
||||||
newLabel->richText = std::string(
|
newLabel->richText = std::string(
|
||||||
"<font font=\"font_arial\" size=\"16\" color=\"COLOR_BLUE\">"
|
"<font font=\"font_arial\" size=\"16\" color=\"COLOR_BLUE\">"
|
||||||
@ -44,6 +47,7 @@ namespace Dawn {
|
|||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
|
||||||
"</font>"
|
"</font>"
|
||||||
);
|
);
|
||||||
|
newLabelItem->transform.setParent(canvas->transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Asset*> getRequiredAssets() override {
|
std::vector<Asset*> getRequiredAssets() override {
|
||||||
|
Reference in New Issue
Block a user