frontend/gui moved to graphics/ui
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
#include "UINode.h"
|
||||
|
||||
#include "../../core/Batch2D.h"
|
||||
|
||||
using gui::UINode;
|
||||
using gui::Align;
|
||||
|
||||
UINode::UINode(glm::vec2 size) : size(size) {
|
||||
}
|
||||
|
||||
UINode::~UINode() {
|
||||
}
|
||||
|
||||
bool UINode::isVisible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
void UINode::setVisible(bool flag) {
|
||||
visible = flag;
|
||||
}
|
||||
|
||||
Align UINode::getAlign() const {
|
||||
return align;
|
||||
}
|
||||
|
||||
void UINode::setAlign(Align align) {
|
||||
this->align = align;
|
||||
}
|
||||
|
||||
void UINode::setHover(bool flag) {
|
||||
hover = flag;
|
||||
}
|
||||
|
||||
bool UINode::isHover() const {
|
||||
return hover;
|
||||
}
|
||||
|
||||
void UINode::setParent(UINode* node) {
|
||||
parent = node;
|
||||
}
|
||||
|
||||
UINode* UINode::getParent() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
void UINode::click(GUI*, int x, int y) {
|
||||
pressed = true;
|
||||
}
|
||||
|
||||
void UINode::mouseRelease(GUI*, int x, int y) {
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
bool UINode::isPressed() const {
|
||||
return pressed;
|
||||
}
|
||||
|
||||
void UINode::defocus() {
|
||||
focused = false;
|
||||
}
|
||||
|
||||
bool UINode::isFocused() const {
|
||||
return focused;
|
||||
}
|
||||
|
||||
bool UINode::isInside(glm::vec2 point) {
|
||||
glm::vec2 pos = calcPos();
|
||||
glm::vec2 size = getSize();
|
||||
return (point.x >= pos.x && point.y >= pos.y &&
|
||||
point.x < pos.x + size.x && point.y < pos.y + size.y);
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> UINode::getAt(glm::vec2 point, std::shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
return isInside(point) ? self : nullptr;
|
||||
}
|
||||
|
||||
bool UINode::isInteractive() const {
|
||||
return interactive && isVisible();
|
||||
}
|
||||
|
||||
void UINode::setInteractive(bool flag) {
|
||||
interactive = flag;
|
||||
}
|
||||
|
||||
void UINode::setResizing(bool flag) {
|
||||
resizing = flag;
|
||||
}
|
||||
|
||||
bool UINode::isResizing() const {
|
||||
return resizing;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::calcPos() const {
|
||||
if (parent) {
|
||||
return pos + parent->calcPos() + parent->contentOffset();
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
void UINode::scrolled(int value) {
|
||||
if (parent) {
|
||||
parent->scrolled(value);
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setPos(glm::vec2 pos) {
|
||||
this->pos = pos;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::getPos() const {
|
||||
return pos;
|
||||
}
|
||||
|
||||
glm::vec2 UINode::getSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
void UINode::setSize(glm::vec2 size) {
|
||||
this->size = glm::vec2(
|
||||
glm::max(minSize.x, size.x), glm::max(minSize.y, size.y)
|
||||
);
|
||||
}
|
||||
|
||||
glm::vec2 UINode::getMinSize() const {
|
||||
return minSize;
|
||||
}
|
||||
|
||||
void UINode::setMinSize(glm::vec2 minSize) {
|
||||
this->minSize = minSize;
|
||||
setSize(getSize());
|
||||
}
|
||||
|
||||
void UINode::setColor(glm::vec4 color) {
|
||||
this->color = color;
|
||||
this->hoverColor = color;
|
||||
}
|
||||
|
||||
void UINode::setHoverColor(glm::vec4 newColor) {
|
||||
this->hoverColor = newColor;
|
||||
}
|
||||
|
||||
glm::vec4 UINode::getHoverColor() const {
|
||||
return hoverColor;
|
||||
}
|
||||
|
||||
glm::vec4 UINode::getColor() const {
|
||||
return color;
|
||||
}
|
||||
|
||||
void UINode::setMargin(glm::vec4 margin) {
|
||||
this->margin = margin;
|
||||
}
|
||||
|
||||
glm::vec4 UINode::getMargin() const {
|
||||
return margin;
|
||||
}
|
||||
|
||||
void UINode::setZIndex(int zindex) {
|
||||
this->zindex = zindex;
|
||||
}
|
||||
|
||||
int UINode::getZIndex() const {
|
||||
return zindex;
|
||||
}
|
||||
|
||||
void UINode::lock() {
|
||||
}
|
||||
|
||||
vec2supplier UINode::getPositionFunc() const {
|
||||
return positionfunc;
|
||||
}
|
||||
|
||||
void UINode::setPositionFunc(vec2supplier func) {
|
||||
positionfunc = func;
|
||||
}
|
||||
|
||||
void UINode::setId(const std::string& id) {
|
||||
this->id = id;
|
||||
}
|
||||
|
||||
const std::string& UINode::getId() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
void UINode::reposition() {
|
||||
if (positionfunc) {
|
||||
setPos(positionfunc());
|
||||
}
|
||||
}
|
||||
|
||||
void UINode::setGravity(Gravity gravity) {
|
||||
if (gravity == Gravity::none) {
|
||||
setPositionFunc(nullptr);
|
||||
return;
|
||||
}
|
||||
setPositionFunc([this, gravity](){
|
||||
auto parent = getParent();
|
||||
if (parent == nullptr) {
|
||||
return getPos();
|
||||
}
|
||||
glm::vec4 margin = getMargin();
|
||||
glm::vec2 size = getSize();
|
||||
glm::vec2 parentSize = parent->getSize();
|
||||
|
||||
float x = 0.0f, y = 0.0f;
|
||||
switch (gravity) {
|
||||
case Gravity::top_left:
|
||||
case Gravity::center_left:
|
||||
case Gravity::bottom_left: x = parentSize.x+margin.x; break;
|
||||
case Gravity::top_center:
|
||||
case Gravity::center_center:
|
||||
case Gravity::bottom_center: x = (parentSize.x-size.x)/2.0f; break;
|
||||
case Gravity::top_right:
|
||||
case Gravity::center_right:
|
||||
case Gravity::bottom_right: x = parentSize.x-size.x-margin.z; break;
|
||||
default: break;
|
||||
}
|
||||
switch (gravity) {
|
||||
case Gravity::top_left:
|
||||
case Gravity::top_center:
|
||||
case Gravity::top_right: y = parentSize.y+margin.y; break;
|
||||
case Gravity::center_left:
|
||||
case Gravity::center_center:
|
||||
case Gravity::center_right: y = (parentSize.y-size.y)/2.0f; break;
|
||||
case Gravity::bottom_left:
|
||||
case Gravity::bottom_center:
|
||||
case Gravity::bottom_right: y = parentSize.y-size.y-margin.w; break;
|
||||
default: break;
|
||||
}
|
||||
return glm::vec2(x, y);
|
||||
});
|
||||
|
||||
if (parent) {
|
||||
reposition();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#ifndef GRAPHICS_UI_ELEMENTS_UINODE_H_
|
||||
#define GRAPHICS_UI_ELEMENTS_UINODE_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "../../../delegates.h"
|
||||
#include "../../../window/input.h"
|
||||
|
||||
class GfxContext;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
class UINode;
|
||||
class GUI;
|
||||
|
||||
using onaction = std::function<void(GUI*)>;
|
||||
using onnumberchange = std::function<void(GUI*, double)>;
|
||||
|
||||
enum class Align {
|
||||
left, center, right,
|
||||
top=left, bottom=right,
|
||||
};
|
||||
|
||||
enum class Gravity {
|
||||
none,
|
||||
|
||||
top_left,
|
||||
top_center,
|
||||
top_right,
|
||||
|
||||
center_left,
|
||||
center_center,
|
||||
center_right,
|
||||
|
||||
bottom_left,
|
||||
bottom_center,
|
||||
bottom_right
|
||||
};
|
||||
|
||||
/// @brief Base abstract class for all UI elements
|
||||
class UINode {
|
||||
/// @brief element identifier used for direct access in UiDocument
|
||||
std::string id = "";
|
||||
protected:
|
||||
/// @brief element position within the parent element
|
||||
glm::vec2 pos {0.0f};
|
||||
/// @brief element size (width, height)
|
||||
glm::vec2 size;
|
||||
/// @brief minimal element size
|
||||
glm::vec2 minSize {1.0f};
|
||||
/// @brief element primary color (background-color or text-color if label)
|
||||
glm::vec4 color {1.0f};
|
||||
/// @brief element color when mouse is over it
|
||||
glm::vec4 hoverColor {1.0f};
|
||||
/// @brief element margin (only supported for Panel sub-nodes)
|
||||
glm::vec4 margin {1.0f};
|
||||
/// @brief is element visible
|
||||
bool visible = true;
|
||||
/// @brief is mouse over the element
|
||||
bool hover = false;
|
||||
/// @brief is mouse has been pressed over the element and not released yet
|
||||
bool pressed = false;
|
||||
/// @brief is element focused
|
||||
bool focused = false;
|
||||
/// @brief is element opaque for cursor interaction
|
||||
bool interactive = true;
|
||||
/// @brief does the element support resizing by parent elements
|
||||
bool resizing = true;
|
||||
/// @brief z-index property specifies the stack order of an element
|
||||
int zindex = 0;
|
||||
/// @brief element content alignment (supported by Label only)
|
||||
Align align = Align::left;
|
||||
/// @brief parent element
|
||||
UINode* parent = nullptr;
|
||||
/// @brief position supplier for the element (called on parent element size update)
|
||||
vec2supplier positionfunc = nullptr;
|
||||
|
||||
UINode(glm::vec2 size);
|
||||
public:
|
||||
virtual ~UINode();
|
||||
|
||||
/// @brief Called every frame for all visible elements
|
||||
/// @param delta delta timУ
|
||||
virtual void act(float delta) {};
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) = 0;
|
||||
|
||||
virtual void setVisible(bool flag);
|
||||
bool isVisible() const;
|
||||
|
||||
virtual void setAlign(Align align);
|
||||
Align getAlign() const;
|
||||
|
||||
virtual void setHover(bool flag);
|
||||
bool isHover() const;
|
||||
|
||||
virtual void setParent(UINode* node);
|
||||
UINode* getParent() const;
|
||||
|
||||
/// @brief Set element color (doesn't affect inner elements).
|
||||
/// Also replaces hover color to avoid adding extra properties
|
||||
virtual void setColor(glm::vec4 newColor);
|
||||
|
||||
/// @brief Get element color
|
||||
/// @return (float R,G,B,A in range [0.0, 1.0])
|
||||
glm::vec4 getColor() const;
|
||||
|
||||
virtual void setHoverColor(glm::vec4 newColor);
|
||||
glm::vec4 getHoverColor() const;
|
||||
|
||||
virtual void setMargin(glm::vec4 margin);
|
||||
glm::vec4 getMargin() const;
|
||||
|
||||
/// @brief Specifies the stack order of an element
|
||||
/// @attention Is not supported by Panel
|
||||
virtual void setZIndex(int idx);
|
||||
|
||||
/// @brief Get element z-index
|
||||
int getZIndex() const;
|
||||
|
||||
virtual void onFocus(GUI*) {focused = true;}
|
||||
virtual void click(GUI*, int x, int y);
|
||||
virtual void clicked(GUI*, mousecode button) {}
|
||||
virtual void mouseMove(GUI*, int x, int y) {};
|
||||
virtual void mouseRelease(GUI*, int x, int y);
|
||||
virtual void scrolled(int value);
|
||||
|
||||
bool isPressed() const;
|
||||
void defocus();
|
||||
bool isFocused() const;
|
||||
|
||||
/** Check if element catches all user input when focused */
|
||||
virtual bool isFocuskeeper() const {return false;}
|
||||
|
||||
virtual void typed(unsigned int codepoint) {};
|
||||
virtual void keyPressed(keycode key) {};
|
||||
|
||||
/** Check if screen position is inside of the element
|
||||
* @param pos screen position */
|
||||
virtual bool isInside(glm::vec2 pos);
|
||||
|
||||
/** Get element under the cursor.
|
||||
* @param pos cursor screen position
|
||||
* @param self shared pointer to element
|
||||
* @return self, sub-element or nullptr if element is not interractive */
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self);
|
||||
|
||||
/* Check if element is opaque for cursor */
|
||||
virtual bool isInteractive() const;
|
||||
/* Make the element opaque (true) or transparent (false) for cursor */
|
||||
virtual void setInteractive(bool flag);
|
||||
|
||||
virtual void setResizing(bool flag);
|
||||
virtual bool isResizing() const;
|
||||
|
||||
/* Get inner content offset. Used for scroll */
|
||||
virtual glm::vec2 contentOffset() {return glm::vec2(0.0f);};
|
||||
/* Calculate screen position of the element */
|
||||
virtual glm::vec2 calcPos() const;
|
||||
virtual void setPos(glm::vec2 pos);
|
||||
virtual glm::vec2 getPos() const;
|
||||
virtual glm::vec2 getSize() const;
|
||||
virtual void setSize(glm::vec2 size);
|
||||
virtual glm::vec2 getMinSize() const;
|
||||
virtual void setMinSize(glm::vec2 size);
|
||||
/* Called in containers when new element added */
|
||||
virtual void refresh() {};
|
||||
virtual void lock();
|
||||
|
||||
virtual vec2supplier getPositionFunc() const;
|
||||
virtual void setPositionFunc(vec2supplier);
|
||||
|
||||
void setId(const std::string& id);
|
||||
const std::string& getId() const;
|
||||
|
||||
/* Fetch pos from positionfunc if assigned */
|
||||
void reposition();
|
||||
|
||||
virtual void setGravity(Gravity gravity);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GRAPHICS_UI_ELEMENTS_UINODE_H_
|
||||
@@ -0,0 +1,300 @@
|
||||
#include "containers.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../../../window/Window.h"
|
||||
#include "../../../assets/Assets.h"
|
||||
#include "../../core/Batch2D.h"
|
||||
#include "../../core/GfxContext.h"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
Container::Container(glm::vec2 size) : UINode(size) {
|
||||
actualLength = size.y;
|
||||
setColor(glm::vec4());
|
||||
}
|
||||
|
||||
std::shared_ptr<UINode> Container::getAt(glm::vec2 pos, std::shared_ptr<UINode> self) {
|
||||
if (!interactive) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!isInside(pos)) return nullptr;
|
||||
|
||||
for (int i = nodes.size()-1; i >= 0; i--) {
|
||||
auto& node = nodes[i];
|
||||
if (!node->isVisible())
|
||||
continue;
|
||||
auto hover = node->getAt(pos, node);
|
||||
if (hover != nullptr) {
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
return UINode::getAt(pos, self);
|
||||
}
|
||||
|
||||
void Container::act(float delta) {
|
||||
for (auto node : nodes) {
|
||||
if (node->isVisible()) {
|
||||
node->act(delta);
|
||||
}
|
||||
}
|
||||
for (IntervalEvent& event : intervalEvents) {
|
||||
event.timer += delta;
|
||||
if (event.timer > event.interval) {
|
||||
event.callback();
|
||||
event.timer = fmod(event.timer, event.interval);
|
||||
if (event.repeat > 0) {
|
||||
event.repeat--;
|
||||
}
|
||||
}
|
||||
}
|
||||
intervalEvents.erase(std::remove_if(
|
||||
intervalEvents.begin(), intervalEvents.end(),
|
||||
[](const IntervalEvent& event) {
|
||||
return event.repeat == 0;
|
||||
}
|
||||
), intervalEvents.end());
|
||||
}
|
||||
|
||||
void Container::scrolled(int value) {
|
||||
int diff = (actualLength-getSize().y);
|
||||
if (scroll < 0 && diff <= 0) {
|
||||
scroll = 0;
|
||||
}
|
||||
if (diff > 0 && scrollable) {
|
||||
scroll += value * scrollStep;
|
||||
if (scroll > 0)
|
||||
scroll = 0;
|
||||
if (-scroll > diff) {
|
||||
scroll = -diff;
|
||||
}
|
||||
} else if (parent) {
|
||||
parent->scrolled(value);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::setScrollable(bool flag) {
|
||||
scrollable = flag;
|
||||
}
|
||||
|
||||
void Container::draw(const GfxContext* pctx, Assets* assets) {
|
||||
glm::vec2 pos = calcPos();
|
||||
glm::vec2 size = getSize();
|
||||
drawBackground(pctx, assets);
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->flush();
|
||||
{
|
||||
GfxContext ctx = pctx->sub();
|
||||
ctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
|
||||
for (auto node : nodes) {
|
||||
if (node->isVisible())
|
||||
node->draw(pctx, assets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Container::drawBackground(const GfxContext* pctx, Assets* assets) {
|
||||
if (color.a <= 0.0f)
|
||||
return;
|
||||
glm::vec2 pos = calcPos();
|
||||
|
||||
auto batch = pctx->getBatch2D();
|
||||
batch->texture(nullptr);
|
||||
batch->setColor(color);
|
||||
batch->rect(pos.x, pos.y, size.x, size.y);
|
||||
}
|
||||
|
||||
void Container::add(std::shared_ptr<UINode> node) {
|
||||
nodes.push_back(node);
|
||||
node->setParent(this);
|
||||
node->reposition();
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::add(std::shared_ptr<UINode> node, glm::vec2 pos) {
|
||||
node->setPos(pos);
|
||||
add(node);
|
||||
}
|
||||
|
||||
void Container::remove(std::shared_ptr<UINode> selected) {
|
||||
selected->setParent(nullptr);
|
||||
nodes.erase(std::remove_if(nodes.begin(), nodes.end(),
|
||||
[selected](const std::shared_ptr<UINode> node) {
|
||||
return node == selected;
|
||||
}
|
||||
), nodes.end());
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Container::listenInterval(float interval, ontimeout callback, int repeat) {
|
||||
intervalEvents.push_back({callback, interval, 0.0f, repeat});
|
||||
}
|
||||
|
||||
void Container::setSize(glm::vec2 size) {
|
||||
if (size == getSize()) {
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
UINode::setSize(size);
|
||||
refresh();
|
||||
for (auto& node : nodes) {
|
||||
node->reposition();
|
||||
}
|
||||
}
|
||||
|
||||
void Container::refresh() {
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](const auto& a, const auto& b) {
|
||||
return a->getZIndex() < b->getZIndex();
|
||||
});
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& Container::getNodes() const {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
Panel::Panel(glm::vec2 size, glm::vec4 padding, float interval)
|
||||
: Container(size),
|
||||
padding(padding),
|
||||
interval(interval)
|
||||
{
|
||||
setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.75f));
|
||||
}
|
||||
|
||||
Panel::~Panel() {
|
||||
}
|
||||
|
||||
void Panel::setMaxLength(int value) {
|
||||
maxLength = value;
|
||||
}
|
||||
|
||||
int Panel::getMaxLength() const {
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
void Panel::setPadding(glm::vec4 padding) {
|
||||
this->padding = padding;
|
||||
refresh();
|
||||
}
|
||||
|
||||
glm::vec4 Panel::getPadding() const {
|
||||
return padding;
|
||||
}
|
||||
|
||||
void Panel::cropToContent() {
|
||||
if (maxLength > 0.0f) {
|
||||
setSize(glm::vec2(getSize().x, glm::min(maxLength, actualLength)));
|
||||
} else {
|
||||
setSize(glm::vec2(getSize().x, actualLength));
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::add(std::shared_ptr<UINode> node) {
|
||||
Container::add(node);
|
||||
refresh();
|
||||
cropToContent();
|
||||
}
|
||||
|
||||
void Panel::refresh() {
|
||||
UINode::refresh();
|
||||
float x = padding.x;
|
||||
float y = padding.y;
|
||||
glm::vec2 size = getSize();
|
||||
if (orientation == Orientation::vertical) {
|
||||
float maxw = size.x;
|
||||
for (auto& node : nodes) {
|
||||
glm::vec2 nodesize = node->getSize();
|
||||
const glm::vec4 margin = node->getMargin();
|
||||
y += margin.y;
|
||||
|
||||
float ex = x + margin.x;
|
||||
node->setPos(glm::vec2(ex, y));
|
||||
y += nodesize.y + margin.w + interval;
|
||||
|
||||
float width = size.x - padding.x - padding.z - margin.x - margin.z;
|
||||
if (node->isResizing()) {
|
||||
node->setSize(glm::vec2(width, nodesize.y));
|
||||
}
|
||||
node->refresh();
|
||||
maxw = fmax(maxw, ex+node->getSize().x+margin.z+padding.z);
|
||||
}
|
||||
actualLength = y + padding.w;
|
||||
} else {
|
||||
float maxh = size.y;
|
||||
for (auto& node : nodes) {
|
||||
glm::vec2 nodesize = node->getSize();
|
||||
const glm::vec4 margin = node->getMargin();
|
||||
x += margin.x;
|
||||
node->setPos(glm::vec2(x, y+margin.y));
|
||||
x += nodesize.x + margin.z + interval;
|
||||
|
||||
node->refresh();
|
||||
maxh = fmax(maxh, y+margin.y+node->getSize().y+margin.w+padding.w);
|
||||
}
|
||||
actualLength = size.y;
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::setOrientation(Orientation orientation) {
|
||||
this->orientation = orientation;
|
||||
}
|
||||
|
||||
Orientation Panel::getOrientation() const {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
PagesControl::PagesControl() : Container(glm::vec2(1)){
|
||||
}
|
||||
|
||||
bool PagesControl::has(std::string name) {
|
||||
return pages.find(name) != pages.end();
|
||||
}
|
||||
|
||||
void PagesControl::addPage(std::string name, std::shared_ptr<UINode> panel) {
|
||||
pages[name] = Page{panel};
|
||||
}
|
||||
|
||||
void PagesControl::setPage(std::string name, bool history) {
|
||||
auto found = pages.find(name);
|
||||
if (found == pages.end()) {
|
||||
throw std::runtime_error("no page found");
|
||||
}
|
||||
if (current.panel) {
|
||||
Container::remove(current.panel);
|
||||
}
|
||||
if (history) {
|
||||
pageStack.push(curname);
|
||||
}
|
||||
curname = name;
|
||||
current = found->second;
|
||||
Container::add(current.panel);
|
||||
setSize(current.panel->getSize());
|
||||
}
|
||||
|
||||
void PagesControl::back() {
|
||||
if (pageStack.empty())
|
||||
return;
|
||||
std::string name = pageStack.top();
|
||||
pageStack.pop();
|
||||
setPage(name, false);
|
||||
}
|
||||
|
||||
Page& PagesControl::getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
void PagesControl::clearHistory() {
|
||||
pageStack = std::stack<std::string>();
|
||||
}
|
||||
|
||||
void PagesControl::reset() {
|
||||
clearHistory();
|
||||
if (current.panel) {
|
||||
curname = "";
|
||||
Container::remove(current.panel);
|
||||
current = Page{nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef GRAPHICS_UI_ELEMENTS_CONTAINERS_H_
|
||||
#define GRAPHICS_UI_ELEMENTS_CONTAINERS_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "UINode.h"
|
||||
|
||||
class Batch2D;
|
||||
class Assets;
|
||||
|
||||
namespace gui {
|
||||
using ontimeout = std::function<void()>;
|
||||
|
||||
struct IntervalEvent {
|
||||
ontimeout callback;
|
||||
float interval;
|
||||
float timer;
|
||||
// -1 - infinity, 1 - one time event
|
||||
int repeat;
|
||||
};
|
||||
|
||||
enum class Orientation { vertical, horizontal };
|
||||
|
||||
class Container : public UINode {
|
||||
protected:
|
||||
std::vector<std::shared_ptr<UINode>> nodes;
|
||||
std::vector<IntervalEvent> intervalEvents;
|
||||
int scroll = 0;
|
||||
int scrollStep = 40;
|
||||
int actualLength = 0;
|
||||
bool scrollable = true;
|
||||
public:
|
||||
Container(glm::vec2 size);
|
||||
|
||||
virtual void act(float delta) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets);
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
|
||||
virtual void add(std::shared_ptr<UINode> node);
|
||||
virtual void add(std::shared_ptr<UINode> node, glm::vec2 pos);
|
||||
virtual void remove(std::shared_ptr<UINode> node);
|
||||
virtual void scrolled(int value) override;
|
||||
virtual void setScrollable(bool flag);
|
||||
void listenInterval(float interval, ontimeout callback, int repeat=-1);
|
||||
virtual glm::vec2 contentOffset() override {return glm::vec2(0.0f, scroll);};
|
||||
virtual void setSize(glm::vec2 size) override;
|
||||
virtual void refresh() override;
|
||||
|
||||
const std::vector<std::shared_ptr<UINode>>& getNodes() const;
|
||||
};
|
||||
|
||||
class Panel : public Container {
|
||||
protected:
|
||||
Orientation orientation = Orientation::vertical;
|
||||
glm::vec4 padding {2.0f};
|
||||
float interval = 2.0f;
|
||||
int maxLength = 0;
|
||||
public:
|
||||
Panel(
|
||||
glm::vec2 size,
|
||||
glm::vec4 padding=glm::vec4(2.0f),
|
||||
float interval=2.0f
|
||||
);
|
||||
virtual ~Panel();
|
||||
|
||||
virtual void cropToContent();
|
||||
|
||||
virtual void setOrientation(Orientation orientation);
|
||||
Orientation getOrientation() const;
|
||||
|
||||
virtual void add(std::shared_ptr<UINode> node) override;
|
||||
|
||||
virtual void refresh() override;
|
||||
|
||||
virtual void setMaxLength(int value);
|
||||
int getMaxLength() const;
|
||||
|
||||
virtual void setPadding(glm::vec4 padding);
|
||||
glm::vec4 getPadding() const;
|
||||
};
|
||||
|
||||
struct Page {
|
||||
std::shared_ptr<UINode> panel = nullptr;
|
||||
|
||||
~Page() {
|
||||
panel = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class PagesControl : public Container {
|
||||
protected:
|
||||
std::unordered_map<std::string, Page> pages;
|
||||
std::stack<std::string> pageStack;
|
||||
Page current;
|
||||
std::string curname = "";
|
||||
public:
|
||||
PagesControl();
|
||||
|
||||
bool has(std::string name);
|
||||
void setPage(std::string name, bool history=true);
|
||||
void addPage(std::string name, std::shared_ptr<UINode> panel);
|
||||
void back();
|
||||
void clearHistory();
|
||||
void reset();
|
||||
|
||||
Page& getCurrent();
|
||||
};
|
||||
}
|
||||
#endif // GRAPHICS_UI_ELEMENTS_CONTAINERS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,390 @@
|
||||
#ifndef GRAPHICS_UI_ELEMENTS_CONTROLS_H_
|
||||
#define GRAPHICS_UI_ELEMENTS_CONTROLS_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "../GUI.h"
|
||||
#include "UINode.h"
|
||||
#include "containers.h"
|
||||
#include "../../../window/input.h"
|
||||
#include "../../../delegates.h"
|
||||
#include "../../../typedefs.h"
|
||||
|
||||
class Batch2D;
|
||||
class Assets;
|
||||
class Font;
|
||||
|
||||
namespace gui {
|
||||
class Label : public UINode {
|
||||
protected:
|
||||
std::wstring text;
|
||||
std::string fontName;
|
||||
wstringsupplier supplier = nullptr;
|
||||
uint lines = 1;
|
||||
float lineInterval = 1.5f;
|
||||
Align valign = Align::center;
|
||||
|
||||
bool multiline = false;
|
||||
|
||||
// runtime values
|
||||
|
||||
/// @brief Text Y offset relative to label position
|
||||
/// (last calculated alignment)
|
||||
int textYOffset = 0;
|
||||
|
||||
/// @brief Text line height multiplied by line interval
|
||||
int totalLineHeight = 1;
|
||||
public:
|
||||
Label(std::string text, std::string fontName="normal");
|
||||
Label(std::wstring text, std::string fontName="normal");
|
||||
|
||||
virtual void setText(std::wstring text);
|
||||
const std::wstring& getText() const;
|
||||
|
||||
virtual void setFontName(std::string name);
|
||||
virtual const std::string& getFontName() const;
|
||||
|
||||
/// @brief Set text vertical alignment (default value: center)
|
||||
/// @param align Align::top / Align::center / Align::bottom
|
||||
virtual void setVerticalAlign(Align align);
|
||||
virtual Align getVerticalAlign() const;
|
||||
|
||||
/// @brief Get line height multiplier used for multiline labels
|
||||
/// (default value: 1.5)
|
||||
virtual float getLineInterval() const;
|
||||
|
||||
/// @brief Set line height multiplier used for multiline labels
|
||||
virtual void setLineInterval(float interval);
|
||||
|
||||
/// @brief Get Y position of the text relative to label position
|
||||
/// @return Y offset
|
||||
virtual int getTextYOffset() const;
|
||||
|
||||
/// @brief Get Y position of the line relative to label position
|
||||
/// @param line target line index
|
||||
/// @return Y offset
|
||||
virtual int getLineYOffset(uint line) const;
|
||||
|
||||
/// @brief Get position of line start in the text
|
||||
/// @param line target line index
|
||||
/// @return position in the text [0..length]
|
||||
virtual size_t getTextLineOffset(uint line) const;
|
||||
|
||||
/// @brief Get line index by its Y offset relative to label position
|
||||
/// @param offset target Y offset
|
||||
/// @return line index [0..+]
|
||||
virtual uint getLineByYOffset(int offset) const;
|
||||
virtual uint getLineByTextIndex(size_t index) const;
|
||||
virtual uint getLinesNumber() const;
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void textSupplier(wstringsupplier supplier);
|
||||
|
||||
virtual void setMultiline(bool multiline);
|
||||
virtual bool isMultiline() const;
|
||||
};
|
||||
|
||||
class Image : public UINode {
|
||||
protected:
|
||||
std::string texture;
|
||||
bool autoresize = false;
|
||||
public:
|
||||
Image(std::string texture, glm::vec2 size=glm::vec2(32,32));
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void setAutoResize(bool flag);
|
||||
virtual bool isAutoResize() const;
|
||||
};
|
||||
|
||||
class Button : public Panel {
|
||||
protected:
|
||||
glm::vec4 pressedColor {0.0f, 0.0f, 0.0f, 0.95f};
|
||||
std::vector<onaction> actions;
|
||||
std::shared_ptr<Label> label = nullptr;
|
||||
public:
|
||||
Button(std::shared_ptr<UINode> content,
|
||||
glm::vec4 padding=glm::vec4(2.0f));
|
||||
|
||||
Button(std::wstring text,
|
||||
glm::vec4 padding,
|
||||
onaction action,
|
||||
glm::vec2 size=glm::vec2(-1));
|
||||
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual Button* listenAction(onaction action);
|
||||
|
||||
virtual Align getTextAlign() const;
|
||||
virtual void setTextAlign(Align align);
|
||||
|
||||
virtual void setText(std::wstring text);
|
||||
virtual std::wstring getText() const;
|
||||
|
||||
virtual glm::vec4 getPressedColor() const;
|
||||
virtual void setPressedColor(glm::vec4 color);
|
||||
|
||||
virtual Button* textSupplier(wstringsupplier supplier);
|
||||
|
||||
virtual void refresh() override;
|
||||
};
|
||||
|
||||
class RichButton : public Container {
|
||||
protected:
|
||||
glm::vec4 pressedColor {0.0f, 0.0f, 0.0f, 0.95f};
|
||||
std::vector<onaction> actions;
|
||||
public:
|
||||
RichButton(glm::vec2 size);
|
||||
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
virtual RichButton* listenAction(onaction action);
|
||||
};
|
||||
|
||||
class TextBox : public Panel {
|
||||
protected:
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glm::vec4 invalidColor {0.1f, 0.05f, 0.03f, 1.0f};
|
||||
std::shared_ptr<Label> label;
|
||||
std::wstring input;
|
||||
std::wstring placeholder;
|
||||
wstringsupplier supplier = nullptr;
|
||||
wstringconsumer consumer = nullptr;
|
||||
wstringchecker validator = nullptr;
|
||||
runnable onEditStart = nullptr;
|
||||
bool valid = true;
|
||||
/// @brief text input pointer, value may be greather than text length
|
||||
uint caret = 0;
|
||||
/// @brief actual local (line) position of the caret on vertical move
|
||||
uint maxLocalCaret = 0;
|
||||
uint textOffset = 0;
|
||||
int textInitX;
|
||||
/// @brief last time of the caret was moved (used for blink animation)
|
||||
double caretLastMove = 0.0;
|
||||
Font* font = nullptr;
|
||||
|
||||
size_t selectionStart = 0;
|
||||
size_t selectionEnd = 0;
|
||||
size_t selectionOrigin = 0;
|
||||
|
||||
bool multiline = false;
|
||||
bool editable = true;
|
||||
|
||||
size_t normalizeIndex(int index);
|
||||
|
||||
int calcIndexAt(int x, int y) const;
|
||||
void paste(const std::wstring& text);
|
||||
void setTextOffset(uint x);
|
||||
void erase(size_t start, size_t length);
|
||||
bool eraseSelected();
|
||||
void resetSelection();
|
||||
void extendSelection(int index);
|
||||
size_t getLineLength(uint line) const;
|
||||
|
||||
/// @brief Get total length of the selection
|
||||
size_t getSelectionLength() const;
|
||||
|
||||
/// @brief Set maxLocalCaret to local (line) caret position
|
||||
void resetMaxLocalCaret();
|
||||
|
||||
void performEditingKeyboardEvents(keycode key);
|
||||
public:
|
||||
TextBox(
|
||||
std::wstring placeholder,
|
||||
glm::vec4 padding=glm::vec4(4.0f)
|
||||
);
|
||||
|
||||
virtual void setTextSupplier(wstringsupplier supplier);
|
||||
|
||||
/// @brief Consumer called on stop editing text (textbox defocus)
|
||||
/// @param consumer std::wstring consumer function
|
||||
virtual void setTextConsumer(wstringconsumer consumer);
|
||||
|
||||
/// @brief Text validator called while text editing and returns true if
|
||||
/// text is valid
|
||||
/// @param validator std::wstring consumer returning boolean
|
||||
virtual void setTextValidator(wstringchecker validator);
|
||||
|
||||
virtual void setFocusedColor(glm::vec4 color);
|
||||
virtual glm::vec4 getFocusedColor() const;
|
||||
|
||||
/// @brief Set color of textbox marked by validator as invalid
|
||||
virtual void setErrorColor(glm::vec4 color);
|
||||
|
||||
/// @brief Get color of textbox marked by validator as invalid
|
||||
virtual glm::vec4 getErrorColor() const;
|
||||
|
||||
/// @brief Get TextBox content text or placeholder if empty
|
||||
virtual std::wstring getText() const;
|
||||
|
||||
/// @brief Set TextBox content text
|
||||
virtual void setText(std::wstring value);
|
||||
|
||||
/// @brief Get text placeholder
|
||||
virtual std::wstring getPlaceholder() const;
|
||||
|
||||
/// @brief Set text placeholder
|
||||
/// @param text will be used instead of empty
|
||||
virtual void setPlaceholder(const std::wstring& text);
|
||||
|
||||
/// @brief Get selected text
|
||||
virtual std::wstring getSelection() const;
|
||||
|
||||
/// @brief Get current caret position in text
|
||||
/// @return integer in range [0, text.length()]
|
||||
virtual uint getCaret() const;
|
||||
|
||||
/// @brief Set caret position in the text
|
||||
/// @param position integer in range [0, text.length()]
|
||||
virtual void setCaret(uint position);
|
||||
|
||||
/// @brief Select part of the text
|
||||
/// @param start index of the first selected character
|
||||
/// @param end index of the last selected character + 1
|
||||
virtual void select(int start, int end);
|
||||
|
||||
/// @brief Check text with validator set with setTextValidator
|
||||
/// @return true if text is valid
|
||||
virtual bool validate();
|
||||
|
||||
virtual void setValid(bool valid);
|
||||
virtual bool isValid() const;
|
||||
|
||||
/// @brief Enable/disable multiline mode
|
||||
virtual void setMultiline(bool multiline);
|
||||
|
||||
/// @brief Check if multiline mode is enabled
|
||||
virtual bool isMultiline() const;
|
||||
|
||||
/// @brief Enable/disable text editing feature
|
||||
virtual void setEditable(bool editable);
|
||||
|
||||
/// @brief Check if text editing feature is enabled
|
||||
virtual bool isEditable() const;
|
||||
|
||||
/// @brief Set runnable called on textbox focus
|
||||
virtual void setOnEditStart(runnable oneditstart);
|
||||
|
||||
virtual void onFocus(GUI*) override;
|
||||
virtual void refresh() override;
|
||||
virtual void click(GUI*, int, int) override;
|
||||
virtual void mouseMove(GUI*, int x, int y) override;
|
||||
virtual bool isFocuskeeper() const override {return true;}
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
virtual void typed(unsigned int codepoint) override;
|
||||
virtual void keyPressed(keycode key) override;
|
||||
virtual std::shared_ptr<UINode> getAt(glm::vec2 pos, std::shared_ptr<UINode> self) override;
|
||||
};
|
||||
|
||||
class InputBindBox : public Panel {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f};
|
||||
glm::vec4 focusedColor {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
std::shared_ptr<Label> label;
|
||||
Binding& binding;
|
||||
public:
|
||||
InputBindBox(Binding& binding, glm::vec4 padding=glm::vec4(6.0f));
|
||||
virtual void drawBackground(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void clicked(GUI*, mousecode button) override;
|
||||
virtual void keyPressed(keycode key) override;
|
||||
virtual bool isFocuskeeper() const override {return true;}
|
||||
};
|
||||
|
||||
class TrackBar : public UINode {
|
||||
protected:
|
||||
glm::vec4 trackColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
doublesupplier supplier = nullptr;
|
||||
doubleconsumer consumer = nullptr;
|
||||
double min;
|
||||
double max;
|
||||
double value;
|
||||
double step;
|
||||
int trackWidth;
|
||||
public:
|
||||
TrackBar(double min,
|
||||
double max,
|
||||
double value,
|
||||
double step=1.0,
|
||||
int trackWidth=1);
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void setSupplier(doublesupplier supplier);
|
||||
virtual void setConsumer(doubleconsumer consumer);
|
||||
|
||||
virtual void mouseMove(GUI*, int x, int y) override;
|
||||
|
||||
virtual double getValue() const;
|
||||
virtual double getMin() const;
|
||||
virtual double getMax() const;
|
||||
virtual double getStep() const;
|
||||
virtual int getTrackWidth() const;
|
||||
virtual glm::vec4 getTrackColor() const;
|
||||
|
||||
virtual void setValue(double);
|
||||
virtual void setMin(double);
|
||||
virtual void setMax(double);
|
||||
virtual void setStep(double);
|
||||
virtual void setTrackWidth(int);
|
||||
virtual void setTrackColor(glm::vec4);
|
||||
};
|
||||
|
||||
class CheckBox : public UINode {
|
||||
protected:
|
||||
glm::vec4 hoverColor {0.05f, 0.1f, 0.2f, 0.75f};
|
||||
glm::vec4 checkColor {1.0f, 1.0f, 1.0f, 0.4f};
|
||||
boolsupplier supplier = nullptr;
|
||||
boolconsumer consumer = nullptr;
|
||||
bool checked = false;
|
||||
public:
|
||||
CheckBox(bool checked=false);
|
||||
|
||||
virtual void draw(const GfxContext* pctx, Assets* assets) override;
|
||||
|
||||
virtual void mouseRelease(GUI*, int x, int y) override;
|
||||
|
||||
virtual void setSupplier(boolsupplier supplier);
|
||||
virtual void setConsumer(boolconsumer consumer);
|
||||
|
||||
virtual CheckBox* setChecked(bool flag);
|
||||
|
||||
virtual bool isChecked() const {
|
||||
if (supplier)
|
||||
return supplier();
|
||||
return checked;
|
||||
}
|
||||
};
|
||||
|
||||
class FullCheckBox : public Panel {
|
||||
protected:
|
||||
std::shared_ptr<CheckBox> checkbox;
|
||||
public:
|
||||
FullCheckBox(std::wstring text, glm::vec2 size, bool checked=false);
|
||||
|
||||
virtual void setSupplier(boolsupplier supplier) {
|
||||
checkbox->setSupplier(supplier);
|
||||
}
|
||||
|
||||
virtual void setConsumer(boolconsumer consumer) {
|
||||
checkbox->setConsumer(consumer);
|
||||
}
|
||||
|
||||
virtual void setChecked(bool flag) {
|
||||
checkbox->setChecked(flag);
|
||||
}
|
||||
|
||||
virtual bool isChecked() const {
|
||||
return checkbox->isChecked();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GRAPHICS_UI_ELEMENTS_CONTROLS_H_
|
||||
Reference in New Issue
Block a user