From a2472c892926bb37abaa848d333f9f18c474503c Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 13 Aug 2023 12:12:25 +0300 Subject: [PATCH] merged GUI elements into vb01 --- CMakeLists.txt | 5 ++ button.cpp | 156 +++++++++++++++++++++++++++++++++ button.h | 53 +++++++++++ checkbox.cpp | 47 ++++++++++ checkbox.h | 39 +++++++++ listbox.cpp | 234 +++++++++++++++++++++++++++++++++++++++++++++++++ listbox.h | 75 ++++++++++++++++ slider.cpp | 78 +++++++++++++++++ slider.h | 52 +++++++++++ textbox.cpp | 131 +++++++++++++++++++++++++++ textbox.h | 59 +++++++++++++ 11 files changed, 929 insertions(+) create mode 100644 button.cpp create mode 100644 button.h create mode 100644 checkbox.cpp create mode 100644 checkbox.h create mode 100644 listbox.cpp create mode 100644 listbox.h create mode 100644 slider.cpp create mode 100644 slider.h create mode 100644 textbox.cpp create mode 100644 textbox.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e432b8..0ac2015 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(BUILD_SHARED_LIBS OFF) set(BUILD_TESTS OFF) set(BUILD_SAMPLES OFF) set(BUILD_WITH_ASSIMP OFF) +set(BUILD_GUI_ELEMENTS ON) set(LIB_NAME vb01) set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -25,6 +26,10 @@ if(BUILD_WITH_ASSIMP) set(ASSET_READERS ${ASSET_READERS} assimpModelReader.cpp) endif() +if(BUILD_GUI_ELEMENTS) + set(GUI ${GUI} button.cpp listbox.cpp textbox.cpp checkbox.cpp slider.cpp) +endif() + set(LIB_SRC ${RENDER} ${MATH} ${UTILS} ${GUI} ${ARMATURE} ${ASSET_MANAGER} ${ASSET_READERS} ${ANIM}) include_directories(external/tinydir) diff --git a/button.cpp b/button.cpp new file mode 100644 index 0000000..4de82b7 --- /dev/null +++ b/button.cpp @@ -0,0 +1,156 @@ +#include +#include +#include + +#include "root.h" +#include "quad.h" +#include "node.h" +#include "material.h" +#include "text.h" +#include "texture.h" +#include "util.h" +#include "button.h" + +using namespace std; +using namespace vb01; + +namespace vb01Gui{ + Button::Button(Vector2 pos, Vector2 size, string name, string fontPath, int trigger, bool separate, string imagePath){ + this->pos = pos; + this->size = size; + this->name = name; + this->separate = separate; + this->imagePath = imagePath; + this->trigger = trigger; + + Root *root = Root::getSingleton(); + guiNode = root->getGuiNode(); + rect = new Quad(Vector3(size.x, size.y, 0), false); + rectNode = new Node(Vector3(pos.x, pos.y, 0)); + Material *mat = new Material(root->getLibPath() + "gui"); + + if(imagePath == ""){ + mat->addBoolUniform(texUni, false); + mat->addVec4Uniform(diffColUni, color); + } + else{ + mat->addBoolUniform(texUni, true); + string frame[]{imagePath}; + Texture *tex = new Texture(frame, 1, false); + mat->addTexUniform("diffuseMap", tex, false); + textures.push_back(tex); + } + + rect->setMaterial(mat); + rectNode->attachMesh(rect); + guiNode->attachChild(rectNode); + + if(separate){ + text = new Text(fontPath, stringToWstring(name)); + text->setScale(.2); + + textNode = new Node(Vector3(pos.x, pos.y + size.y, - .1)); + textNode->addText(text); + + Material *textMat = new Material(root->getLibPath() + "text"); + textMat->addBoolUniform(texUni, false); + textMat->addVec4Uniform(diffColUni, Vector4::VEC_IJKL); + text->setMaterial(textMat); + + guiNode->attachChild(textNode); + } + + initWindowSize[0] = Root::getSingleton()->getWidth(); + initWindowSize[1] = Root::getSingleton()->getHeight(); + } + + Button::~Button(){ + if(textNode){ + guiNode->dettachChild(textNode); + delete textNode; + } + + guiNode->dettachChild(rectNode); + delete rectNode; + } + + void Button::update(){ + int width, height; + GLFWwindow *window = Root::getSingleton()->getWindow(); + glfwGetWindowSize(window, &width, &height); + + float posRatio[] = {pos.x / initWindowSize[0], pos.y / initWindowSize[1]}; + float sizeRatio[] = {size.x / initWindowSize[0], size.y / initWindowSize[1]}; + + pos = Vector2(width * posRatio[0], height * posRatio[1]); + size = Vector2(width * sizeRatio[0], height * sizeRatio[1]); + + initWindowSize[0] = width; + initWindowSize[1] = height; + + if(textNode) + textNode->setVisible(active); + + rectNode->setVisible(active); + } + + void Button::onMouseOver(){ + mouseOver = true; + } + + void Button::onMouseOff(){ + mouseOver = false; + } + + void Button::setPos(Vector2 pos){ + this->pos = pos; + float z1 = rectNode->getPosition().z; + rectNode->setPosition(Vector3(pos.x, pos.y, z1)); + + if(textNode){ + float z2 = textNode->getPosition().z; + textNode->setPosition(Vector3(pos.x, pos.y + size.y, z2)); + } + } + + void Button::setSize(Vector2 size){ + this->size = size; + rect->setSize(Vector3(size.x, size.y, 0)); + } + + void Button::setColor(Vector4 c){ + this->color = c; + ((Material::Vector4Uniform*)rect->getMaterial()->getUniform(diffColUni))->value = color; + } + + void Button::setZOrder(float zOrder){ + Vector3 rectPos = rectNode->getPosition(); + rectNode->setPosition(Vector3(rectPos.x, rectPos.y, zOrder)); + + if(textNode){ + Vector3 textPos = textNode->getPosition(); + textNode->setPosition(Vector3(textPos.x, textPos.y, zOrder - .1)); + } + } + + void Button::setImage(string image){ + int texId = -1; + Material *mat = rect->getMaterial(); + + for(int i = 0; i < textures.size(); i++) + if(textures[i]->getPath()[0] == image) + texId = i; + + /* + if(texId == -1){ + string p[]{image}; + textures.push_back(new Texture(p, 1)); + mat->setDiffuseMap(textures[textures.size() - 1], 0); + } + else + mat->setDiffuseMap(textures[texId], 0); + */ + + this->imagePath = image; + } +} diff --git a/button.h b/button.h new file mode 100644 index 0000000..6a99bfd --- /dev/null +++ b/button.h @@ -0,0 +1,53 @@ +#ifndef BUTTON_H +#define BUTTON_H + +#include +#include + +#include "vector.h" + +class GLFWwindow; + +namespace vb01{ + class Quad; + class Node; + class Text; + class Texture; +} + +namespace vb01Gui{ + class Button{ + public: + Button(vb01::Vector2, vb01::Vector2, std::string, std::string, int = -1, bool = true, std::string = ""); + virtual ~Button(); + void update(); + virtual void onMouseOver(); + virtual void onMouseOff(); + virtual void onClick(){} + inline vb01::Vector2 getPos(){return pos;} + void setPos(vb01::Vector2); + inline vb01::Vector2 getSize(){return size;} + void setSize(vb01::Vector2); + inline std::string getName(){return name;} + inline void setActive(bool active){this->active = active;} + inline bool isSeparate(){return separate;} + inline bool isActive(){return active;} + inline vb01::Vector4 getColor(){return color;} + inline int getTrigger(){return trigger;} + void setColor(vb01::Vector4); + void setZOrder(float); + void setImage(std::string); + protected: + int trigger, initWindowSize[2]; + bool separate, active = true, mouseOver = false; + vb01::Quad *rect; + vb01::Node *rectNode, *textNode = nullptr, *guiNode; + vb01::Vector2 pos, size; + vb01::Text *text = nullptr; + std::string name, imagePath, texUni = "texturingEnabled", diffColUni = "diffuseColor"; + std::vector textures; + vb01::Vector4 color = vb01::Vector4(.6, .6, .6, 1); + }; +} + +#endif diff --git a/checkbox.cpp b/checkbox.cpp new file mode 100644 index 0000000..95dfdc9 --- /dev/null +++ b/checkbox.cpp @@ -0,0 +1,47 @@ +#include "checkbox.h" +#include "text.h" +#include "node.h" +#include "material.h" + +using namespace vb01; +using namespace std; + +namespace vb01Gui{ + Checkbox::CheckboxButton::CheckboxButton(Checkbox *ch, Vector2 pos, Vector2 size, string name, bool separate) : Button(pos, size, name, "", -1, separate) { + checkbox = ch; + } + + void Checkbox::CheckboxButton::onClick() { + checkbox->check(); + float col = (checkbox->isChecked() ? .6 : .2); + setColor(Vector4(col, col, col, 1)); + } + + Checkbox::Checkbox(Vector2 pos, string fontPath){ + this->pos = pos; + checkboxButton = new CheckboxButton(this, pos, Vector2(length,length), "CheckboxButton", false); + + /* + Root *root = Root::getSingleton(); + checkNode = new Node(); + Text *check = new Text(fontPath, L"X"); + Material *mat = new Material(root->getLibPath() + "text"); + mat->addBoolUniform("texturingEnabled", false); + mat->addVec4Uniform("diffuseColor", Vector4::VEC_IJKL); + check->setMaterial(mat); + checkNode->addText(check); + checkNode->setVisible(false); + root->getGuiNode()->attachChild(checkNode); + checkNode->setPosition(Vector3(pos.x, pos.y, 0.1f)); + */ + } + + Checkbox::~Checkbox() { + + } + + void Checkbox::check() { + checked = !checked; + //checkNode->setVisible(checked); + } +} diff --git a/checkbox.h b/checkbox.h new file mode 100644 index 0000000..e1d8bd6 --- /dev/null +++ b/checkbox.h @@ -0,0 +1,39 @@ +#ifndef CHECKBOX_H +#define CHECKBOX_H + +#include "button.h" + +namespace vb01{ + class Quad; + class Node; + class Text; +} + +namespace vb01Gui{ + class Checkbox { + public: + Checkbox(vb01::Vector2, std::string); + ~Checkbox(); + void update(){} + virtual void check(); + virtual bool isChecked(){return checked;} + private: + class CheckboxButton : public Button { + public: + CheckboxButton(Checkbox*, vb01::Vector2, vb01::Vector2, std::string, bool); + void onClick(); + private: + Checkbox *checkbox = nullptr; + }; + + const int length = 15; + bool checked = false; + vb01::Vector2 pos; + CheckboxButton *checkboxButton; + vb01::Node *checkNode = nullptr; + public: + inline CheckboxButton* getCheckboxButton(){return checkboxButton;} + }; +} + +#endif diff --git a/listbox.cpp b/listbox.cpp new file mode 100644 index 0000000..d37c347 --- /dev/null +++ b/listbox.cpp @@ -0,0 +1,234 @@ +#include + +#include "listbox.h" +#include "util.h" +#include "node.h" +#include "text.h" +#include "quad.h" +#include "root.h" +#include "material.h" + +using namespace vb01; +using namespace std; + +namespace vb01Gui{ + Listbox::ListboxButton::ListboxButton(Listbox *l, Vector2 pos, Vector2 size, string name) : Button(pos, size, name, "", -1, false){listbox = l;} + + void Listbox::ListboxButton::onClick(){ + if(!listbox->isOpen()) + listbox->openUp(); + else{ + Vector2 cursorPos = getCursorPos(); + + if(cursorPos.x < listbox->getScrollingButton()->getPos().x){ + if(listbox->isCloseable()) + listbox->close(); + } + else + listbox->scrollToHeight(cursorPos.y); + } + } + + Listbox::ScrollingButton::ScrollingButton(Vector2 pos, Vector2 size, string name) : Button(pos, size, name, "", -1, false){} + + void Listbox::ScrollingButton::onClick(){} + + Listbox::Listbox(Vector2 pos, Vector2 size, std::vector &lines, int maxDisplay, string fontPath, bool closeable){ + this->pos = pos; + this->size = size; + this->maxDisplay = maxDisplay; + this->fontPath = fontPath; + this->lineHeight = size.y; + this->closeable = closeable; + + Root *root = Root::getSingleton(); + Vector2 mousePos = getCursorPos(); + guiNode = Root::getSingleton()->getGuiNode(); + string texUni = "texturingEnabled", diffColUni = "diffuseColor"; + + for(int i = 0; i < lines.size(); i++){ + Text *text = new Text(fontPath, stringToWstring(lines[i])); + text->setScale(.2); + + Material *textMat = new Material(root->getLibPath() + "text"); + textMat->addBoolUniform(texUni, false); + textMat->addVec4Uniform(diffColUni, Vector4::VEC_IJKL); + text->setMaterial(textMat); + + Node *node = new Node(Vector3(pos.x, pos.y + size.y * (i + 1), textZCoord)); + node->addText(text); + guiNode->attachChild(node); + node->setVisible(false); + + this->lines.push_back(text); + } + + this->lines[0]->getNode()->setVisible(true); + + listboxButton = new ListboxButton(this, pos, size, "ListboxButton"); + + scrollingButton = new ScrollingButton(Vector2(pos.x + size.x - 20, pos.y + lineHeight), Vector2(20, lineHeight * (maxDisplay - 2) / (maxDisplay + 1)), "scrollingButton"); + scrollingButton->setColor(Vector4(.2, .2, .2, 1)); + scrollingButton->setZOrder(-.05); + scrollingButton->setActive(false); + + Quad *selRect = new Quad(Vector3(size.x, size.y, 0), false); + selRectNode = new Node(Vector3(size.x, size.y, -.05)); + Material *mat = new Material(root->getLibPath() + "gui"); + mat->addBoolUniform(texUni, false); + mat->addVec4Uniform(diffColUni, Vector4(.6, .35, .05, 1)); + selRect->setMaterial(mat); + selRectNode->attachMesh(selRect); + selRectNode->setVisible(false); + guiNode->attachChild(selRectNode); + + if(!closeable) + openUp(); + } + + Listbox::~Listbox(){ + while(!lines.empty()){ + int id = lines.size() - 1; + Node *textNode = lines[id]->getNode(); + guiNode->dettachChild(textNode); + delete textNode; + lines.pop_back(); + } + + guiNode->dettachChild(selRectNode); + delete selRectNode; + } + + void Listbox::update(){ + scrollingButton->update(); + listboxButton->update(); + + if(open){ + Vector2 mousePos = getCursorPos(); + Vector2 p = pos; + + if(mousePos.y > pos.y && mousePos.y < pos.y + size.y * maxDisplay){ + selectedOption = scrollOffset + ((int)(mousePos.y - pos.y) / ((int)size.y)); + p.y = pos.y + size.y * ((int)(mousePos.y - pos.y) / ((int)size.y)); + } + else if(mousePos.y < pos.y){ + selectedOption = 0; + p.y = pos.y; + } + else{ + selectedOption = (maxDisplay + scrollOffset) - 1; + p.y = pos.y + size.y * (maxDisplay - 1); + } + + selRectNode->setPosition(Vector3(p.x, p.y, -.05)); + + Vector2 scrollButtonPos = Vector2(pos.x + size.x - 20, pos.y + lineHeight + lineHeight * scrollOffset * (double(maxDisplay - 2) / (maxDisplay + 1))); + scrollingButton->setPos(scrollButtonPos); + } + } + + void Listbox::scrollToHeight(float clickPos){ + float clickHeight = clickPos - (pos.y + lineHeight); + int newOffset = clickHeight / (lineHeight * (maxDisplay - 2) / (maxDisplay + 1)); + int diffOffset = scrollOffset - newOffset; + + for(int i = 0; i < abs(diffOffset); ++i){ + if(diffOffset > 0) + scrollUp(); + else + scrollDown(); + } + } + + void Listbox::openUp(){ + open = true; + lines[selectedOption]->getNode()->setPosition(Vector3(pos.x, pos.y + size.y * (selectedOption-scrollOffset + 1), textZCoord)); + lines[selectedOption]->getNode()->setVisible(false); + Vector2 size = listboxButton->getSize(); + size.y *= maxDisplay; + listboxButton->setSize(size); + selRectNode->setVisible(true); + scrollingButton->setActive(true); + + for(int i = scrollOffset; i < scrollOffset + maxDisplay; i++) + lines[i]->getNode()->setVisible(true); + + onOpen(); + } + + void Listbox::close(){ + open = false; + Vector2 size = listboxButton->getSize(); + size.y /= maxDisplay; + listboxButton->setSize(size); + selRectNode->setVisible(false); + scrollingButton->setActive(false); + + for(int i = scrollOffset; i < scrollOffset + maxDisplay; i++) + lines[i]->getNode()->setVisible(false); + + lines[selectedOption]->getNode()->setPosition(Vector3(pos.x, pos.y + size.y, textZCoord)); + lines[selectedOption]->getNode()->setVisible(true); + + onClose(); + } + + void Listbox::scrollUp(){ + if(scrollOffset > 0){ + scrollOffset--; + + for(Text *l : lines){ + Node *node = l->getNode(); + Vector3 p = node->getPosition(); + node->setPosition(Vector3(p.x, p.y + size.y, p.z)); + + if(node->getPosition().y > pos.y + size.y * maxDisplay) + node->setVisible(false); + else if(node->getPosition().y > pos.y) + node->setVisible(true); + } + } + } + + void Listbox::scrollDown(){ + if (scrollOffset < lines.size() - maxDisplay){ + scrollOffset++; + + for(Text *l : lines){ + Node *node = l->getNode(); + Vector3 p = node->getPosition(); + node->setPosition(Vector3(p.x, p.y - size.y, p.z)); + + if(node->getPosition().y - size.y < pos.y) + node->setVisible(false); + else if(node->getPosition().y < pos.y + size.y * (maxDisplay + 1)) + node->setVisible(true); + } + } + } + + void Listbox::changeLine(int id, wstring change){ + lines[id]->setText(change); + } + + std::vector Listbox::getContents(){ + std::vector lines; + + for(Text *t : this->lines) + lines.push_back(t->getText()); + + return lines; + } + + void Listbox::addLine(wstring line){ + Text *t = new Text(fontPath, line); + Node *node = new Node(Vector3(pos.x, pos.y - size.y * lines.size(), textZCoord)); + node->addText(t); + guiNode->attachChild(node); + lines.push_back(t); + } + + string Listbox::convert(string str){ + return str; + } +} diff --git a/listbox.h b/listbox.h new file mode 100644 index 0000000..49c361c --- /dev/null +++ b/listbox.h @@ -0,0 +1,75 @@ +#ifndef LISTBOX_H +#define LISTBOX_H + +#include + +#include "button.h" +#include "vector.h" + +namespace vb01{ + class Node; + class Text; +} + +namespace vb01Gui{ + class Listbox{ + public: + class ListboxButton : public Button{ + public: + ListboxButton(Listbox*, vb01::Vector2, vb01::Vector2, std::string); + void onClick(); + private: + Listbox *listbox = nullptr; + }; + + Listbox(vb01::Vector2, vb01::Vector2, std::vector&, int, std::string, bool = true); + ~Listbox(); + void update(); + void openUp(); + void close(); + void scrollUp(); + void scrollDown(); + void addLine(std::wstring); + void changeLine(int, std::wstring); + void scrollToHeight(float); + virtual void onOpen(){} + virtual void onClose(){} + inline int getScrollOffset(){return scrollOffset;} + inline float getLineHeight(){return lineHeight;} + inline bool isOpen(){return open;} + inline bool isCloseable(){return closeable;} + void appendLines(std::vector&); + inline int getSelectedOption(){return selectedOption;} + inline int getMaxDisplay(){return maxDisplay;} + inline int getNumLines(){return lines.size();} + std::vector getContents(); + inline vb01::Vector2 getPos(){return pos;} + inline vb01::Vector2 getSize(){return size;} + private: + class ScrollingButton : public Button{ + public: + ScrollingButton(vb01::Vector2, vb01::Vector2, std::string); + void onClick(); + private: + }; + + std::string convert(std::string); + + float textZCoord = -.3, lineHeight; + int maxDisplay, scrollOffset = 0; + bool open = false, closeable = false; + std::string fontPath; + vb01::Vector2 pos, size; + ListboxButton *listboxButton; + ScrollingButton *scrollingButton; + vb01::Node *selRectNode, *guiNode; + std::vector lines; + protected: + int selectedOption = 0; + public: + inline ListboxButton* getListboxButton(){return listboxButton;} + inline ScrollingButton* getScrollingButton(){return scrollingButton;} + }; +} + +#endif diff --git a/slider.cpp b/slider.cpp new file mode 100644 index 0000000..077e34e --- /dev/null +++ b/slider.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include + +#include "slider.h" +#include "root.h" +#include "util.h" + + +using namespace std; +using namespace vb01; + +namespace vb01Gui{ + Slider::MovableSliderButton::MovableSliderButton(Slider *slider, Vector2 pos, Vector2 size, string name, bool separate) : Button(pos, size, name, "", -1, separate) { + this->slider = slider; + } + + void Slider::MovableSliderButton::onClick() { + clicked = !clicked; + } + + void Slider::MovableSliderButton::update() { + Vector2 cursorPos; + + if (clicked && cursorPos.y > slider->getPos().y && cursorPos.y < slider->getPos().y + slider->getSize().y) { + if (cursorPos.x < slider->getPos().x) + cursorPos.x = slider->getPos().x; + else if (cursorPos.x > slider->getPos().x + slider->getSize().x) + cursorPos.x = slider->getPos().x + slider->getSize().x; + + slider->setValue((((double) cursorPos.x - slider->getPos().x) / slider->getSize().x) * slider->getMaxValue()); + } + } + + Slider::StaticSliderButton::StaticSliderButton(Slider *slider, Vector2 pos, Vector2 size, string name, bool separate) : Button(pos, size, name, "", -1, separate) { + this->slider = slider; + } + + void Slider::StaticSliderButton::onClick() { + Vector2 cursorPos = getCursorPos(); + s16 buttonClickPoint = -slider->getPos().x + cursorPos.x; + slider->setValue(((double) buttonClickPoint / slider->getSize().x) * slider->getMaxValue()); + } + + Slider::Slider(Vector2 pos, Vector2 size, double minValue, double maxValue) { + this->pos = pos; + this->size = size; + this->minValue = minValue; + this->maxValue = maxValue; + + staticSliderButton = new StaticSliderButton(this, pos, size, "StaticSliderButton", false); + movableSliderButton = new MovableSliderButton(this, Vector2(pos.x + staticSliderButton->getSize().x / 1, pos.y - 15), Vector2(10, 40), "MovableSliderButton", false); + + value = (maxValue - minValue) / 2; + } + + Slider::~Slider() { + } + + void Slider::update() { + if (value < minValue) + value = minValue; + else if (value > maxValue) + value = maxValue; + + Vector2 p = movableSliderButton->getPos(), s = movableSliderButton->getSize(); + p.x = pos.x + (double) size.x / maxValue * value; + movableSliderButton->update(); + movableSliderButton->setPos(p); + + if(textbox && !textbox->isEnabled()){ + ostringstream ss; + ss << value; + textbox->setEntry(stringToWstring(ss.str())); + } + } +} diff --git a/slider.h b/slider.h new file mode 100644 index 0000000..ce629b5 --- /dev/null +++ b/slider.h @@ -0,0 +1,52 @@ +#ifndef SLIDER_H +#define SLIDER_H + +#include "vector.h" +#include "button.h" +#include "textbox.h" + +namespace vb01Gui{ + class Slider { + public: + Slider(vb01::Vector2, vb01::Vector2, double, double); + ~Slider(); + void update(); + inline void setTextbox(Textbox *t){this->textbox=t;} + inline double getMinValue(){return minValue;} + inline double getValue(){return value;} + inline double getMaxValue(){return maxValue;} + inline void setValue(double v){this->value=v;} + inline vb01::Vector2 getPos(){return pos;} + inline vb01::Vector2 getSize(){return size;} + inline Textbox* getTextbox(){return textbox;} + private: + class MovableSliderButton : public Button { + public: + MovableSliderButton(Slider*, vb01::Vector2, vb01::Vector2, std::string, bool); + void onClick(); + void update(); + private: + Slider *slider; + bool clicked = false; + }; + + class StaticSliderButton : public Button { + public: + StaticSliderButton(Slider*, vb01::Vector2, vb01::Vector2, std::string, bool); + void onClick(); + private: + Slider *slider; + }; + + MovableSliderButton *movableSliderButton = nullptr; + StaticSliderButton *staticSliderButton = nullptr; + double minValue, value, maxValue; + vb01::Vector2 pos, size; + Textbox *textbox = nullptr; + public: + inline MovableSliderButton* getMovableSliderButton(){return movableSliderButton;} + inline StaticSliderButton* getStaticSliderButton(){return staticSliderButton;} + }; +} + +#endif diff --git a/textbox.cpp b/textbox.cpp new file mode 100644 index 0000000..c90846e --- /dev/null +++ b/textbox.cpp @@ -0,0 +1,131 @@ +#include +#include +#include + +#include "textbox.h" +#include "slider.h" +#include "root.h" +#include "node.h" +#include "quad.h" +#include "material.h" +#include "util.h" + +using namespace vb01; +using namespace std; + +namespace vb01Gui{ + Textbox::TextboxButton::TextboxButton(Textbox *t, Vector2 pos, Vector2 size, string name) : Button(pos, size, name, "", -1, false) {textbox = t;} + + void Textbox::TextboxButton::onClick(){ + if(!textbox->isEnabled()) + textbox->enable(); + else + textbox->disable(); + } + + Textbox::Textbox(Vector2 pos, Vector2 size, string fontPath, wstring entry){ + this->pos = pos; + this->size = size; + this->fontPath = fontPath; + + textboxButton = new TextboxButton(this, pos, size, "TextboxButton"); + string libPath = Root::getSingleton()->getLibPath(); + + guiNode = Root::getSingleton()->getGuiNode(); + text = new Text(fontPath, entry); + text->setScale(.5); + Material *textMat = new Material(libPath + "text"); + textMat->addBoolUniform("texturingEnabled", false); + textMat->addVec4Uniform("diffuseColor", Vector4(1, 1, 1, 1)); + text->setMaterial(textMat); + textNode = new Node(Vector3(pos.x, pos.y + size.y, -.1)); + textNode->addText(text); + guiNode->attachChild(textNode); + + cursorPosOffset = entry.length(); + cursorRect = new Quad(Vector3(cursorWidth, size.y, 0), false); + cursorNode = new Node(Vector3(pos.x + text->getLength(), pos.y, cursorZCoord)); + Material *mat = new Material(libPath + "gui"); + mat->addBoolUniform("texturingEnabled", false); + mat->addVec4Uniform("diffuseColor", Vector4(1, 1, 1, 1)); + cursorRect->setMaterial(mat); + cursorNode->attachMesh(cursorRect); + cursorNode->setVisible(false); + guiNode->attachChild(cursorNode); + } + + Textbox::~Textbox(){ + guiNode->dettachChild(textNode); + guiNode->dettachChild(cursorNode); + delete textNode; + delete cursorNode; + } + + void Textbox::update(){ + if(enabled){ + if(canChangeCursor()){ + canShowCursor = !canShowCursor; + lastBlinkTime = getTime(); + cursorNode->setVisible(canShowCursor); + } + } + } + + void Textbox::type(u32 c, bool capitalLetters){ + entry += c; + text->setText(entry); + moveCursor(false, text->getGlyph(c)->size.x); + } + + void Textbox::moveCursor(bool left, float charWidth){ + Vector3 p = cursorNode->getPosition(); + + if(left && entry.size() - cursorPosOffset > 0){ + cursorPosOffset--; + cursorNode->setPosition(Vector3(p.x - charWidth, p.y, p.z)); + } + else if(!left){ + cursorPosOffset++; + cursorNode->setPosition(Vector3(p.x + charWidth, p.y, p.z)); + } + } + + void Textbox::deleteCharacter(){ + if(entry.length() > 0 && canDeleteChar()){ + char c = entry.c_str()[entry.length() - 1]; + entry = entry.substr(0, entry.length() - 1); + text->setText(entry); + moveCursor(true, text->getGlyph(c)->size.x); + lastDeleteTime = getTime(); + } + } + + void Textbox::enable(){ + enabled = true; + lastBlinkTime = getTime(); + cursorNode->setVisible(true); + } + + void Textbox::disable(){ + enabled = false; + cursorNode->setVisible(false); + + if(slider){ + float val = strtof(wstringToString(entry).c_str(), nullptr); + + if(val < slider->getMinValue()) + val = slider->getMinValue(); + else if(val > slider->getMaxValue()) + val = slider->getMaxValue(); + + slider->setValue(val); + } + } + + void Textbox::setEntry(wstring entry){ + this->entry = entry; + text->setText(entry); + cursorNode->setPosition(Vector3(pos.x + text->getLength(), pos.y, cursorZCoord)); + cursorPosOffset = entry.length(); + } +} diff --git a/textbox.h b/textbox.h new file mode 100644 index 0000000..a6d86d1 --- /dev/null +++ b/textbox.h @@ -0,0 +1,59 @@ +#ifndef TEXTBOX_H +#define TEXTBOX_H + +#include "util.h" +#include "text.h" +#include "button.h" + +namespace vb01{ + class Node; + class Quad; +} + +namespace vb01Gui{ + class Slider; + + class Textbox{ + public: + Textbox(vb01::Vector2, vb01::Vector2, std::string, std::wstring = L""); + ~Textbox(); + void update(); + void enable(); + void disable(); + void type(vb01::u32, bool = false); + void moveCursor(bool, float); + void deleteCharacter(); + void setSlider(Slider *s){this->slider = s;} + void setEntry(std::wstring); + inline bool isEnabled(){return enabled;} + inline std::wstring getText(){return text->getText();} + void remove(); + private: + class TextboxButton : public Button{ + public: + TextboxButton(Textbox*, vb01::Vector2, vb01::Vector2, std::string); + void onClick(); + private: + Textbox *textbox; + }; + inline bool canChangeCursor(){return vb01::getTime() - lastBlinkTime > 250;} + inline bool canDeleteChar(){return vb01::getTime() - lastDeleteTime > 50;} + + float cursorZCoord = -.2; + const int cursorWidth = 5; + vb01::Vector2 pos, size; + std::wstring entry = L""; + std::string fontPath = ""; + bool enabled = false, canShowCursor = false, capitalLeters = false; + vb01::s64 lastBlinkTime = 0, lastDeleteTime = 0, cursorPosOffset = 0; + vb01::Quad *cursorRect; + vb01::Text *text; + vb01::Node *guiNode, *textNode, *cursorNode; + TextboxButton *textboxButton; + Slider *slider = nullptr; + public: + inline TextboxButton* getTextboxButton(){return textboxButton;} + }; +} + +#endif