#include <UI.h>
#include <string>
#include <algorithm>
#include <chrono>
#include <thread>
#include <vector>
#include "constants.h"
#include "renderer.h"
void UI::pushToRenderer(Renderer* renderer) {
float leftX = xpos - 0.5 * (width / SCREEN_X);
float rightX = xpos + 0.5 * (width / SCREEN_X);
float topY = ypos + 0.5 * (height / SCREEN_Y);
float bottomY = ypos - 0.5 * (height / SCREEN_Y);
Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY };
Renderer::ColorRGB color(R, G, B);
renderer->drawQuad(quadVertices, color, texIndex, uvs);
}
bool UI::isHovered(GLFWwindow* window) {
double x, y;
glfwGetCursorPos(window, &x, &y);
float xNDC = static_cast<float>(x) / SCREEN_X * 2.0f - 1.0f;
float yNDC = 1.0f - static_cast<float>(y) / SCREEN_Y * 2.0f;
float leftX = xpos - 0.5 * (width / SCREEN_X);
float rightX = xpos + 0.5 * (width / SCREEN_X);
float topY = ypos + 0.5 * (height / SCREEN_Y);
float bottomY = ypos - 0.5 * (height / SCREEN_Y);
return (xNDC >= leftX && xNDC <= rightX && yNDC >= bottomY && yNDC <= topY);
}
bool UI::isClicked(GLFWwindow* window) {
return (isHovered(window) && (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS));
}
Button::Button(float height, float width, float xpos, float ypos, int R, int G, int B, int texIndex = 0, const float* uvs = nullptr) {
this->height = height;
this->width = width;
this->xpos = xpos;
this->ypos = ypos;
this->R = R;
this->G = G;
this->B = B;
this->texIndex = texIndex;
this->uvs = uvs;
}
Button::Button(float height, float width, float xpos, float ypos, int R, int G, int B, int texIndex, std::string message) {
this->height = height;
this->width = width;
this->xpos = xpos;
this->ypos = ypos;
this->R = R;
this->G = G;
this->B = B;
this->texIndex = texIndex;
this->uvs = nullptr;
this->message = message;
}
void Button::pushToRenderer(Renderer* renderer) {
// Draw button background
float leftX = xpos - 0.5f * (width / SCREEN_X);
float rightX = xpos + 0.5f * (width / SCREEN_X);
float topY = ypos + 0.5f * (height / SCREEN_Y);
float bottomY = ypos - 0.5f * (height / SCREEN_Y);
Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY };
Renderer::ColorRGB color(R, G, B);
if (message.empty()) {
renderer->drawQuad(quadVertices, color, texIndex, uvs); // default rendering image if no text
}
else {
renderer->drawQuad(quadVertices, color, 0, nullptr); // draws blank background if we are rendering text
int charNum = static_cast<int>(message.length());
float maxFontSizeByWidth = width / charNum;
float fontSize = std::min(maxFontSizeByWidth, height * 0.8f); // .8 height for padding
float charWidth = fontSize / SCREEN_X;
float charHeight = fontSize / SCREEN_Y;
float totalTextWidth = charWidth * charNum;
float startX = xpos - (totalTextWidth) / 2.0f;
for (int i = 0; i < charNum; i++) {
char c = message[i];
int charSetIndex = std::clamp((int)c - 32, 0, 95);
int col = charSetIndex % 16;
int row = charSetIndex / 16;
float ndcStartX = startX + i * charWidth;
float ndcEndX = ndcStartX + charWidth;
float ndcStartY = ypos - charHeight * 0.5f;
float ndcEndY = ypos + charHeight * 0.5f;
Renderer::QuadVertices textVertices = {
ndcStartX, ndcEndY,
ndcEndX, ndcEndY,
ndcStartX, ndcStartY,
ndcEndX, ndcStartY
};
float uv_x = col * 0.0625f;
float uv_y = row * 0.0625f;
float uv_size = 0.0625f;
float uvs[8] = {
uv_x, uv_y + uv_size,
uv_x, uv_y,
uv_x + uv_size, uv_y,
uv_x + uv_size, uv_y + uv_size
};
renderer->drawQuad(textVertices, color, texIndex, uvs);
}
}
}
Text::Text(float width, float xpos, float ypos, int texIndex, std::string message): UI() {
this->width = width;
this->height = width / message.length();
this->xpos = xpos;
this->ypos = ypos;
this->texIndex = texIndex;
this->message = message;
}
void Text::pushToRenderer(Renderer* renderer) {
char charset[] = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_'
};
int charNum = message.length();
float maxCharSize = width / charNum;
for (int i = 0; i < charNum; i++) {
char c = message[i];
int charSetIndex = std::clamp((int)c - 32, 0, 95);
int col = charSetIndex % 16;
int row = charSetIndex / 16;
float charWidth = width / charNum / (SCREEN_X);
float charHeight = height / (SCREEN_Y);
float ndcStartX = xpos + (i * charWidth) - (width * 0.5) / SCREEN_X;
float ndcEndX = ndcStartX + charWidth;
float ndcStartY = ypos - charHeight * 0.5f;
float ndcEndY = ypos + charHeight * 0.5f;
Renderer::QuadVertices quadVertices = {
ndcStartX, ndcEndY, // BL
ndcEndX, ndcEndY, // BR
ndcStartX, ndcStartY, // TL
ndcEndX, ndcStartY // TR
};
float uv_x = col * 0.0625f;
float uv_y = row * 0.0625f;
float uv_size = 0.0625f;
float uvs[8] = {
uv_x, uv_y + uv_size, // Top-Left (x3)
uv_x, uv_y, // Bottom-Left (x1)
uv_x + uv_size, uv_y, // Bottom-Right (x2)
uv_x + uv_size, uv_y + uv_size // Top-Right (x4)
};
Renderer::ColorRGB color(255, 255, 255);
renderer->drawQuad(quadVertices, color, texIndex, uvs);
}
}
TextBox::TextBox(float width, float fontSize, float xpos, float ypos, int texIndex, std::string defaultMessage)
: Text(width, xpos, ypos, texIndex, defaultMessage) {
this->fontSize = fontSize;
}
void TextBox::pushToRenderer(Renderer* renderer) {
char charset[] = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_'
};
int charNum = message.length();
float charWidth = fontSize / SCREEN_X;
float charHeight = fontSize / SCREEN_Y;
float totalTextWidth = charWidth * charNum;
float actualCharWidth = charWidth;
if (totalTextWidth > width / SCREEN_X) {
actualCharWidth = (width / SCREEN_X) / charNum;
}
else if (totalTextWidth < (width / SCREEN_X) * 0.1f) {
actualCharWidth = (width / SCREEN_X) * 0.1f / charNum;
}
float startX = xpos - (actualCharWidth * charNum) / 2.0f;
float leftX = xpos - 0.5f * (width / SCREEN_X);
float rightX = xpos + 0.5f * (width / SCREEN_X);
float topY = ypos + 0.5f * (fontSize / SCREEN_Y);
float bottomY = ypos - 0.5f * (fontSize / SCREEN_Y);
Renderer::QuadVertices backgroundVertices = {
leftX, topY,
rightX, topY,
leftX, bottomY,
rightX, bottomY
};
Renderer::ColorRGB color(255, 255, 255);
renderer->drawQuad(backgroundVertices, color, 0, nullptr);
for (int i = 0; i < charNum; i++) {
char c = message[i];
int charSetIndex = std::clamp((int)c - 32, 0, 95);
int col = charSetIndex % 16;
int row = charSetIndex / 16;
float ndcStartX = startX + i * actualCharWidth;
float ndcEndX = ndcStartX + actualCharWidth;
float ndcStartY = ypos - charHeight * 0.5f;
float ndcEndY = ypos + charHeight * 0.5f;
Renderer::QuadVertices quadVertices = {
ndcStartX, ndcEndY, // BL
ndcEndX, ndcEndY, // BR
ndcStartX, ndcStartY, // TL
ndcEndX, ndcStartY // TR
};
float uv_x = col * 0.0625f;
float uv_y = row * 0.0625f;
float uv_size = 0.0625f;
float uvs[8] = {
uv_x, uv_y + uv_size,
uv_x, uv_y,
uv_x + uv_size, uv_y,
uv_x + uv_size, uv_y + uv_size
};
renderer->drawQuad(quadVertices, color, texIndex, uvs);
}
}
void TextBox::collectInput(GLFWwindow* window) {
char allowedChars[] = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_'
};
for (int key = GLFW_KEY_SPACE; key <= GLFW_KEY_Z; ++key) {
int state = glfwGetKey(window, key);
if (state == GLFW_PRESS && !keyHeld[key]) {
char c = static_cast<char>(key);
if (std::find(std::begin(allowedChars), std::end(allowedChars), c) != std::end(allowedChars)) {
message += c;
}
keyHeld[key] = true;
}
else if (state == GLFW_RELEASE) {
keyHeld[key] = false;
}
}
// Backspace logic
int backspaceState = glfwGetKey(window, GLFW_KEY_BACKSPACE);
if (backspaceState == GLFW_PRESS && !keyHeld[GLFW_KEY_BACKSPACE]) {
if (!message.empty()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
message.pop_back();
}
}
if (message.empty()) {
//message = " ";
}
}
DrawingBoard::DrawingBoard(float height, float width, float xpos, float ypos, std::string saveAs)
// DrawingBoard drawingBoard(300.0f, 600.0f, 0.0f, 0.2f, "DRAW CHARACTER", "playerTex.png");
: clearBtn(*new Button(
height * 0.2f,
width * 0.5f,
xpos + width * 0.25f / SCREEN_X,
ypos - 0.7f * (height / SCREEN_Y) + height * 0.2f * 0.5f / SCREEN_Y,
200, 200, 100, 4, std::string("CLEAR"))),
saveBtn(*new Button(
height * 0.2f,
width * 0.5f,
xpos - width * 0.25f / SCREEN_X,
ypos - 0.7f * (height / SCREEN_Y) + height * 0.2f * 0.5f / SCREEN_Y,
200, 100, 200, 4, std::string("SAVE"))) {
this->height = height;
this->width = width;
this->xpos = xpos;
this->ypos = ypos;
this->saveAs = saveAs;
}
void DrawingBoard::pushToRenderer(Renderer* renderer) {
float leftX = xpos - 0.5f * (width / SCREEN_X);
float rightX = xpos + 0.5f * (width / SCREEN_X);
float topY = ypos + 0.5f * (height / SCREEN_Y);
float bottomY = ypos - 0.5f * (height / SCREEN_Y);
Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY };
Renderer::ColorRGB color(255, 255, 255);
renderer->drawQuad(quadVertices, color, texIndex, nullptr);
for (const auto& point : drawingPoints) {
float pointSize = 10;
float leftX = point.first - (pointSize / SCREEN_X);
float rightX = point.first + (pointSize / SCREEN_X);
float topY = point.second + (pointSize / SCREEN_Y);
float bottomY = point.second - (pointSize / SCREEN_Y);
Renderer::QuadVertices quadVertices = { leftX, topY, rightX, topY, leftX, bottomY, rightX, bottomY };
Renderer::ColorRGB color(0, 0, 0);
renderer->drawQuad(quadVertices, color, 0, nullptr);
}
clearBtn.pushToRenderer(renderer);
saveBtn.pushToRenderer(renderer);
}
bool DrawingBoard::isClicked(GLFWwindow* window) {
if (this->clearBtn.isClicked(window)) {
this->drawingPoints.clear();
saved = false;
this->saveBtn.message = "SAVE";
}
else if (saveBtn.isClicked(window)) {
if (saved || saveRequested) return false;
saveRequested = true;
this->saveBtn.message = "SAVED!";
}
else if (isHovered(window) && (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS)) {
double x, y;
glfwGetCursorPos(window, &x, &y);
float xNDC = static_cast<float>(x) / SCREEN_X * 2.0f - 1.0f;
float yNDC = 1.0f - static_cast<float>(y) / SCREEN_Y * 2.0f;
drawingPoints.push_back(std::make_pair(xNDC, yNDC));
//std::cout << "Added point: " << xNDC << ", " << yNDC << std::endl;
saved = false;
this->saveBtn.message = "SAVE";
return true;
}
return false;
}