You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

599 lines
25 KiB

#include "InputComponent.h"
#include "DocumentComponent.h"
#include <iostream>
#include "../graphical/font/TextRasterizerCache.h"
#include "../graphical/renderers/glfw/Shader.h"
extern TextRasterizerCache *rasterizerCache;
extern std::unique_ptr<Scheduler> scheduler;
// : BoxComponent(rawX, rawY, rawWidth, rawHeight, passedWindowWidth, passedWindowHeight)
InputComponent::InputComponent(const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int passedWindowWidth, const int passedWindowHeight) {
//std::cout << "InputComponent::InputComponent - create, boundToPage" << boundToPage << std::endl;
//std::cout << "InputComponent::InputComponent - window: " << windowWidth << "x" << windowHeight << " passed " << passedWindowWidth << "x" << passedWindowHeight << std::endl;
//boundToPage = true;
useBoxShader = true;
// set up state
windowWidth = passedWindowWidth;
windowHeight = passedWindowHeight;
// lets not set, like resize set, and then maybe parent width will be correct
// didn't really changed anything
width = rawWidth;
height = rawHeight;
// ugh
x = rawX;
y = rawY;
lastRenderedWindowHeight = windowHeight;
// const float rawX, const float rawY, const float rawWidth, const float rawHeight, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight
// we only want it one line high (esp for text area)
this->cursorBox = new BoxComponent(x, y, 2, 13, 0x000000FF, windowWidth, windowHeight);
this->cursorBox->boundToPage = this->boundToPage;
this->cursorBox->x = x;
this->cursorBox->y = y;
if (boundToPage) {
this->cursorBox->y = this->windowHeight + y - 13;
}
this->cursorBox->resize(this->windowWidth, this->windowHeight);
this->updateText();
onFocus=[this]() {
this->focused = true;
//std::cout << "focus input" << std::endl;
// blink cursor
this->cursorTimer = scheduler->setInterval([this]() {
this->showCursor = !this->showCursor;
//std::cout << "showCursor " << this->showCursor << std::endl;
if (this->win) {
this->win->renderDirty = true;
}
}, 500);
this->updateCursor(0, 0);
this->win->renderDirty = true;
};
onBlur=[this]() {
//std::cout << "blur input" << std::endl;
if (this->cursorTimer != nullptr) {
scheduler->clearInterval(this->cursorTimer);
}
this->focused = false;
this->win->renderDirty = true;
};
//std::cout << "InputComponent::InputComponent - placing box at " << (int)x << "," << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
// copy initial state
initialX = x;
initialY = y;
initialWidth = width;
initialHeight = height;
initialWindowWidth = windowWidth;
initialWindowHeight = windowHeight;
data[0][0][0] = 0xf0; // set R color
data[0][0][1] = 0xf0; // set G color
data[0][0][2] = 0xf0; // set B color
data[0][0][3] = 0xff; // set alpha
float vx = rawX;
float vy = rawY;
//std::cout << "placing box at " << (int)vx << "x" << (int)vy << " size: " << (int)rawWidth << "x" << (int)rawHeight << std::endl;
float vWidth = rawWidth;
float vHeight = rawHeight;
pointToViewport(vx, vy);
// converts 512 to 1 and 1 to 2
//std::cout << "vWidth before: " << (int)vWidth << std::endl;
distanceToViewport(vWidth, vHeight);
//std::cout << "vWidth after: " << (int)vWidth << std::endl;
//std::cout << "InputComponent::InputComponent - placing box at GL " << vx << "," << vy << " to " << vx+vWidth << "," << vy+vHeight << " size: " << vWidth << "x" << vHeight << std::endl;
vertices[(0 * 5) + 0] = vx;
vertices[(0 * 5) + 1] = vy + vHeight;
vertices[(1 * 5) + 0] = vx + vWidth;
vertices[(1 * 5) + 1] = vy + vHeight;
vertices[(2 * 5) + 0] = vx + vWidth;
vertices[(2 * 5) + 1] = vy;
vertices[(3 * 5) + 0] = vx;
vertices[(3 * 5) + 1] = vy;
glGenVertexArrays(1, &vertexArrayObject);
glGenBuffers(1, &vertexBufferObject);
glGenBuffers(1, &elementBufferObject);
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glBindVertexArray(0); // protect what we created against any further modification
}
void InputComponent::render() {
//std::cout << "InputComponent::render - at " << (int)x << "," << (int)y << std::endl;
//std::cout << "InputComponent::render - boundToPage " << boundToPage << std::endl;
GLenum glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - start not ok: " << glErr << std::endl;
}
if (verticesDirty) {
//std::cout << "BoxComponent::render - update dirty vertex" << std::endl;
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glBindBuffer not ok: " << glErr << std::endl;
}
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glBufferData not ok: " << glErr << std::endl;
}
verticesDirty = false;
}
glBindVertexArray(vertexArrayObject);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glBindVertexArray not ok: " << glErr << std::endl;
}
glBindTexture(GL_TEXTURE_2D, texture);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glBindTexture not ok: " << glErr << std::endl;
}
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glDrawElements not ok: " << glErr << std::endl;
}
glBindVertexArray(0);
// can actuall delete vertices here
if (userInputText) {
// make sure we're using win's transformMatrix
Shader *fontShader = this->win->shaderLoader.getShader(VertexShader("FontShader.vert"),
FragmentShader("FontShader.frag"));
fontShader->bind();
if (!boundToPage) {
GLint transformLocation = fontShader->uniform("transform");
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glGetUniformLocation not ok: " << glErr << std::endl;
}
// it's about the document transformMatrix
glUniformMatrix4fv(transformLocation, 1, GL_FALSE, this->win->transformMatrix);
glErr=glGetError();
if(glErr != GL_NO_ERROR) {
std::cout << "InputComponent::render - glUniformMatrix4fv not ok: " << glErr << std::endl;
}
}
//std::cout << "rendering some text" << std::endl;
userInputText->render();
Shader *textureShader = this->win->shaderLoader.getShader(VertexShader("TextureShader.vert"),
FragmentShader("TextureShader.frag"));
textureShader->bind();
}
if (focused) {
//std::cout << "Rendering cursor" << std::endl;
// render it if we need to
if (showCursor) {
cursorBox->render();
}
}
}
void InputComponent::resize(const int passedWindowWidth, const int passedWindowHeight) {
//std::cout << "InputComponent::resize" << std::endl;
//std::cout << "InputComponent::resize - rasterizing at " << (int)x << "x" << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
// maybe already done at component::resize
// set up state
windowWidth = passedWindowWidth;
windowHeight = passedWindowHeight;
//std::cout << "InputComponent::resize - boxShader: " << useBoxShader << " boundToPage: " << boundToPage << std::endl;
/*
if (!boundToPage) {
// ok because box shader is anchored to the bottom of the screen
// if we resize Y (increase height), we need to rebind
int yDiff = windowHeight - lastRenderedWindowHeight;
//std::cout << "InputComponent::resize - Adjusting y: " << y << " by " << yDiff << std::endl;
this->y += yDiff;
lastRenderedWindowHeight = windowHeight;
}
*/
// turning this off breaks coordinates
//boundToPage = true;
/*
useBoxShader = true;
for (int i = 0; i < 3; i++) {
data[0][0][i] = 0xf0; // set RGB color
}
data[0][0][3] = 0xff; // set alpha
*/
//std::cout << "InputComponent::resize - placing box at " << (int)x << "," << (int)y << " size: " << (int)width << "x" << (int)height << std::endl;
float vx = x;
float vy = y;
if (this->boundToPage) {
vy = this->windowHeight + y - height;
//std::cout << "InputComponent::resize - Adjust y to " << vy << " from " << y << " h: " << (int)height << std::endl;
}
pointToViewport(vx, vy);
float vWidth = width;
float vHeight = height;
distanceToViewport(vWidth, vHeight);
float vx1 = vx + vWidth;
float vy1 = vy + vHeight;
//std::cout << "InputComponent::resize - placing box at GL " << vx << "," << vy << " to " << vx1 << "," << vy1 << " size: " << vWidth << "x" << vHeight << std::endl;
vertices[(0 * 5) + 0] = vx;
vertices[(0 * 5) + 1] = vy1;
vertices[(1 * 5) + 0] = vx1;
vertices[(1 * 5) + 1] = vy1;
vertices[(2 * 5) + 0] = vx1;
vertices[(2 * 5) + 1] = vy;
vertices[(3 * 5) + 0] = vx;
vertices[(3 * 5) + 1] = vy;
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(0); // protect what we created against any further modification
//updateText();
if (userInputText) {
// do we need updateText here?
userInputText->resize(passedWindowWidth, passedWindowHeight);
}
}
std::string InputComponent::getValue() {
//return this->value;
return this->text.getValue();
}
void InputComponent::setValue(std::string newValue) {
//this->value = newValue;
this->text.setValue(newValue);
if (!this->multiLine) {
this->cursorCharX = static_cast<int>(newValue.size() & INT_MAX);
}
// if we pasted in a middle of block
}
void InputComponent::addChar(char c) {
this->text.insertAt(std::string(1, c), static_cast<size_t>(this->cursorCharX), static_cast<size_t>(this->cursorCharY));
//this->value += c;
//std::cout << "InputComponent::addChar - was cursor at " << this->cursorCharX << "," << this->cursorCharY << std::endl;
if (c == '\r') {
if (this->multiLine) {
this->cursorCharY++;
this->cursorCharX = 0;
}
} else {
this->cursorCharX++;
}
//std::cout << "InputComponent::addChar - at " << (int)x << "," << (int)y << std::endl;
this->updateCursor(0, 0);
this->updateText();
}
void InputComponent::backSpace() {
char last = '\0';
if (this->text.getValue().length()) {
last = this->text.getValue().at(this->text.getValue().length() - 1);
}
//std::cout << "InputComponent::backSpace - was cursor at " << this->cursorCharX << "," << this->cursorCharY << std::endl;
//value = value.substr(0, value.length() - 1);
if (this->cursorCharX) {
// since this backSpace, we want to remove the character before this cursor
// 0,A,1,S,2,D,3,F,4
// ^ means remove char 2 (starting at 0)
//
this->text.deleteAt(static_cast<size_t>(this->cursorCharX - 1), static_cast<size_t>(this->cursorCharY), 1);
} else {
// maybe delete line
this->text.deleteAt(static_cast<size_t>(this->cursorCharX), static_cast<size_t>(this->cursorCharY), 1);
}
//std::cout << "InputComponent::backSpace - removed [" << last << "]" << std::endl;
if (last == '\r') {
if (this->multiLine) {
this->cursorCharY--;
if (this->cursorCharY < 0) {
this->cursorCharY = 0;
this->cursorCharX = 0;
} else {
this->cursorCharX = this->text.lineLength(static_cast<size_t>(this->cursorCharY));
}
//std::pair<int, int> lineData = getLine(this->text.getValue(), this->cursorCharY);
//this->cursorCharX = lineData.second - lineData.first + 1;
}
} else {
this->cursorCharX--;
if (this->cursorCharX < 0) this->cursorCharX = 0;
}
//std::cout << "InputComponent::backSpace - at " << (int)x << "," << (int)y << std::endl;
this->updateCursor(0, 0);
this->updateText();
}
void InputComponent::updateCursor(int mX, int mY) {
//std::cout << "InputComponent::updateCursor - at " << this->cursorCharX << "," << this->cursorCharY << " move: " << mX << "," << mY << std::endl;
bool moved = false;
if (mX || mY) {
if (this->multiLine) {
if (mY < 0) {
// up
this->cursorCharY--;
if (this->cursorCharY < 0) {
this->cursorCharX = 0;
this->cursorCharY = 0;
}
} else if (mY > 0) {
// down
this->cursorCharY++;
if (static_cast<size_t>(this->cursorCharY) >= this->text.lines.size()) {
this->cursorCharY = this->text.lines.size() - 1;
this->cursorCharX = this->text.lineLength(static_cast<size_t>(this->cursorCharY));
}
}
} else {
if (mY < 0) {
// up
this->cursorCharX = 0;
} else if (mY > 0) {
// down
this->cursorCharX = this->text.lineLength(0);
}
}
if (mX < 0) {
// left
this->cursorCharX--;
if (this->cursorCharX < 0) {
this->cursorCharX = 0;
this->cursorCharY--;
if (this->cursorCharY < 0) {
this->cursorCharY = 0;
} else {
this->cursorCharX = this->text.lineLength(static_cast<size_t>(this->cursorCharY));
}
}
} else if (mX > 0) {
// right
this->cursorCharX++;
if (static_cast<size_t>(this->cursorCharX) > this->text.lineLength(static_cast<size_t>(this->cursorCharY))) {
this->cursorCharY++;
if (static_cast<size_t>(this->cursorCharY) >= this->text.lines.size()) {
this->cursorCharY = this->text.lines.size() - 1;
this->cursorCharX = this->text.lineLength(static_cast<size_t>(this->cursorCharY));
} else {
this->cursorCharX = 0;
}
}
}
//std::cout << "InputComponent::updateCursor - cursor now at " << this->cursorCharX << "," << this-> cursorCharY << std::endl;
}
if (this->cursorLastX != this->cursorCharX || this->cursorLastY != this->cursorCharY) {
moved = true;
}
//std::cout << "InputComponent::updateCursor - text[" << this->value << "]" << std::endl;
//std::pair<int, int> lineData = getLine(this->text.getValue(), this->cursorCharY);
//std::cout << "InputComponent::updateCursor - line " << this->cursorCharY << " starts at " << lineData.first << std::endl;
//size_t pos = static_cast<size_t>(lineData.first + this->cursorCharX);
//std::cout << "InputComponent::updateCursor - cx " << this->cursorCharX << " starts at value pos " << pos << std::endl;
//if (pos < this->text.getValue().size()) {
//std::cout << "InputComponent::updateCursor - Cursor at " << this->cursorCharX << "," << this->cursorCharY << " " << this->value.at(pos) << std::endl;
//}
const std::shared_ptr<TextRasterizer> textRasterizer = rasterizerCache->loadFont(12, false); // fontSize, bold
rasterizationRequest request;
//request.text = text.getValue().substr(0, pos);
request.text = this->text.getValueUpTo(static_cast<size_t>(this->cursorCharX), static_cast<size_t>(this->cursorCharY));
//std::cout << "InputComponent::updateCursor - sizeText[" << request.text << "]" << std::endl;
request.startX = x;
request.availableWidth = windowWidth;
request.sourceStartX = 0;
request.sourceStartY = 0;
if (this->multiLine) {
request.noWrap = false;
} else {
request.noWrap = true;
}
std::unique_ptr<sizeResponse> textInfo = textRasterizer->size(request);
//std::cout << "InputComponent::updateCursor - response at [" << textInfo->endingX << "," << textInfo->endingY << "]" << std::endl;
int textEndingX = textInfo->endingX;
int textEndingY = textInfo->endingY;
// adjust text crop
//std::cout << "c.endingY: " << textInfo->endingY << " c.y:" << endingY << "" << std::endl;
this->textCropX = std::max(textInfo->endingX, static_cast<int>(this->width));
this->textCropY = std::max(-textInfo->endingY, static_cast<int>(this->height));
this->textScrollY = 0; // reset scroll
//this->cursorStartAtX = textInfo->endingX;
//this->cursorStartAtY = textInfo->endingY;
// adjust our scroll
//std::cout << "cursor height" << (int)textInfo->height << " > " << (int)this->height << " (" << this->textScrollY << ")" << std::endl;
if (textInfo->height > this->height) {
textEndingY += textInfo->height - this->height;
this->textScrollY = textInfo->height - this->height;
}
// this is texture shader coordinates now (not text shader coords)
cursorBox->x = x + textEndingX;
if (boundToPage) {
cursorBox->y = this->windowHeight + this->y + textEndingY;
} else {
// + this->cursorStartAtY
cursorBox->y = this->y + 5;
}
//std::cout << "placing cursor at " << (int)cursorBox->x << "," << (int)cursorBox->y << std::endl;
cursorBox->resize(this->windowWidth, this->windowHeight);
// ok placement of the cursor may trigger the text to scroll and need to be re-rasterized
if (moved) {
/*
if (userInputText) {
std::cout << "cursor at " << (int)textInfo->endingY << " to " << textInfo->endingY << " textScrollY: " << this->textScrollY << " inputHeight: " << (int)this->height << " textHeight: " << (int)this->userInputText->height << " adjHeight: " << (int)(this->height - this->textScrollY) << std::endl;
// if we're at the top of input comp moving up
// or at the bottom of the input comp going down
if (-endingY > this->height - this->textScrollY) {
//this->textScrollY = this->height + endingY;
std::cout << "set textScrollY: " << this->textScrollY << std::endl;
}
}
*/
this->updateText();
// we're updated
this->cursorLastX = this->cursorCharX;
this->cursorLastY = this->cursorCharY;
}
// if we move the cursor, redraw immediately
this->showCursor = true;
if (this->win) {
this->win->renderDirty = true;
}
}
void InputComponent::updateText() {
//std::cout << "InputComponent::updateText - input value is now: " << this->text.getValue() << std::endl;
if (this->node) {
std::map<std::string, std::string>::iterator it = this->node->properties.find("value");
if (it == this->node->properties.end()) {
this->node->properties.insert(std::make_pair("value", this->text.getValue()));
} else {
it->second = this->text.getValue();
}
}
//std::cout << "placed userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y - windowHeight) << std::endl;
// 125 pixels width
// but first we need to know how wide the text is
const std::shared_ptr<TextRasterizer> textRasterizer = rasterizerCache->loadFont(12, false); // fontSize, bold
rasterizationRequest request;
request.text = text.getValue();
request.startX = x;
request.availableWidth = windowWidth;
request.sourceStartX = 0;
request.sourceStartY = 0;
// don't wrap if ML, otherwise do
if (this->multiLine) {
request.noWrap = false;
} else {
request.noWrap = true;
}
std::unique_ptr<sizeResponse> textInfo = textRasterizer->size(request);
if (textInfo.get() == nullptr) {
std::cout << "InputComponent::updateText - couldn't estimate value[" << this->text.getValue() << "] size" << std::endl;
return;
}
//int textWidth = textInfo->first;
//int estWidth = textInfo->width;
//int estHeight = textInfo->height;
//this->cursorStartAtX = textInfo->endingX;
//this->cursorStartAtY = textInfo->endingY;
// if textHeight is bigger than window, adjust cursor too
/*
if (estHeight > this->height) {
std::cout << "InputComponent::updateText - used to adj cursorStartAtY += " << (int)(estHeight - this->height) << " (" << estHeight << " - " << (int)this->height << ")" << std::endl;
//this->cursorStartAtY += estHeight - this->height;
}
*/
//this->updateCursor(0, 0);
// why does changing the width mess shit up?
//std::cout << "InputComponent::updateText - our width: " << static_cast<int>(width) << " windowWidth: " << windowWidth << std::endl;
if (this->win) {
// maybe tie the fontsize to height
if (this->userInputText) {
delete this->userInputText;
}
this->userInputText = new TextComponent(this->text.getValue(), 0, 0, 12, false, 0x000000FF, this->windowWidth, this->windowHeight);
if (!userInputText->win && this->win) {
userInputText->win = this->win;
}
//std::cout << "placing userInputText at " << static_cast<int>(x) << "," << static_cast<int>(y) << std::endl;
userInputText->x = this->x;
//std::cout << "InputComponent::updateText - y: " << static_cast<int>(this->y) << " boundToPage: " << this->boundToPage << std::endl;
//if (this->y < 0) {
if (this->boundToPage) {
userInputText->y = this->y;
} else {
userInputText->y = this->y - this->windowHeight + 16;
}
userInputText->boundToPage = this->boundToPage;
userInputText->rasterStartX = this->textScrollX;
userInputText->rasterStartY = this->textScrollY;
userInputText->rasterCropX = this->textCropX;
userInputText->rasterCropY = this->textCropY;
//std::cout << "InputComponent::updateText - estWidth: " << estWidth << " width: " << static_cast<int>(this->width) << " eX: " << textInfo->endingX << std::endl;
// no longer scroll to end of text
/*
if (estWidth > this->width) {
//std::cout << "scrolling text" << std::endl;
userInputText->rasterStartX = estWidth - this->width;
}
//std::cout << "InputComponent::updateText - estHeight: " << estHeight << " height: " << static_cast<int>(this->height) << " eY: " << textInfo->endingY << std::endl;
if (this->multiLine && estHeight > this->height) {
//std::cout << "scrolling text" << std::endl;
userInputText->rasterStartY = estHeight - this->height; // start at one widget height from the bottom
}
*/
//
/*
// translate cursor in scroll
std::cout << "cursor at " << this->cursorStartAtX << "," << this->cursorStartAtY << " size: " << (int)this->width << "," << (int)this->height << std::endl;
// so we only care about cursorStartAtY here atm
// we get values like -58, -43, -28, -13
if (this->cursorStartAtX > this->width) {
userInputText->rasterStartX = this->cursorStartAtX - this->width;
}
if (this->multiLine && -(this->cursorStartAtY + userInputText->height) > this->height) {
std::cout << "scrolling y start: " << (-(this->cursorStartAtY + userInputText->height) - this->height) << std::endl;
userInputText->rasterStartY = -this->cursorStartAtY - this->height;
}
*/
//std::cout << "InputComponent::updateText - textHeight: " << estHeight << " inputHeight: " << static_cast<int>(height) << " eY: " << textInfo->endingY << " y0max:" << textInfo->y0max << " rasterStartY: " << userInputText->rasterStartY << " lines: " << textInfo->lines << std::endl;
// don't wrap if multiLine, otherwise wrap
userInputText->noWrap = !this->multiLine;
userInputText->resize(this->windowWidth, this->windowHeight, this->width); // need to make sure there's a texture
this->win->renderDirty = true;
} // else it was called from cstr, and window isn't set up yet but no worrites, render will be dirty
}