Improve TextFieldElement
This commit is contained in:
parent
3b03637cd4
commit
94c2d9b8bd
@ -2,9 +2,11 @@
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <iostream>
|
||||
#include <stb_image_write.h>
|
||||
#include <string>
|
||||
|
||||
#include "constants.h"
|
||||
#include "engine.h"
|
||||
@ -16,7 +18,7 @@ namespace kek {
|
||||
|
||||
Shader *fontShader = nullptr;
|
||||
|
||||
TextObject::TextObject(Font *font, std::string text) {
|
||||
TextObject::TextObject(std::shared_ptr<Font> font, std::string text) {
|
||||
assert(text.length() > 0);
|
||||
|
||||
this->font = font;
|
||||
@ -52,7 +54,7 @@ TextObject &TextObject::operator=(TextObject &&other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Font *TextObject::getFont() {
|
||||
std::shared_ptr<Font> TextObject::getFont() {
|
||||
return font;
|
||||
}
|
||||
|
||||
@ -65,25 +67,16 @@ std::string TextObject::getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
TextMetrics TextObject::getMetrics(int sizePixels) {
|
||||
TextMetrics TextObject::getMetrics(unsigned int offset, unsigned int length, int sizePixels) {
|
||||
TextMetrics metrics = measureChars(Unicode::convertStdToU32(text), offset, length, nullptr);
|
||||
float sizeRatio = (float) sizePixels / KEK_FONT_RESOLUTION;
|
||||
return TextMetrics((int) (sizeRatio * offsetX), (int) (sizeRatio * offsetY), (int) (sizeRatio * width), (int) (sizeRatio * height));
|
||||
return TextMetrics((int) (sizeRatio * metrics.offsetX), (int) (sizeRatio * metrics.offsetY), (int) (sizeRatio * metrics.width), (int) (sizeRatio * metrics.height));
|
||||
}
|
||||
|
||||
struct RenderChar {
|
||||
float data[24];
|
||||
|
||||
RenderChar(float xPos, float yPos, float w, float h, float texL, float texR, float texU, float texD) {
|
||||
float coords[] = {
|
||||
xPos, yPos, texL, texU,
|
||||
xPos, yPos + h, texL, texD,
|
||||
xPos + w, yPos, texR, texU,
|
||||
xPos + w, yPos, texR, texU,
|
||||
xPos, yPos + h, texL, texD,
|
||||
xPos + w, yPos + h, texR, texD};
|
||||
for(int i = 0; i < 24; i++) data[i] = coords[i];
|
||||
}
|
||||
};
|
||||
TextMetrics TextObject::getMetrics(int sizePixels) {
|
||||
float sizeRatio = (float) sizePixels / KEK_FONT_RESOLUTION;
|
||||
return TextMetrics((int) (sizeRatio * unscaledMetrics.offsetX), (int) (sizeRatio * unscaledMetrics.offsetY), (int) (sizeRatio * unscaledMetrics.width), (int) (sizeRatio * unscaledMetrics.height));
|
||||
}
|
||||
|
||||
void TextObject::allocateBuffer(TextBlock *block, int numChars) {
|
||||
unsigned int targetSize = (numChars + KEK_TEXT_BLOCK_SIZE - 1) / KEK_TEXT_BLOCK_SIZE * KEK_TEXT_BLOCK_SIZE; // ceil(numChars / KEK_TEXT_BLOCK_SIZE) * KEK_TEXT_BLOCK_SIZE
|
||||
@ -106,9 +99,11 @@ void TextObject::allocateBuffer(TextBlock *block, int numChars) {
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void TextObject::loadChars() {
|
||||
std::map<unsigned int, std::vector<RenderChar>> chars;
|
||||
std::u32string str = Unicode::convertStdToU32(text);
|
||||
TextMetrics TextObject::measureChars(std::u32string str, unsigned int offset, unsigned int length, std::map<unsigned int, std::vector<RenderChar>> *chars) {
|
||||
if(offset != 0 || length != str.length()) {
|
||||
length = std::min(length, (unsigned int) (str.length() - offset));
|
||||
str = str.substr(offset, length);
|
||||
}
|
||||
|
||||
float x = 0, y = 0;
|
||||
int lineHeight = font->getDefaultMetrics().lineHeight;
|
||||
@ -132,29 +127,38 @@ void TextObject::loadChars() {
|
||||
float h = ch.size.y;
|
||||
if(h > maxH) maxH = h;
|
||||
|
||||
float texL = (ch.textureX) / (float) KEK_FONT_BITMAP_WIDTH;
|
||||
float texR = (ch.textureX + ch.size.x) / (float) KEK_FONT_BITMAP_WIDTH;
|
||||
float texU = (ch.textureY) / (float) KEK_FONT_BITMAP_HEIGHT;
|
||||
float texD = (ch.textureY + ch.size.y) / (float) KEK_FONT_BITMAP_HEIGHT;
|
||||
if(chars != nullptr) {
|
||||
auto &_chars = *chars;
|
||||
|
||||
RenderChar rCh = RenderChar(xPos, yPos, w, h, texL, texR, texU, texD);
|
||||
float texL = (ch.textureX) / (float) KEK_FONT_BITMAP_WIDTH;
|
||||
float texR = (ch.textureX + ch.size.x) / (float) KEK_FONT_BITMAP_WIDTH;
|
||||
float texU = (ch.textureY) / (float) KEK_FONT_BITMAP_HEIGHT;
|
||||
float texD = (ch.textureY + ch.size.y) / (float) KEK_FONT_BITMAP_HEIGHT;
|
||||
|
||||
auto charsForBlock = chars.find(charBlock);
|
||||
if(charsForBlock != chars.end()) {
|
||||
charsForBlock->second.push_back(rCh);
|
||||
} else {
|
||||
std::vector<RenderChar> chs;
|
||||
chs.push_back(rCh);
|
||||
chars[charBlock] = chs;
|
||||
RenderChar rCh = RenderChar(xPos, yPos, w, h, texL, texR, texU, texD);
|
||||
|
||||
auto charsForBlock = _chars.find(charBlock);
|
||||
if(charsForBlock != _chars.end()) {
|
||||
charsForBlock->second.push_back(rCh);
|
||||
} else {
|
||||
std::vector<RenderChar> chs;
|
||||
chs.push_back(rCh);
|
||||
_chars[charBlock] = chs;
|
||||
}
|
||||
}
|
||||
|
||||
x += (ch.advance >> 6); // == ch.advance / 64
|
||||
}
|
||||
|
||||
this->offsetX = offX;
|
||||
this->offsetY = offY;
|
||||
this->width = x;
|
||||
this->height = maxH;
|
||||
return TextMetrics(offX, offY, x, maxH);
|
||||
}
|
||||
|
||||
void TextObject::loadChars() {
|
||||
std::map<unsigned int, std::vector<RenderChar>> chars;
|
||||
std::u32string str = Unicode::convertStdToU32(text);
|
||||
|
||||
TextMetrics metrics = measureChars(str, 0, str.length(), &chars);
|
||||
this->unscaledMetrics = metrics;
|
||||
|
||||
auto it = chars.begin();
|
||||
while(it != chars.end()) {
|
||||
@ -284,10 +288,6 @@ CharacterBlock Font::generateCharacterBlock(unsigned int block) {
|
||||
return chBlock;
|
||||
}
|
||||
|
||||
TextObject *Font::createText(std::string text) {
|
||||
return new TextObject(this, text);
|
||||
}
|
||||
|
||||
FontMetrics Font::getDefaultMetrics() {
|
||||
return FontMetrics(lineHeight >> 6, ascender >> 6, descender >> 6);
|
||||
}
|
||||
@ -340,4 +340,18 @@ void Font::drawTextCentered(TextObject *textObject, glm::mat4 projection, int x,
|
||||
Font::drawText(textObject, projection, x - metrics.offsetX + metrics.width / 2, y + metrics.offsetY - metrics.height / 2, sizePixels, color);
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> Font::load(std::string fontPath) {
|
||||
std::shared_ptr<Font> ret = nullptr;
|
||||
|
||||
auto iter = kekData.loadedFonts.find(fontPath);
|
||||
if(iter != kekData.loadedFonts.end()) ret = iter->second.lock();
|
||||
|
||||
if(!ret) {
|
||||
ret = std::make_shared<Font>(fontPath);
|
||||
kekData.loadedFonts.emplace(fontPath, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ void Texture::use(GLenum texture) {
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> Texture::load(std::string texturePath) {
|
||||
std::shared_ptr<Texture> ret;
|
||||
std::shared_ptr<Texture> ret = nullptr;
|
||||
|
||||
auto iter = kekData.loadedTextures.find(texturePath);
|
||||
if(iter != kekData.loadedTextures.end()) ret = iter->second.lock();
|
||||
|
@ -244,12 +244,12 @@ UIElement *UIElement::dragEnterAll(UIPoint pos, UIPoint screenPos) {
|
||||
|
||||
void UI::init() {
|
||||
kekData.ui = new UIData();
|
||||
kekData.ui->defaultFont = new Font(KEK_DEFAULT_FONT);
|
||||
kekData.ui->defaultFont = Font::load(KEK_DEFAULT_FONT);
|
||||
kekData.ui->rectangleShader = new Shader("shader/rectangle/vertex.glsl", "shader/rectangle/fragment.glsl");
|
||||
}
|
||||
|
||||
void UI::destroy() {
|
||||
delete kekData.ui->defaultFont;
|
||||
kekData.ui->defaultFont = nullptr;
|
||||
delete kekData.ui->rectangleShader;
|
||||
delete kekData.ui;
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "constants.h"
|
||||
#include "fonts.h"
|
||||
#include "input.h"
|
||||
#include "internal.h"
|
||||
#include "internal/ui.h"
|
||||
@ -13,7 +15,7 @@
|
||||
|
||||
namespace kek {
|
||||
|
||||
TextElement::TextElement(UIValue x, UIValue y, Font *font)
|
||||
TextElement::TextElement(UIValue x, UIValue y, std::shared_ptr<Font> font)
|
||||
: UIElement(x, y) {
|
||||
this->text = new TextObject(font, "Text");
|
||||
this->sizePixels = KEK_DEFAULT_FONT_SIZE_PIXELS;
|
||||
@ -195,7 +197,7 @@ void ButtonElement::draw(UIPoint screenPos, glm::mat4 projection) {
|
||||
RectangleElement::draw(screenPos, projection);
|
||||
}
|
||||
|
||||
TextFieldElement::TextFieldElement(UIValue x, UIValue y, UIValue w, Font *font)
|
||||
TextFieldElement::TextFieldElement(UIValue x, UIValue y, UIValue w, std::shared_ptr<Font> font)
|
||||
: RectangleElement(x, y, w, uiPx(0)) {
|
||||
this->enableClipping = true;
|
||||
this->focusable = true;
|
||||
@ -233,6 +235,14 @@ TextFieldElement::~TextFieldElement() {
|
||||
delete cursor;
|
||||
}
|
||||
|
||||
void TextFieldElement::updateText(std::u32string newText) {
|
||||
this->text = Unicode::convertU32ToStd(newText);
|
||||
this->textElement->setText(this->text);
|
||||
|
||||
TextMetrics m = textElement->text->getMetrics(0, newText.length() - cursorPos, textElement->sizePixels);
|
||||
this->cursor->x = uiPx(m.width);
|
||||
}
|
||||
|
||||
UIElementType TextFieldElement::getType() {
|
||||
return UIElementType::TEXT_FIELD;
|
||||
}
|
||||
@ -247,23 +257,43 @@ void TextFieldElement::focusEnter() {
|
||||
|
||||
std::u32string str = Unicode::convertStdToU32(_this->text);
|
||||
if(event.codepoint == KEK_INPUT_DELETE) {
|
||||
if(str.length() == 0) return;
|
||||
str = str.substr(0, str.length() - 1);
|
||||
if(_this->cursorPos == str.length()) return;
|
||||
str = str.erase(str.length() - _this->cursorPos - 1, 1);
|
||||
} else {
|
||||
str.push_back(event.codepoint);
|
||||
str.insert(str.length() - _this->cursorPos, 1, event.codepoint);
|
||||
}
|
||||
|
||||
_this->text = Unicode::convertU32ToStd(str);
|
||||
_this->textElement->setText(_this->text);
|
||||
|
||||
_this->lastCharTyped = glfwGetTime();
|
||||
_this->cursor->x = uiPx(_this->textElement->getBounds().w);
|
||||
_this->updateText(str);
|
||||
},
|
||||
this),
|
||||
KeyCallback([](KeyEvent event, void *data) {
|
||||
TextFieldElement *_this = (TextFieldElement *) data;
|
||||
if(event.key == GLFW_KEY_ENTER && event.action == GLFW_PRESS) {
|
||||
Input::uncaptureKeyboardInput(_this->capture);
|
||||
|
||||
if(event.action == GLFW_PRESS || event.action == GLFW_REPEAT) {
|
||||
switch(event.key) {
|
||||
case GLFW_KEY_ENTER:
|
||||
Input::uncaptureKeyboardInput(_this->capture);
|
||||
break;
|
||||
case GLFW_KEY_LEFT: {
|
||||
std::u32string str = Unicode::convertStdToU32(_this->text);
|
||||
if(_this->cursorPos < str.length()) {
|
||||
_this->cursorPos++;
|
||||
_this->updateText(str);
|
||||
_this->lastCharTyped = glfwGetTime();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GLFW_KEY_RIGHT: {
|
||||
std::u32string str = Unicode::convertStdToU32(_this->text);
|
||||
if(_this->cursorPos > 0) {
|
||||
_this->cursorPos--;
|
||||
_this->updateText(str);
|
||||
_this->lastCharTyped = glfwGetTime();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
this),
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <ft2build.h>
|
||||
#include <memory>
|
||||
#include FT_FREETYPE_H
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
@ -31,12 +32,17 @@ struct TextMetrics {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
TextMetrics(int offsetX, int offsetY, unsigned int width, unsigned int height) {
|
||||
this->offsetX = offsetX;
|
||||
this->offsetY = offsetY;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
TextMetrics()
|
||||
: offsetX(0),
|
||||
offsetY(0),
|
||||
width(0),
|
||||
height(0) {}
|
||||
|
||||
TextMetrics(int offsetX, int offsetY, unsigned int width, unsigned int height)
|
||||
: offsetX(offsetX),
|
||||
offsetY(offsetY),
|
||||
width(width),
|
||||
height(height) {}
|
||||
};
|
||||
|
||||
struct FontMetrics {
|
||||
@ -62,21 +68,33 @@ struct TextBlock {
|
||||
unsigned int vbo;
|
||||
};
|
||||
|
||||
struct RenderChar {
|
||||
float data[24];
|
||||
|
||||
RenderChar(float xPos, float yPos, float w, float h, float texL, float texR, float texU, float texD) {
|
||||
float coords[] = {
|
||||
xPos, yPos, texL, texU,
|
||||
xPos, yPos + h, texL, texD,
|
||||
xPos + w, yPos, texR, texU,
|
||||
xPos + w, yPos, texR, texU,
|
||||
xPos, yPos + h, texL, texD,
|
||||
xPos + w, yPos + h, texR, texD};
|
||||
for(int i = 0; i < 24; i++) data[i] = coords[i];
|
||||
}
|
||||
};
|
||||
|
||||
class TextObject {
|
||||
|
||||
private:
|
||||
Font *font;
|
||||
std::shared_ptr<Font> font;
|
||||
std::string text;
|
||||
|
||||
int offsetX;
|
||||
int offsetY;
|
||||
int width;
|
||||
int height;
|
||||
TextMetrics unscaledMetrics;
|
||||
|
||||
public:
|
||||
std::map<unsigned int, TextBlock> blocks;
|
||||
|
||||
TextObject(Font *font, std::string text);
|
||||
TextObject(std::shared_ptr<Font> font, std::string text);
|
||||
|
||||
~TextObject();
|
||||
|
||||
@ -88,12 +106,14 @@ class TextObject {
|
||||
|
||||
TextObject &operator=(TextObject &&other);
|
||||
|
||||
Font *getFont();
|
||||
std::shared_ptr<Font> getFont();
|
||||
|
||||
void setText(std::string text);
|
||||
|
||||
std::string getText();
|
||||
|
||||
TextMetrics getMetrics(unsigned int offset, unsigned int length, int sizePixels);
|
||||
|
||||
TextMetrics getMetrics(int sizePixels);
|
||||
|
||||
private:
|
||||
@ -101,6 +121,8 @@ class TextObject {
|
||||
|
||||
void allocateBuffer(TextBlock *block, int numChars);
|
||||
|
||||
TextMetrics measureChars(std::u32string str, unsigned int offset, unsigned int length, std::map<unsigned int, std::vector<RenderChar>> *chars);
|
||||
|
||||
void loadChars();
|
||||
};
|
||||
|
||||
@ -123,8 +145,6 @@ class Font {
|
||||
|
||||
CharacterBlock getCharacterBlock(unsigned int block);
|
||||
|
||||
TextObject *createText(std::string text);
|
||||
|
||||
FontMetrics getDefaultMetrics();
|
||||
|
||||
FontMetrics getMetrics(int sizePixels);
|
||||
@ -149,6 +169,9 @@ class Font {
|
||||
// Draws text centered around (x,y)
|
||||
void drawTextCentered(TextObject *textObject, glm::mat4 projection, int x, int y, int sizePixels, Color color);
|
||||
|
||||
// Loads a font from resources
|
||||
static std::shared_ptr<Font> load(std::string path);
|
||||
|
||||
private:
|
||||
CharacterBlock generateCharacterBlock(unsigned int block);
|
||||
};
|
||||
|
@ -33,6 +33,7 @@ struct KekData {
|
||||
float lastFrameTime;
|
||||
|
||||
std::map<std::string, std::weak_ptr<Texture>> loadedTextures;
|
||||
std::map<std::string, std::weak_ptr<Font>> loadedFonts;
|
||||
|
||||
FT_Library freetype;
|
||||
|
||||
|
@ -7,7 +7,7 @@ namespace kek {
|
||||
struct UIData {
|
||||
std::vector<UIElement *> elements;
|
||||
UIElement *focusedElement;
|
||||
Font *defaultFont;
|
||||
std::shared_ptr<Font> defaultFont;
|
||||
Shader *rectangleShader;
|
||||
};
|
||||
|
||||
|
@ -30,16 +30,15 @@ enum class TextBounds {
|
||||
|
||||
class TextElement: public UIElement {
|
||||
|
||||
protected:
|
||||
public:
|
||||
TextObject *text;
|
||||
|
||||
public:
|
||||
int sizePixels;
|
||||
Color color;
|
||||
TextMode textMode;
|
||||
TextBounds textBounds;
|
||||
|
||||
TextElement(UIValue x, UIValue y, Font *font);
|
||||
TextElement(UIValue x, UIValue y, std::shared_ptr<Font> font);
|
||||
|
||||
TextElement(UIValue x, UIValue y);
|
||||
|
||||
@ -109,9 +108,12 @@ class TextFieldElement: public RectangleElement {
|
||||
|
||||
KeyboardCapture capture;
|
||||
float lastCharTyped = 0;
|
||||
unsigned int cursorPos = 0; // Cursor offset from the back of the string
|
||||
|
||||
void updateText(std::u32string newText);
|
||||
|
||||
public:
|
||||
TextFieldElement(UIValue x, UIValue y, UIValue w, Font *font);
|
||||
TextFieldElement(UIValue x, UIValue y, UIValue w, std::shared_ptr<Font> font);
|
||||
|
||||
TextFieldElement(UIValue x, UIValue y, UIValue w);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user