Expression Parsing
parent
d8dac4d74e
commit
1ec7598ac0
|
@ -331,6 +331,6 @@ add_library(Zepha_Core
|
||||||
client/gui/compound/GuiCellGraph.cpp
|
client/gui/compound/GuiCellGraph.cpp
|
||||||
client/gui/compound/GuiCellGraph.h
|
client/gui/compound/GuiCellGraph.h
|
||||||
client/gui/basic/GuiCells.cpp
|
client/gui/basic/GuiCells.cpp
|
||||||
client/gui/basic/GuiCells.h client/gui/Gui.h client/gui/Root.cpp client/gui/Root.h client/gui/BoxElement.cpp client/gui/BoxElement.h client/gui/Gui.cpp client/gui/Style.h)
|
client/gui/basic/GuiCells.h client/gui/Gui.h client/gui/Root.cpp client/gui/Root.h client/gui/BoxElement.cpp client/gui/BoxElement.h client/gui/Gui.cpp client/gui/Style.h client/gui/TextElement.cpp client/gui/TextElement.h client/gui/Expression.cpp client/gui/Expression.h)
|
||||||
|
|
||||||
target_include_directories(Zepha_Core PUBLIC .)
|
target_include_directories(Zepha_Core PUBLIC .)
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "client/gui/Root.h"
|
#include "client/gui/Root.h"
|
||||||
#include "client/graph/Model.h"
|
#include "client/graph/Model.h"
|
||||||
#include "game/atlas/asset/AtlasRef.h"
|
|
||||||
#include "client/graph/mesh/EntityMesh.h"
|
#include "client/graph/mesh/EntityMesh.h"
|
||||||
|
|
||||||
void Gui::BoxElement::updateElement() {
|
void Gui::BoxElement::updateElement() {
|
||||||
|
@ -20,7 +19,6 @@ void Gui::BoxElement::updateElement() {
|
||||||
}
|
}
|
||||||
curBg = rawBg;
|
curBg = rawBg;
|
||||||
|
|
||||||
|
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
const let bgColor = getStyle<vec4, ValueType::COLOR>(bgRule);
|
const let bgColor = getStyle<vec4, ValueType::COLOR>(bgRule);
|
||||||
const string bgImage = getStyle<string>(bgRule, "");
|
const string bgImage = getStyle<string>(bgRule, "");
|
||||||
|
@ -50,8 +48,10 @@ void Gui::BoxElement::updateElement() {
|
||||||
entity.setModel(model);
|
entity.setModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
entity.setScale(vec3(getComputedSize() * static_cast<i32>(PX_SCALE), 0));
|
let margin = getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
|
||||||
entity.setPos(vec3(getComputedScreenPos() * static_cast<i32>(PX_SCALE), 0));
|
|
||||||
|
entity.setScale(vec3(getComputedSize(), 0));
|
||||||
|
entity.setPos(vec3(getComputedScreenPos() + ivec2 { margin.x, margin.y }, 0));
|
||||||
|
|
||||||
Element::updateElement();
|
Element::updateElement();
|
||||||
}
|
}
|
|
@ -1,8 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "Element.h"
|
#include "Element.h"
|
||||||
|
|
||||||
#include "game/atlas/asset/AtlasRef.h"
|
#include "game/atlas/asset/AtlasRef.h"
|
||||||
|
|
||||||
namespace Gui {
|
namespace Gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple box element that may have background and/or children.
|
||||||
|
*/
|
||||||
|
|
||||||
class BoxElement: public Element {
|
class BoxElement: public Element {
|
||||||
public:
|
public:
|
||||||
using Element::Element;
|
using Element::Element;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Element.h"
|
#include "Element.h"
|
||||||
|
|
||||||
#include "util/Util.h"
|
#include "util/Util.h"
|
||||||
|
#include "client/gui/Root.h"
|
||||||
#include "client/graph/Renderer.h"
|
#include "client/graph/Renderer.h"
|
||||||
|
|
||||||
Gui::Element::~Element() {
|
Gui::Element::~Element() {
|
||||||
|
@ -17,51 +18,55 @@ void Gui::Element::setStyle(StyleRule style, const std::any& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Gui::Element::getComputedSize() {
|
ivec2 Gui::Element::getComputedSize() {
|
||||||
return {
|
let size = getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, glm::max(layoutSize, 0));
|
||||||
getStyle<i32, ValueType::LENGTH>(StyleRule::WIDTH, std::max(layoutSize.x, 0)),
|
return size;
|
||||||
getStyle<i32, ValueType::LENGTH>(StyleRule::HEIGHT, std::max(layoutSize.y, 0))
|
}
|
||||||
};
|
|
||||||
|
ivec2 Gui::Element::getComputedOuterSize() {
|
||||||
|
let size = getComputedSize();
|
||||||
|
let margin = getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
|
||||||
|
return ivec2 { size.x + margin.x + margin.z, size.y + margin.y + margin.w };
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Gui::Element::getComputedContentSize() {
|
ivec2 Gui::Element::getComputedContentSize() {
|
||||||
let size = getComputedSize();
|
let size = getComputedSize();
|
||||||
let padding = getStyle<ivec4>(StyleRule::PADDING, {});
|
let padding = getStyle<ivec4, ValueType::LENGTH>(StyleRule::PADDING, {});
|
||||||
return glm::max(ivec2 { size.x - padding.x - padding.z, size.y - padding.y - padding.w }, ivec2 {});
|
return glm::max(ivec2 { size.x - padding.x - padding.z, size.y - padding.y - padding.w }, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Gui::Element::getExplicitSize() {
|
ivec2 Gui::Element::getExplicitSize() {
|
||||||
return {
|
return getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, ivec2(-1));
|
||||||
getStyle<i32, ValueType::LENGTH>(StyleRule::WIDTH, -1),
|
|
||||||
getStyle<i32, ValueType::LENGTH>(StyleRule::HEIGHT, -1)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Gui::Element::getComputedPos() {
|
ivec2 Gui::Element::getComputedPos() {
|
||||||
return {
|
return getStyle<ivec2, ValueType::LENGTH>(StyleRule::POS, layoutPosition);
|
||||||
getStyle<i32, ValueType::LENGTH>(StyleRule::LEFT, layoutPosition.x),
|
|
||||||
getStyle<i32, ValueType::LENGTH>(StyleRule::TOP, layoutPosition.y)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 Gui::Element::getComputedScreenPos() {
|
ivec2 Gui::Element::getComputedScreenPos() {
|
||||||
return getComputedPos() + parentOffset;
|
return getComputedPos() + parentOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Gui::Element::handleMouseHover(ivec2 mousePos) {
|
bool Gui::Element::handleMouseHover(ivec2 mousePos, bool& pointer) {
|
||||||
bool childIntersects = false;
|
bool childIntersects = false;
|
||||||
for (let& child : children)
|
for (let& child : children)
|
||||||
if (child->handleMouseHover(mousePos)) childIntersects = true;
|
if (child->handleMouseHover(mousePos, pointer))
|
||||||
|
childIntersects = true;
|
||||||
|
|
||||||
if (childIntersects) {
|
if (childIntersects) {
|
||||||
hovered = false;
|
if (hovered) {
|
||||||
|
hovered = false;
|
||||||
|
updateElement();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec2 size = getComputedSize() * static_cast<i32>(PX_SCALE);
|
ivec2 size = getComputedSize();
|
||||||
ivec2 pos = getComputedScreenPos() * static_cast<i32>(PX_SCALE);
|
ivec2 pos = getComputedScreenPos();
|
||||||
bool intersects = mousePos.x >= pos.x && mousePos.x <= pos.x + size.x &&
|
bool intersects = mousePos.x >= pos.x && mousePos.x <= pos.x + size.x &&
|
||||||
mousePos.y >= pos.y && mousePos.y <= pos.y + size.y;
|
mousePos.y >= pos.y && mousePos.y <= pos.y + size.y;
|
||||||
|
|
||||||
|
if (intersects) pointer = getStyle<string>(StyleRule::CURSOR, "") == "pointer";
|
||||||
|
|
||||||
if (hovered != intersects) {
|
if (hovered != intersects) {
|
||||||
hovered = intersects;
|
hovered = intersects;
|
||||||
updateElement();
|
updateElement();
|
||||||
|
@ -116,8 +121,8 @@ void Gui::Element::layoutChildren() {
|
||||||
* The element gap across the primary axis.
|
* The element gap across the primary axis.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const i32 gap = getStyle<ivec2>(StyleRule::GAP, ivec2(0))[primary];
|
const i32 gap = getStyle<ivec2, ValueType::LENGTH>(StyleRule::GAP, ivec2(0))[primary];
|
||||||
const ivec4& padding = getStyle<ivec4>(StyleRule::PADDING, ivec4 {});
|
const ivec4& padding = getStyle<ivec4, ValueType::LENGTH>(StyleRule::PADDING, ivec4 {});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculates the explicit spaced used up by children across the primary axis,
|
* Calculates the explicit spaced used up by children across the primary axis,
|
||||||
|
@ -133,6 +138,8 @@ void Gui::Element::layoutChildren() {
|
||||||
let childExplicitSize = child->getExplicitSize();
|
let childExplicitSize = child->getExplicitSize();
|
||||||
if (childExplicitSize[primary] != -1) explicitSize += childExplicitSize[primary];
|
if (childExplicitSize[primary] != -1) explicitSize += childExplicitSize[primary];
|
||||||
else implicitCount++;
|
else implicitCount++;
|
||||||
|
let childMargin = child->getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
|
||||||
|
explicitSize += childMargin[primary] + childMargin[primary + 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,7 +150,7 @@ void Gui::Element::layoutChildren() {
|
||||||
if (align[primary] == 1) offset[primary] += selfSize[primary] - explicitSize - (gap * (children.size() - 1));
|
if (align[primary] == 1) offset[primary] += selfSize[primary] - explicitSize - (gap * (children.size() - 1));
|
||||||
else if (align[primary] == 0) offset[primary] += selfSize[primary] / 2 -
|
else if (align[primary] == 0) offset[primary] += selfSize[primary] / 2 -
|
||||||
explicitSize / 2 - (gap * (children.size() - 1)) / 2;
|
explicitSize / 2 - (gap * (children.size() - 1)) / 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The amount of size each implicitly sized element should occupy.
|
* The amount of size each implicitly sized element should occupy.
|
||||||
*/
|
*/
|
||||||
|
@ -159,6 +166,7 @@ void Gui::Element::layoutChildren() {
|
||||||
|
|
||||||
for (const let& child : children) {
|
for (const let& child : children) {
|
||||||
let childExplicitSize = child->getExplicitSize();
|
let childExplicitSize = child->getExplicitSize();
|
||||||
|
let childMargin = child->getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
|
||||||
|
|
||||||
child->layoutSize[primary] =
|
child->layoutSize[primary] =
|
||||||
(childExplicitSize[primary] == -1 && align[primary] == 2) ? implicitElemSize : 0;
|
(childExplicitSize[primary] == -1 && align[primary] == 2) ? implicitElemSize : 0;
|
||||||
|
@ -174,7 +182,8 @@ void Gui::Element::layoutChildren() {
|
||||||
child->layoutPosition[primary] = offset[primary];
|
child->layoutPosition[primary] = offset[primary];
|
||||||
|
|
||||||
offset[primary] += ((childExplicitSize[primary] == -1 && align[primary] == 2)
|
offset[primary] += ((childExplicitSize[primary] == -1 && align[primary] == 2)
|
||||||
? implicitElemSize : childExplicitSize[primary]) + gap;
|
? implicitElemSize : childExplicitSize[primary])
|
||||||
|
+ gap + childMargin[primary] + childMargin[primary + 2];
|
||||||
|
|
||||||
child->parentOffset = selfOffset;
|
child->parentOffset = selfOffset;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <any>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "client/gui/Gui.h"
|
#include "client/gui/Gui.h"
|
||||||
|
@ -15,6 +14,11 @@ class Renderer;
|
||||||
namespace Gui {
|
namespace Gui {
|
||||||
class Root;
|
class Root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all Gui Elements.
|
||||||
|
* Represents an element within a Gui Root, which may be drawn to the screen.
|
||||||
|
*/
|
||||||
|
|
||||||
class Element {
|
class Element {
|
||||||
friend class BoxElement;
|
friend class BoxElement;
|
||||||
|
|
||||||
|
@ -28,15 +32,20 @@ namespace Gui {
|
||||||
Element(Root& root, vec<StyleSheet>& stylesheets): root(root), stylesheets(stylesheets) {}
|
Element(Root& root, vec<StyleSheet>& stylesheets): root(root), stylesheets(stylesheets) {}
|
||||||
|
|
||||||
~Element();
|
~Element();
|
||||||
|
|
||||||
|
/** Sets the element's props to the struct specified. */
|
||||||
virtual void setProps(const Props& props);
|
virtual void setProps(const Props& props);
|
||||||
|
|
||||||
|
/** Sets a style rule on the element. */
|
||||||
virtual void setStyle(StyleRule style, const std::any& value);
|
virtual void setStyle(StyleRule style, const std::any& value);
|
||||||
|
|
||||||
|
/** Recalculates the element based on its props. Call when props or stylesheets change. */
|
||||||
virtual void updateElement();
|
virtual void updateElement();
|
||||||
|
|
||||||
|
/** Draws the element to the screen. */
|
||||||
virtual void draw(Renderer& renderer);
|
virtual void draw(Renderer& renderer);
|
||||||
|
|
||||||
|
/** Creates and prepends an element to this element. */
|
||||||
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
|
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
|
||||||
sptr<E> prepend(const Props& props = {}) {
|
sptr<E> prepend(const Props& props = {}) {
|
||||||
const let elem = make_shared<E>(root, stylesheets);
|
const let elem = make_shared<E>(root, stylesheets);
|
||||||
|
@ -45,6 +54,7 @@ namespace Gui {
|
||||||
return elem;
|
return elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Prepends an existing element to this element. */
|
||||||
sptr<Element> prepend(sptr<Element> elem) {
|
sptr<Element> prepend(sptr<Element> elem) {
|
||||||
children.push_front(elem);
|
children.push_front(elem);
|
||||||
elem->parent = this;
|
elem->parent = this;
|
||||||
|
@ -52,6 +62,7 @@ namespace Gui {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates and appends an element to this element. */
|
||||||
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
|
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
|
||||||
sptr<E> append(const Props& props = {}) {
|
sptr<E> append(const Props& props = {}) {
|
||||||
const let elem = make_shared<E>(root, stylesheets);
|
const let elem = make_shared<E>(root, stylesheets);
|
||||||
|
@ -60,6 +71,7 @@ namespace Gui {
|
||||||
return elem;
|
return elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Appends an existing element to this element. */
|
||||||
sptr<Element> append(sptr<Element> elem) {
|
sptr<Element> append(sptr<Element> elem) {
|
||||||
children.push_back(elem);
|
children.push_back(elem);
|
||||||
elem->parent = this;
|
elem->parent = this;
|
||||||
|
@ -67,12 +79,25 @@ namespace Gui {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the element's computed size. */
|
||||||
virtual ivec2 getComputedSize();
|
virtual ivec2 getComputedSize();
|
||||||
|
|
||||||
|
/** Returns the element's computed size + margins. */
|
||||||
|
virtual ivec2 getComputedOuterSize();
|
||||||
|
|
||||||
|
/** Returns the element's computed content size, which is its size - padding. */
|
||||||
virtual ivec2 getComputedContentSize();
|
virtual ivec2 getComputedContentSize();
|
||||||
|
|
||||||
|
/** Returns the element's explicit size. Unspecified dimensions are -1. */
|
||||||
virtual ivec2 getExplicitSize();
|
virtual ivec2 getExplicitSize();
|
||||||
|
|
||||||
|
/** Returns the element's computed position relative to its parent. */
|
||||||
virtual ivec2 getComputedPos();
|
virtual ivec2 getComputedPos();
|
||||||
|
|
||||||
|
/** Returns the element's computed position relative to the screen. */
|
||||||
virtual ivec2 getComputedScreenPos();
|
virtual ivec2 getComputedScreenPos();
|
||||||
|
|
||||||
|
/** Gets a style value from the element's styles or the root's stylesheets. */
|
||||||
const optional<any> getStyle(StyleRule rule) const {
|
const optional<any> getStyle(StyleRule rule) const {
|
||||||
const optional<any> opt = props.styles.get(rule);
|
const optional<any> opt = props.styles.get(rule);
|
||||||
if (opt) return *opt;
|
if (opt) return *opt;
|
||||||
|
@ -86,6 +111,7 @@ namespace Gui {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a style value from the element's styles or the root's stylesheets. */
|
||||||
template<typename V, ValueType T = ValueType::LITERAL>
|
template<typename V, ValueType T = ValueType::LITERAL>
|
||||||
const optional<V> getStyle(StyleRule rule) const {
|
const optional<V> getStyle(StyleRule rule) const {
|
||||||
const optional<V> opt = props.styles.get<V, T>(rule);
|
const optional<V> opt = props.styles.get<V, T>(rule);
|
||||||
|
@ -100,6 +126,7 @@ namespace Gui {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a style value from the element's styles or the root's stylesheets. */
|
||||||
template<typename V, ValueType T = ValueType::LITERAL>
|
template<typename V, ValueType T = ValueType::LITERAL>
|
||||||
const V getStyle(StyleRule rule, V def) const {
|
const V getStyle(StyleRule rule, V def) const {
|
||||||
const optional<V> opt = getStyle<V, T>(rule);
|
const optional<V> opt = getStyle<V, T>(rule);
|
||||||
|
@ -107,7 +134,18 @@ namespace Gui {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleMouseHover(ivec2 mousePos);
|
/**
|
||||||
|
* Called by the root when the mouse position changes.
|
||||||
|
* Returns a boolean if the element or its children are hovered.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool handleMouseHover(ivec2 mousePos, bool& pointer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the root when the mouse clicks.
|
||||||
|
* Triggers a click interaction on the hovered element.
|
||||||
|
*/
|
||||||
|
|
||||||
bool handleMouseClick(u32 button, bool down);
|
bool handleMouseClick(u32 button, bool down);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -121,10 +159,16 @@ namespace Gui {
|
||||||
|
|
||||||
bool hovered = false;
|
bool hovered = false;
|
||||||
|
|
||||||
|
/** The screen offset of the parent. */
|
||||||
ivec2 parentOffset {};
|
ivec2 parentOffset {};
|
||||||
|
|
||||||
|
/** The element's implicit size, as defined by the parent layout. */
|
||||||
ivec2 layoutSize { -1, -1 };
|
ivec2 layoutSize { -1, -1 };
|
||||||
|
|
||||||
|
/** The element's implicit position, as defined by the parent layout. */
|
||||||
ivec2 layoutPosition {};
|
ivec2 layoutPosition {};
|
||||||
|
|
||||||
|
/** Updates child sizes and offsets based on layout styles. */
|
||||||
virtual void layoutChildren();
|
virtual void layoutChildren();
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
#include <stack>
|
||||||
|
#include <math.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "client/gui/Expression.h"
|
||||||
|
|
||||||
|
#include "util/Util.h"
|
||||||
|
#include "Gui.h"
|
||||||
|
|
||||||
|
Gui::Expression::Expression(const string& exp) {
|
||||||
|
setExpression(exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gui::Expression::setExpression(string exp) {
|
||||||
|
// Avoid reparsing the same expression.
|
||||||
|
usize newHash = std::hash<string>{}(exp);
|
||||||
|
if (hash == newHash) return;
|
||||||
|
hash = newHash;
|
||||||
|
|
||||||
|
// Sanitize expression
|
||||||
|
exp.erase(std::remove_if(exp.begin(), exp.end(), isspace), exp.end());
|
||||||
|
|
||||||
|
// Process Infix into Postfix (RPN)
|
||||||
|
infix = {};
|
||||||
|
std::stack<char> operators {};
|
||||||
|
|
||||||
|
bool nextOperatorIsUnary = true;
|
||||||
|
|
||||||
|
String temp = {};
|
||||||
|
|
||||||
|
while (exp.size()) {
|
||||||
|
let& c = exp[0];
|
||||||
|
// Number or Unit or Keyword
|
||||||
|
if ((c >= '0' && c <= '9') || c == '.' || (c >= 97 && c <= 122) ||
|
||||||
|
(nextOperatorIsUnary && (c == '+' || c == '-'))) {
|
||||||
|
temp.v += c;
|
||||||
|
nextOperatorIsUnary = false;
|
||||||
|
}
|
||||||
|
// Binary Operator
|
||||||
|
else if (!nextOperatorIsUnary && (c == '+' || c == '-' || c == '*' || c == '/' || c == '^')) {
|
||||||
|
if (temp.v.size()) {
|
||||||
|
infix.emplace(temp);
|
||||||
|
temp = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (operators.size() && operators.top() != '(' &&
|
||||||
|
((c != '^' && PRECEDENCE.at(operators.top()) >= PRECEDENCE.at(c)) ||
|
||||||
|
PRECEDENCE.at(operators.top()) > PRECEDENCE.at(c))) {
|
||||||
|
|
||||||
|
infix.emplace(string(1, operators.top()));
|
||||||
|
operators.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
operators.emplace(c);
|
||||||
|
nextOperatorIsUnary = true;
|
||||||
|
}
|
||||||
|
// Opening Parentheses
|
||||||
|
else if (c == '(') {
|
||||||
|
if (temp.v.size()) {
|
||||||
|
infix.emplace(temp);
|
||||||
|
temp = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
operators.push(c);
|
||||||
|
nextOperatorIsUnary = true;
|
||||||
|
}
|
||||||
|
// Closing Parentheses
|
||||||
|
else if (c == ')') {
|
||||||
|
if (!temp.v.size()) throw std::logic_error("Empty or mismatched parentheses.");
|
||||||
|
infix.emplace(temp);
|
||||||
|
temp = {};
|
||||||
|
|
||||||
|
if (!operators.size()) throw std::logic_error("Mismatched parentheses.");
|
||||||
|
while (operators.top() != '(') {
|
||||||
|
infix.emplace(string(1, operators.top()));
|
||||||
|
operators.pop();
|
||||||
|
if (!operators.size()) throw std::logic_error("Mismatched parentheses.");
|
||||||
|
}
|
||||||
|
if (operators.top() != '(') throw std::logic_error("Mismatched parentheses.");
|
||||||
|
operators.pop();
|
||||||
|
nextOperatorIsUnary = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
exp.erase(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temp.v.size()) {
|
||||||
|
infix.push(temp);
|
||||||
|
temp = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (operators.size()) {
|
||||||
|
if (operators.top() == '(') throw std::logic_error("Mismatched parentheses.");
|
||||||
|
infix.emplace(string(1, operators.top()));
|
||||||
|
operators.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 Gui::Expression::eval() {
|
||||||
|
let infix = this->infix;
|
||||||
|
std::stack<String> eval {};
|
||||||
|
|
||||||
|
while (infix.size()) {
|
||||||
|
let& t = infix.front();
|
||||||
|
infix.pop();
|
||||||
|
|
||||||
|
if (!t.isOperator()) {
|
||||||
|
eval.emplace(t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (eval.size() < 2) throw std::runtime_error("Eval stack has < 2 items! This is an engine error!");
|
||||||
|
|
||||||
|
String b = eval.top();
|
||||||
|
eval.pop();
|
||||||
|
String a = eval.top();
|
||||||
|
eval.pop();
|
||||||
|
|
||||||
|
switch (t.v[0]) {
|
||||||
|
case '+':
|
||||||
|
eval.emplace(std::to_string(a.eval() + b.eval()));
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
eval.emplace(std::to_string(a.eval() - b.eval()));
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
eval.emplace(std::to_string(a.eval() * b.eval()));
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
eval.emplace(std::to_string(a.eval() / b.eval()));
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
eval.emplace(std::to_string(pow(a.eval(), b.eval())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eval.size()) throw std::runtime_error("Eval stack is empty! This is an engine error!");
|
||||||
|
return eval.top().eval();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<char, u8> Gui::Expression::PRECEDENCE {
|
||||||
|
{ '^', 4 },
|
||||||
|
{ '*', 3 },
|
||||||
|
{ '/', 3 },
|
||||||
|
{ '+', 2 },
|
||||||
|
{ '-', 2 }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Gui::Expression::String::isOperator() {
|
||||||
|
return v.size() == 1 && (v[0] == '+' || v[0] == '-' || v[0] == '*' || v[0] == '/' || v[0] == '^');
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 Gui::Expression::String::eval() {
|
||||||
|
usize unitInd = -1;
|
||||||
|
|
||||||
|
f32 value = std::stof(v, &unitInd);
|
||||||
|
string unit = v.substr(unitInd);
|
||||||
|
|
||||||
|
switch (Util::hash(unit.data())) {
|
||||||
|
default:
|
||||||
|
throw std::logic_error("Unknown unit '" + unit + "'.");
|
||||||
|
|
||||||
|
case Util::hash("dp"):
|
||||||
|
return value * Gui::PX_SCALE;
|
||||||
|
|
||||||
|
case Util::hash("deg"):
|
||||||
|
return value * M_PI / 180.f;
|
||||||
|
|
||||||
|
case Util::hash(""):
|
||||||
|
case Util::hash("px"):
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "util/Types.h"
|
||||||
|
|
||||||
|
namespace Gui {
|
||||||
|
enum class UnitOrOperator: u8 {
|
||||||
|
DISPLAY_PIXEL,
|
||||||
|
REAL_PIXEL,
|
||||||
|
DEGREE,
|
||||||
|
|
||||||
|
ADD = 128,
|
||||||
|
SUBTRACT,
|
||||||
|
MULTIPLY,
|
||||||
|
DIVIDE,
|
||||||
|
EXPONENT
|
||||||
|
};
|
||||||
|
|
||||||
|
class Expression {
|
||||||
|
struct String {
|
||||||
|
String() = default;
|
||||||
|
explicit String(const string& v): v(v) {}
|
||||||
|
|
||||||
|
string v {};
|
||||||
|
|
||||||
|
bool isOperator();
|
||||||
|
|
||||||
|
f32 eval();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
Token() = default;
|
||||||
|
explicit Token(f32 val, UnitOrOperator unit): val(val), unit(unit);
|
||||||
|
explicit Token(const string& str);
|
||||||
|
|
||||||
|
bool isOperator();
|
||||||
|
f32 evalValue();
|
||||||
|
|
||||||
|
f32 val;
|
||||||
|
UnitOrOperator unit;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
Expression() = default;
|
||||||
|
Expression(const string& exp);
|
||||||
|
|
||||||
|
void setExpression(string exp);
|
||||||
|
|
||||||
|
f32 eval();
|
||||||
|
private:
|
||||||
|
usize hash = 0;
|
||||||
|
std::queue<String> infix {};
|
||||||
|
|
||||||
|
const static std::unordered_map<char, u8> PRECEDENCE;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#include "Root.h"
|
#include "Root.h"
|
||||||
|
|
||||||
|
#include "util/Types.h"
|
||||||
#include "util/Timer.h"
|
#include "util/Timer.h"
|
||||||
#include "client/gui/BoxElement.h"
|
#include "client/gui/BoxElement.h"
|
||||||
|
|
||||||
|
@ -7,20 +8,19 @@ Gui::Root::Root(Window& window, TextureAtlas& atlas) :
|
||||||
atlas(atlas),
|
atlas(atlas),
|
||||||
window(window),
|
window(window),
|
||||||
body(make_shared<BoxElement>(*this, stylesheets)) {
|
body(make_shared<BoxElement>(*this, stylesheets)) {
|
||||||
const ivec2 size = glm::ceil(vec2(window.getSize()) / static_cast<f32>(Gui::PX_SCALE));
|
const ivec2 size = window.getSize();
|
||||||
|
|
||||||
body->setProps({
|
body->setProps({
|
||||||
.id = "body",
|
.id = "body",
|
||||||
.styles = {{
|
.styles = {{
|
||||||
{ StyleRule::WIDTH, size.x },
|
{ StyleRule::SIZE, array<string, 2> {
|
||||||
{ StyleRule::HEIGHT, size.y }
|
std::to_string(size.x) + "px", std::to_string(size.y) + "px" } }
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
lock = window.onResize([&](ivec2 size) {
|
lock = window.onResize([&](ivec2 size) {
|
||||||
size = glm::ceil(vec2(window.getSize()) / static_cast<f32>(Gui::PX_SCALE));
|
body->setStyle(StyleRule::SIZE, array<string, 2> {
|
||||||
body->setStyle(StyleRule::WIDTH, size.x);
|
std::to_string(size.x) + "px", std::to_string(size.y) + "px" });
|
||||||
body->setStyle(StyleRule::HEIGHT, size.y);
|
|
||||||
Timer t("Resize UI");
|
Timer t("Resize UI");
|
||||||
body->updateElement();
|
body->updateElement();
|
||||||
t.printElapsedMs();
|
t.printElapsedMs();
|
||||||
|
@ -29,16 +29,26 @@ Gui::Root::Root(Window& window, TextureAtlas& atlas) :
|
||||||
// window.input.bindMouseCallback()
|
// window.input.bindMouseCallback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gui::Root::~Root() {
|
||||||
|
window.setCursorHand(false);
|
||||||
|
}
|
||||||
|
|
||||||
void Gui::Root::addStylesheet(const std::unordered_map<string, Style>& sheet) {
|
void Gui::Root::addStylesheet(const std::unordered_map<string, Style>& sheet) {
|
||||||
stylesheets.emplace_back(sheet);
|
stylesheets.emplace_back(sheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gui::Root::update() {
|
void Gui::Root::update() {
|
||||||
const let pos = window.input.getMousePos();
|
const let pos = window.input.getMousePos();
|
||||||
body->handleMouseHover(pos);
|
bool pointer = false;
|
||||||
|
body->handleMouseHover(pos, pointer);
|
||||||
|
setCursorPointer(pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gui::Root::draw(Renderer& renderer) {
|
void Gui::Root::draw(Renderer& renderer) {
|
||||||
if (!body) return;
|
if (!body) return;
|
||||||
body->draw(renderer);
|
body->draw(renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gui::Root::setCursorPointer(bool hand) {
|
||||||
|
window.setCursorHand(hand);
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ namespace Gui {
|
||||||
public:
|
public:
|
||||||
Root(Window& window, TextureAtlas& atlas);
|
Root(Window& window, TextureAtlas& atlas);
|
||||||
|
|
||||||
|
~Root();
|
||||||
|
|
||||||
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
|
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
|
||||||
sptr<E> create(const Element::Props& props = {}, const vec<sptr<Element>>& children = {}) {
|
sptr<E> create(const Element::Props& props = {}, const vec<sptr<Element>>& children = {}) {
|
||||||
let elem = make_shared<E>(*this, stylesheets);
|
let elem = make_shared<E>(*this, stylesheets);
|
||||||
|
@ -28,6 +30,8 @@ namespace Gui {
|
||||||
|
|
||||||
void draw(Renderer& renderer);
|
void draw(Renderer& renderer);
|
||||||
|
|
||||||
|
void setCursorPointer(bool hand);
|
||||||
|
|
||||||
vec<StyleSheet> stylesheets;
|
vec<StyleSheet> stylesheets;
|
||||||
const sptr<Element> body;
|
const sptr<Element> body;
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
#include "client/gui/Gui.h"
|
#include "client/gui/Gui.h"
|
||||||
|
|
||||||
|
#include "client/gui/Expression.h"
|
||||||
|
|
||||||
namespace Gui {
|
namespace Gui {
|
||||||
enum class StyleRule {
|
enum class StyleRule {
|
||||||
|
POS,
|
||||||
|
SIZE,
|
||||||
|
MARGIN,
|
||||||
PADDING,
|
PADDING,
|
||||||
WIDTH,
|
|
||||||
HEIGHT,
|
|
||||||
TOP,
|
|
||||||
LEFT,
|
|
||||||
|
|
||||||
GAP,
|
GAP,
|
||||||
LAYOUT,
|
LAYOUT,
|
||||||
|
@ -16,10 +17,12 @@ namespace Gui {
|
||||||
H_ALIGN,
|
H_ALIGN,
|
||||||
V_ALIGN,
|
V_ALIGN,
|
||||||
|
|
||||||
|
CURSOR,
|
||||||
OVERFLOW,
|
OVERFLOW,
|
||||||
|
|
||||||
BACKGROUND,
|
BACKGROUND,
|
||||||
BACKGROUND_HOVER
|
BACKGROUND_HOVER,
|
||||||
|
|
||||||
|
CONTENT
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ValueType {
|
enum class ValueType {
|
||||||
|
@ -139,9 +142,30 @@ namespace Gui {
|
||||||
L == ValueType::LENGTH, bool> = true>
|
L == ValueType::LENGTH, bool> = true>
|
||||||
|
|
||||||
optional<N> get(StyleRule rule) const {
|
optional<N> get(StyleRule rule) const {
|
||||||
return get<N>(rule);
|
let raw = get<string>(rule);
|
||||||
|
if (!raw) return std::nullopt;
|
||||||
|
return Gui::Expression(*raw).eval();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an optional of the specified Rule's value,
|
||||||
|
* which is interpreted as a length.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename VN, ValueType L, std::enable_if_t<
|
||||||
|
(std::is_integral_v<typename VN::value_type> || std::is_floating_point_v<typename VN::value_type>) &&
|
||||||
|
std::is_same_v<VN, glm::vec<VN::length(), typename VN::value_type>> &&
|
||||||
|
L == ValueType::LENGTH, bool> = true>
|
||||||
|
|
||||||
|
optional<VN> get(StyleRule rule) const {
|
||||||
|
let raw = get<array<string, VN::length()>>(rule);
|
||||||
|
if (!raw) return std::nullopt;
|
||||||
|
VN vec;
|
||||||
|
for (usize i = 0; i < VN::length(); i++)
|
||||||
|
vec[i] = Gui::Expression((*raw)[i]).eval();
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the specified Rule's value as a V,
|
* Returns the specified Rule's value as a V,
|
||||||
* or the default value provided as the second parameter.
|
* or the default value provided as the second parameter.
|
||||||
|
|
|
@ -0,0 +1,246 @@
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "TextElement.h"
|
||||||
|
|
||||||
|
#include "client/gui/Root.h"
|
||||||
|
#include "client/graph/Model.h"
|
||||||
|
#include "client/graph/mesh/EntityMesh.h"
|
||||||
|
|
||||||
|
void Gui::TextElement::updateElement() {
|
||||||
|
const string text = getStyle<string>(StyleRule::CONTENT, "");
|
||||||
|
|
||||||
|
if (!font) {
|
||||||
|
font = std::make_unique<Font>(root.atlas, root.atlas["font"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
usize newHash = std::hash<string>{}(text);
|
||||||
|
if (hash != newHash) {
|
||||||
|
hash = newHash;
|
||||||
|
|
||||||
|
vec4 textColor = vec4(1);
|
||||||
|
vec4 backgroundColor = vec4(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
u32 ind = 0;
|
||||||
|
u32 width = 0;
|
||||||
|
|
||||||
|
vec<EntityVertex> vertices;
|
||||||
|
vertices.reserve(text.length() * 8 + 200);
|
||||||
|
vec<u32> indices;
|
||||||
|
indices.reserve(text.length() * 12 + 240);
|
||||||
|
|
||||||
|
vec<string> lines;
|
||||||
|
{
|
||||||
|
std::stringstream textStream(text);
|
||||||
|
string line;
|
||||||
|
while (std::getline(textStream, line, '\n')) lines.emplace_back(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 offset = {};
|
||||||
|
u32 h = Font::charHeight;
|
||||||
|
|
||||||
|
bool bold = false;
|
||||||
|
bool italic = false;
|
||||||
|
i32 underline = -1;
|
||||||
|
i32 strikethrough = -1;
|
||||||
|
u32 strikethroughVertStart = 0;
|
||||||
|
vec4 color = textColor;
|
||||||
|
|
||||||
|
for (usize i = 0; i < lines.size(); i++) {
|
||||||
|
let& line = lines[i];
|
||||||
|
bool empty = line.find_first_not_of(" \t\n") == -1;
|
||||||
|
|
||||||
|
if (empty) {
|
||||||
|
offset.x = 0;
|
||||||
|
offset.y += h / 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 bgVertStart = 0;
|
||||||
|
if (backgroundColor.w != 0) {
|
||||||
|
bgVertStart = vertices.size();
|
||||||
|
for (u32 i = 0; i < 4; i++) vertices.push_back({});
|
||||||
|
for (u32 i : INDICES) indices.push_back(i + ind);
|
||||||
|
ind += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usize j = 0; j < line.length() + 1; j++) {
|
||||||
|
char c = j < line.length() ? line[j] : ' ';
|
||||||
|
if (c == '\t') c = ' ';
|
||||||
|
|
||||||
|
if (c == '`') {
|
||||||
|
bool flushDecorators = j == line.length();
|
||||||
|
|
||||||
|
char d = line[++j];
|
||||||
|
if (d == '`') goto escape_formatting;
|
||||||
|
else if (d == ' ') offset.x++;
|
||||||
|
else if (d == 'b') bold = true;
|
||||||
|
else if (d == 'i') italic = true;
|
||||||
|
else if (d == 'u') underline = offset.x;
|
||||||
|
else if (d == 's') {
|
||||||
|
strikethrough = offset.x;
|
||||||
|
strikethroughVertStart = vertices.size();
|
||||||
|
for (u32 i = 0; i < 4; i++) vertices.push_back({});
|
||||||
|
for (u32 i : INDICES) indices.push_back(i + ind);
|
||||||
|
ind += 4;
|
||||||
|
}
|
||||||
|
else if (d == 'c') flushDecorators = true;
|
||||||
|
else if (d == 'r') {
|
||||||
|
bold = false;
|
||||||
|
italic = false;
|
||||||
|
flushDecorators = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flushDecorators) {
|
||||||
|
if (underline != -1) {
|
||||||
|
TextElement::drawRect(
|
||||||
|
{ underline, offset.y + h - 1, offset.x, offset.y + h },
|
||||||
|
color, vertices, indices, ind);
|
||||||
|
TextElement::drawRect(
|
||||||
|
{ underline + 1, offset.y + h, offset.x + 1, offset.y + h + 1 },
|
||||||
|
color * BG_MULTIPLE, vertices, indices, ind);
|
||||||
|
underline = offset.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strikethrough != -1) {
|
||||||
|
TextElement::drawRect(
|
||||||
|
{ strikethrough, offset.y + h / 2, offset.x, offset.y + h / 2 + 1 },
|
||||||
|
color, vertices, indices, ind);
|
||||||
|
TextElement::drawRect(
|
||||||
|
{ strikethrough + 1, offset.y + h / 2 + 1, offset.x + 1, offset.y + h / 2 + 2 },
|
||||||
|
color * BG_MULTIPLE, vertices, indices, ind, strikethroughVertStart);
|
||||||
|
strikethrough = offset.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d == 'r') {
|
||||||
|
color = textColor;
|
||||||
|
underline = -1;
|
||||||
|
strikethrough = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d == 'c') {
|
||||||
|
char code = line[++j];
|
||||||
|
if (code == 'r') color = textColor;
|
||||||
|
else {
|
||||||
|
u32 v;
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << code;
|
||||||
|
ss >> v;
|
||||||
|
color = COLORS[v];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
escape_formatting:
|
||||||
|
if (j == line.length()) continue;
|
||||||
|
|
||||||
|
u32 w = font->getCharWidth(c) + 1;
|
||||||
|
vec4 UV = font->getCharUVs(c);
|
||||||
|
|
||||||
|
for (u32 k = 0; k < (bold ? 4 : 2); k++) {
|
||||||
|
vec4 c = color;
|
||||||
|
|
||||||
|
if (k == 0 || (k == 1 && bold)) c *= BG_MULTIPLE;
|
||||||
|
|
||||||
|
if (k == 0) {
|
||||||
|
offset.x += 1;
|
||||||
|
offset.y += 1;
|
||||||
|
}
|
||||||
|
else if ((k == 1 || k == 3) && bold) {
|
||||||
|
offset.x += 1;
|
||||||
|
}
|
||||||
|
else if ((k == 1 && !bold) || (k == 2 && bold)) {
|
||||||
|
offset.x -= bold ? 2 : 1;
|
||||||
|
offset.y -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices.emplace_back(offset + vec3(italic ? 2 : 0, 0, 0), vec4 { UV.x, UV.y, 0, c.w },
|
||||||
|
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
|
||||||
|
|
||||||
|
vertices.emplace_back(offset + vec3(0, h, 0), vec4 { UV.x, UV.w, 0, c.w },
|
||||||
|
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
|
||||||
|
|
||||||
|
vertices.emplace_back(offset + vec3(w, h, 0), vec4 { UV.z, UV.w, 0, c.w },
|
||||||
|
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
|
||||||
|
|
||||||
|
vertices.emplace_back(offset + vec3(w + (italic ? 2 : 0), 0, 0), vec4 { UV.z, UV.y, 0, c.w },
|
||||||
|
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
|
||||||
|
|
||||||
|
for (u32 i : INDICES) indices.push_back(i + ind);
|
||||||
|
ind += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset.x += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backgroundColor.w != 0) TextElement::drawRect({ -1, offset.y - 1, offset.x + 2, offset.y + h + 1 },
|
||||||
|
backgroundColor, vertices, indices, ind, bgVertStart);
|
||||||
|
|
||||||
|
if (offset.x > width) width = offset.x;
|
||||||
|
offset.x = 0;
|
||||||
|
offset.y += h + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mesh = make_unique<EntityMesh>();
|
||||||
|
mesh->create(vertices, indices);
|
||||||
|
|
||||||
|
let model = make_shared<Model>();
|
||||||
|
model->fromMesh(std::move(mesh));
|
||||||
|
entity.setModel(model);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setScale(PX_SCALE * (2/3.f));
|
||||||
|
// entity.setScale(vec3(getComputedSize() * static_cast<i32>(PX_SCALE), 0));
|
||||||
|
entity.setPos(vec3(getComputedScreenPos() * static_cast<i32>(PX_SCALE), 0));
|
||||||
|
Element::updateElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gui::TextElement::drawRect(const vec4 pos, const vec4 color,
|
||||||
|
vec<EntityVertex>& vertices, vec<u32>& indices, u32& ind, const u32 insert) {
|
||||||
|
|
||||||
|
vec<EntityVertex> myVerts = {
|
||||||
|
{ vec3 { pos.x, pos.y, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} },
|
||||||
|
{ vec3 { pos.x, pos.w, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} },
|
||||||
|
{ vec3 { pos.z, pos.w, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} },
|
||||||
|
{ vec3 { pos.z, pos.y, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (insert != -1) {
|
||||||
|
vertices[insert] = myVerts[0];
|
||||||
|
vertices[insert + 1] = myVerts[1];
|
||||||
|
vertices[insert + 2] = myVerts[2];
|
||||||
|
vertices[insert + 3] = myVerts[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (EntityVertex& vert : myVerts) vertices.emplace_back(vert);
|
||||||
|
for (u32 i : INDICES) indices.push_back(i + ind);
|
||||||
|
ind += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const array<vec4, 16> Gui::TextElement::COLORS = {
|
||||||
|
Util::hexToColorVec("#ffffff"),
|
||||||
|
Util::hexToColorVec("#aaaaaa"),
|
||||||
|
Util::hexToColorVec("#666666"),
|
||||||
|
Util::hexToColorVec("#000000"),
|
||||||
|
|
||||||
|
Util::hexToColorVec("#f53658"),
|
||||||
|
Util::hexToColorVec("#ff9940"),
|
||||||
|
Util::hexToColorVec("#fffb82"),
|
||||||
|
Util::hexToColorVec("#9fff80"),
|
||||||
|
Util::hexToColorVec("#0fa84f"),
|
||||||
|
Util::hexToColorVec("#26d4d4"),
|
||||||
|
Util::hexToColorVec("#7df4ff"),
|
||||||
|
Util::hexToColorVec("#33a2f5"),
|
||||||
|
Util::hexToColorVec("#2c58e8"),
|
||||||
|
Util::hexToColorVec("#b05cff"),
|
||||||
|
Util::hexToColorVec("#fd7dff"),
|
||||||
|
Util::hexToColorVec("#ff739f"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const array<u32, 6> Gui::TextElement::INDICES = { 0, 1, 2, 2, 3, 0 };
|
||||||
|
|
||||||
|
const vec4 Gui::TextElement::BG_MULTIPLE = { 0.3, 0.3, 0.35, 0.75 };
|
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Element.h"
|
||||||
|
|
||||||
|
#include "client/graph/Font.h"
|
||||||
|
//#include "game/atlas/asset/AtlasRef.h"
|
||||||
|
|
||||||
|
namespace Gui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays formatted text specified by the contact property.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TextElement: public Element {
|
||||||
|
public:
|
||||||
|
using Element::Element;
|
||||||
|
|
||||||
|
virtual void updateElement() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// sptr<AtlasRef> tex;
|
||||||
|
// optional<any> curBg;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uptr<Font> font;
|
||||||
|
usize hash = 0;
|
||||||
|
|
||||||
|
void drawRect(const vec4 pos, const vec4 color,
|
||||||
|
vec<EntityVertex>& vertices, vec<u32>& indices, u32& ind, const u32 insert = -1);
|
||||||
|
|
||||||
|
static const vec4 BG_MULTIPLE;
|
||||||
|
static const array<u32, 6> INDICES;
|
||||||
|
static const array<vec4, 16> COLORS;
|
||||||
|
};
|
||||||
|
}
|
|
@ -8,8 +8,8 @@
|
||||||
#include "util/Log.h"
|
#include "util/Log.h"
|
||||||
#include "ConnectScene.h"
|
#include "ConnectScene.h"
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/gui/Gui.h"
|
|
||||||
#include "client/gui/BoxElement.h"
|
#include "client/gui/BoxElement.h"
|
||||||
|
#include "client/gui/TextElement.h"
|
||||||
#include "client/menu/SubgameDef.h"
|
#include "client/menu/SubgameDef.h"
|
||||||
#include "client/gui/basic/GuiText.h"
|
#include "client/gui/basic/GuiText.h"
|
||||||
#include "game/atlas/asset/AtlasRef.h"
|
#include "game/atlas/asset/AtlasRef.h"
|
||||||
|
@ -19,79 +19,48 @@
|
||||||
MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
||||||
root(client.renderer.window, client.game->textures) {
|
root(client.renderer.window, client.game->textures) {
|
||||||
|
|
||||||
// components(make_unique<GuiContainer>()),
|
|
||||||
// menuContainer(make_shared<GuiContainer>("__menu")),
|
|
||||||
// sandbox(sandboxArea, client, menuContainer) {
|
|
||||||
|
|
||||||
client.renderer.setClearColor(0, 0, 0);
|
client.renderer.setClearColor(0, 0, 0);
|
||||||
client.renderer.window.input.setMouseLocked(false);
|
client.renderer.window.input.setMouseLocked(false);
|
||||||
|
|
||||||
// Font f(client.game->textures, client.game->textures["font"]);
|
|
||||||
// win = client.renderer.window.getSize();
|
|
||||||
// sandboxArea = win - ivec2(0, 18 * GS);
|
|
||||||
|
|
||||||
// components->add(menuContainer);
|
|
||||||
//
|
|
||||||
// branding = make_shared<GuiContainer>("zephaBranding");
|
|
||||||
// components->add(branding);
|
|
||||||
// {
|
|
||||||
// auto zephaText = make_shared<GuiText>("zephaText");
|
|
||||||
// zephaText->create({ GS, GS }, {}, { 1, 1, 1, 1 }, {}, f);
|
|
||||||
// zephaText->setText("Zepha");
|
|
||||||
// branding->add(zephaText);
|
|
||||||
//
|
|
||||||
// auto alphaText = make_shared<GuiText>("alphaText");
|
|
||||||
// alphaText->create({ GS, GS }, {}, { 1, 0.5, 0.7, 1 }, {}, f);
|
|
||||||
// alphaText->setText("ALPHA");
|
|
||||||
// alphaText->setPos({ 25 * GS, 0 });
|
|
||||||
// branding->add(alphaText);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// root.body->setStyle(Gui::Style::Rule::DIRECTION, "row");
|
|
||||||
// root.body->setStyle(Gui::Style::Rule::GAP_X, 8);
|
|
||||||
// root.body->setStyle(Gui::Style::Rule::GAP_Y, 8);
|
|
||||||
// root.body->setStyle(Gui::Style::Rule::H_ALIGN, "center");
|
|
||||||
// root.body->setStyle(Gui::Style::Rule::V_ALIGN, "center");
|
|
||||||
|
|
||||||
root.body->setStyle(Gui::StyleRule::BACKGROUND, string("#123"));
|
root.body->setStyle(Gui::StyleRule::BACKGROUND, string("#123"));
|
||||||
|
|
||||||
root.addStylesheet({
|
root.addStylesheet({
|
||||||
{ "sandbox", {{
|
{ "sandbox", {{
|
||||||
|
{ Gui::StyleRule::H_ALIGN, string("center") },
|
||||||
|
{ Gui::StyleRule::V_ALIGN, string("center") }
|
||||||
}}},
|
}}},
|
||||||
{ "navigation", {{
|
{ "navigation", {{
|
||||||
{ Gui::StyleRule::HEIGHT, 18 }
|
{ Gui::StyleRule::SIZE, array<string, 2> { "-1", "18dp" } }
|
||||||
}}},
|
}}},
|
||||||
{ "navigationWrap", {{
|
{ "navigationWrap", {{
|
||||||
{ Gui::StyleRule::DIRECTION, string("row") },
|
{ Gui::StyleRule::DIRECTION, string("row") },
|
||||||
{ Gui::StyleRule::TOP, 0 },
|
{ Gui::StyleRule::POS, array<string, 2> { "0", "0" } }
|
||||||
{ Gui::StyleRule::LEFT, 0 }
|
|
||||||
}}},
|
}}},
|
||||||
{ "navigationBackground", {{
|
{ "navigationBackground", {{
|
||||||
{ Gui::StyleRule::WIDTH, 64 },
|
{ Gui::StyleRule::SIZE, array<string, 2> { "64dp", "18dp" } },
|
||||||
{ Gui::StyleRule::HEIGHT, 18 },
|
|
||||||
{ Gui::StyleRule::BACKGROUND, string("menu_bar_bg") }
|
{ Gui::StyleRule::BACKGROUND, string("menu_bar_bg") }
|
||||||
}}},
|
}}},
|
||||||
{ "navigationButton", {{
|
{ "navigationButton", {{
|
||||||
{ Gui::StyleRule::WIDTH, 16 },
|
{ Gui::StyleRule::SIZE, array<string, 2> { "16dp", "16dp" } },
|
||||||
{ Gui::StyleRule::HEIGHT, 16 }
|
{ Gui::StyleRule::CURSOR, string("pointer") }
|
||||||
}}}
|
}}}
|
||||||
});
|
});
|
||||||
|
|
||||||
let sandbox = root.body->append<Gui::BoxElement>({ .classes = { "sandbox" } });
|
let sandbox = root.body->append<Gui::BoxElement>({ .classes = { "sandbox" } });
|
||||||
let navigation = root.body->append<Gui::BoxElement>({ .classes = { "navigation" } });
|
let navigation = root.body->append<Gui::BoxElement>({ .classes = { "navigation" } });
|
||||||
let navigationBG = navigation->append<Gui::BoxElement>({ .classes = { "navigationWrap" } });
|
let navigationBG = navigation->append<Gui::BoxElement>({ .classes = { "navigationWrap" } });
|
||||||
|
|
||||||
for (usize i = 0; i < 2000 / Gui::PX_SCALE / 64; i++)
|
for (usize i = 0; i < 2000 / Gui::PX_SCALE / 64; i++)
|
||||||
navigationBG->append<Gui::BoxElement>({ .classes = { "navigationBackground" } });
|
navigationBG->append<Gui::BoxElement>({ .classes = { "navigationBackground" } });
|
||||||
|
|
||||||
let navigationList = navigation->append<Gui::BoxElement>({
|
let navigationList = navigation->append<Gui::BoxElement>({
|
||||||
.classes = { "navigationWrap" },
|
.classes = { "navigationWrap" },
|
||||||
.styles = {{
|
.styles = {{
|
||||||
{ Gui::StyleRule::PADDING, ivec4(1) },
|
{ Gui::StyleRule::PADDING, array<string, 4> { "1dp", "1dp", "1dp", "1dp" } },
|
||||||
{ Gui::StyleRule::GAP, ivec2(1) }
|
{ Gui::StyleRule::GAP, array<string, 2> { "1dp", "1dp" } }
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
let serversButton = navigationList->append<Gui::BoxElement>({
|
let serversButton = navigationList->append<Gui::BoxElement>({
|
||||||
.classes = { "navigationButton" },
|
.classes = { "navigationButton" },
|
||||||
.styles = {{
|
.styles = {{
|
||||||
|
@ -99,7 +68,7 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
||||||
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_multiplayer)") }
|
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_multiplayer)") }
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
let contentButton = navigationList->append<Gui::BoxElement>({
|
let contentButton = navigationList->append<Gui::BoxElement>({
|
||||||
.classes = { "navigationButton" },
|
.classes = { "navigationButton" },
|
||||||
.styles = {{
|
.styles = {{
|
||||||
|
@ -107,9 +76,46 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
||||||
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_content)") }
|
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_content)") }
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
navigationList->append<Gui::BoxElement>({});
|
navigationList->append<Gui::BoxElement>({
|
||||||
|
.styles = {{
|
||||||
|
{ Gui::StyleRule::BACKGROUND, string("#fff5") },
|
||||||
|
{ Gui::StyleRule::SIZE, array<string, 2> { "1dp", "10dp" } },
|
||||||
|
{ Gui::StyleRule::MARGIN, array<string, 4> { "2dp", "3dp", "2dp", "3dp" } }
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
|
||||||
|
findSubgames();
|
||||||
|
|
||||||
|
for (usize i = 0; i < subgames.size(); i++) {
|
||||||
|
let& subgame = subgames[i];
|
||||||
|
navigationList->append<Gui::BoxElement>({
|
||||||
|
.classes = { "navigationButton" },
|
||||||
|
.styles = {{
|
||||||
|
{ Gui::StyleRule::BACKGROUND, string("crop(0, 0, 16, 16, " + subgame.iconRef->name + ")") },
|
||||||
|
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, " + subgame.iconRef->name + ")") }
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
//
|
||||||
|
// button->setCallback(Element::CallbackType::PRIMARY, [&](bool down, ivec2) {
|
||||||
|
// if (!down) return;
|
||||||
|
// selectedSubgame = &subgame;
|
||||||
|
// sandbox.load(*selectedSubgame);
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subgames.size() > 0) {
|
||||||
|
selectedSubgame = &subgames[0];
|
||||||
|
// sandbox.load(*selectedSubgame);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationList->append<Gui::BoxElement>({
|
||||||
|
.styles = {{
|
||||||
|
{ Gui::StyleRule::BACKGROUND, string("#f006") },
|
||||||
|
{ Gui::StyleRule::SIZE, array<string, 2> { "-1", "16dp" } }
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
|
||||||
let settingsButton = navigationList->append<Gui::BoxElement>({
|
let settingsButton = navigationList->append<Gui::BoxElement>({
|
||||||
.classes = { "navigationButton" },
|
.classes = { "navigationButton" },
|
||||||
.styles = {{
|
.styles = {{
|
||||||
|
@ -117,7 +123,7 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
||||||
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_settings)") }
|
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_settings)") }
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
let closeButton = navigationList->append<Gui::BoxElement>({
|
let closeButton = navigationList->append<Gui::BoxElement>({
|
||||||
.classes = { "navigationButton" },
|
.classes = { "navigationButton" },
|
||||||
.styles = {{
|
.styles = {{
|
||||||
|
@ -125,7 +131,7 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
||||||
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_quit)") }
|
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_quit)") }
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
// closeButton->setCallback(Element::CallbackType::PRIMARY,
|
// closeButton->setCallback(Element::CallbackType::PRIMARY,
|
||||||
// [](bool down, ivec2) { if (down) exit(0); });
|
// [](bool down, ivec2) { if (down) exit(0); });
|
||||||
|
|
||||||
|
@ -134,47 +140,10 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
|
||||||
// client.scene.setScene(make_unique<ConnectScene>(client, Address{ "127.0.0.1" }));
|
// client.scene.setScene(make_unique<ConnectScene>(client, Address{ "127.0.0.1" }));
|
||||||
// });
|
// });
|
||||||
|
|
||||||
//
|
|
||||||
// navigationBarIcons->add(contentButton);
|
|
||||||
//
|
|
||||||
// auto divider = make_shared<GuiRect>("divider");
|
// auto divider = make_shared<GuiRect>("divider");
|
||||||
// divider->create({ GS, GS * 10 }, {}, { 1, 1, 1, 0.3 });
|
// divider->create({ GS, GS * 10 }, {}, { 1, 1, 1, 0.3 });
|
||||||
// divider->setPos({ GS * 2 + GS * 18 * 2, GS * 4 });
|
// divider->setPos({ GS * 2 + GS * 18 * 2, GS * 4 });
|
||||||
// navigationBarIcons->add(divider);
|
// navigationBarIcons->add(divider);
|
||||||
//
|
|
||||||
// findSubgames();
|
|
||||||
//
|
|
||||||
// for (usize i = 0; i < subgames.size(); i++) {
|
|
||||||
// auto& subgame = subgames[i];
|
|
||||||
// auto button = make_shared<GuiImageButton>(subgame.config.name);
|
|
||||||
//
|
|
||||||
// button->create({ 16 * GS, 16 * GS }, {},
|
|
||||||
// client.game->textures["crop(0, 0, 16, 16, " + subgame.iconRef->name + ")"],
|
|
||||||
// client.game->textures["crop(16, 0, 16, 16, " + subgame.iconRef->name + ")"]);
|
|
||||||
//
|
|
||||||
// button->setPos({ GS * 7 + GS * 18 * (i + 2), GS });
|
|
||||||
// button->setCallback(Element::CallbackType::PRIMARY, [&](bool down, ivec2) {
|
|
||||||
// if (!down) return;
|
|
||||||
// selectedSubgame = &subgame;
|
|
||||||
// sandbox.load(*selectedSubgame);
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// navigationBarIcons->add(button);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (subgames.size() > 0) {
|
|
||||||
// selectedSubgame = &subgames[0];
|
|
||||||
// sandbox.load(*selectedSubgame);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// positionElements();
|
|
||||||
//
|
|
||||||
// lock = client.renderer.window.onResize([&](ivec2 newWin) {
|
|
||||||
// win = newWin;
|
|
||||||
// sandboxArea = newWin - ivec2(0, 18 * GS);
|
|
||||||
// positionElements();
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenuScene::findSubgames() {
|
void MainMenuScene::findSubgames() {
|
||||||
|
@ -252,26 +221,6 @@ void MainMenuScene::findSubgames() {
|
||||||
[](SubgameDef& a, SubgameDef& b) { return a.config.name < b.config.name; });
|
[](SubgameDef& a, SubgameDef& b) { return a.config.name < b.config.name; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenuScene::positionElements() {
|
|
||||||
// sandbox.windowResized();
|
|
||||||
//
|
|
||||||
// branding->setPos({ win.x - 55 * GS, win.y - 30 * GS });
|
|
||||||
//
|
|
||||||
// navigationBar->setPos({ 0, win.y - 18 * GS });
|
|
||||||
//
|
|
||||||
// auto navigationBarBg = navigationBar->get<GuiContainer>("navigationBarBg");
|
|
||||||
// for (usize i = 0; i < static_cast<f32>(win.x) / 64.f / GS; i++) {
|
|
||||||
// auto segment = make_shared<GuiRect>("segment_" + std::to_string(i));
|
|
||||||
// segment->create({ 64 * GS, 18 * GS }, {}, client.game->textures["menu_bar_bg"]);
|
|
||||||
// segment->setPos({ i * 64 * GS, 0 });
|
|
||||||
// navigationBarBg->add(segment);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// auto navigationBarIcons = navigationBar->get<GuiContainer>("navigationBarIcons");
|
|
||||||
// navigationBarIcons->get<GuiImageButton>("closeButton")->setPos({ win.x - 16 * GS - GS, GS });
|
|
||||||
// navigationBarIcons->get<GuiImageButton>("settingsButton")->setPos({ win.x - 16 * GS * 2 - GS * 3, GS });
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainMenuScene::update() {
|
void MainMenuScene::update() {
|
||||||
client.game->textures.update();
|
client.game->textures.update();
|
||||||
root.update();
|
root.update();
|
||||||
|
@ -289,9 +238,4 @@ void MainMenuScene::draw() {
|
||||||
renderer.beginGUIDrawCalls();
|
renderer.beginGUIDrawCalls();
|
||||||
renderer.enableTexture(&client.game->textures.atlasTexture);
|
renderer.enableTexture(&client.game->textures.atlasTexture);
|
||||||
root.draw(renderer);
|
root.draw(renderer);
|
||||||
// components->draw(client.renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainMenuScene::cleanup() {
|
|
||||||
client.renderer.window.setCursorHand(false);
|
|
||||||
}
|
}
|
|
@ -24,31 +24,11 @@ public:
|
||||||
|
|
||||||
void draw() override;
|
void draw() override;
|
||||||
|
|
||||||
void cleanup() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** Repositions elements after a window resize. */
|
/** Find valid subgames in the subgames folder. */
|
||||||
void positionElements();
|
|
||||||
|
|
||||||
/** Finds valid subgames in the subgames folder. */
|
|
||||||
void findSubgames();
|
void findSubgames();
|
||||||
|
|
||||||
/** The UI scaling, in pixels. */
|
|
||||||
static constexpr f32 GS = 3;
|
|
||||||
|
|
||||||
/** The dimensions of the window. */
|
|
||||||
ivec2 win {};
|
|
||||||
|
|
||||||
/** The dimensions of the sandbox area. */
|
|
||||||
ivec2 sandboxArea {};
|
|
||||||
|
|
||||||
/** Element references. */
|
|
||||||
// uptr<GuiContainer> components;
|
|
||||||
// sptr<GuiContainer> branding;
|
|
||||||
// sptr<GuiContainer> navigationBar;
|
|
||||||
// sptr<GuiContainer> menuContainer;
|
|
||||||
|
|
||||||
/** Provides the API for menu mods. */
|
/** Provides the API for menu mods. */
|
||||||
// MenuSandbox sandbox;
|
// MenuSandbox sandbox;
|
||||||
Gui::Root root;
|
Gui::Root root;
|
||||||
|
@ -58,7 +38,5 @@ private:
|
||||||
|
|
||||||
/** A reference to the currently selected subgame. */
|
/** A reference to the currently selected subgame. */
|
||||||
SubgameDef* selectedSubgame = nullptr;
|
SubgameDef* selectedSubgame = nullptr;
|
||||||
|
|
||||||
Window::RCBLock lock;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
local menu = zepha.build_gui(function()
|
local menu = zepha.build_gui(function()
|
||||||
return Gui.Body {
|
return Gui.Box {
|
||||||
background = "zeus_background_christmas_night",
|
background = 'zeus_background_christmas_night',
|
||||||
|
|
||||||
Gui.Rect {
|
Gui.Box {
|
||||||
key = "particle_wrap",
|
key = 'particle_wrap',
|
||||||
size = { pc(100), pc(100) }
|
size = { '100%', '100%' }
|
||||||
},
|
},
|
||||||
|
|
||||||
Gui.Rect {
|
Gui.Box {
|
||||||
key = "sidebar",
|
pos = { '20% - 50s%', 0 },
|
||||||
position = { pc(20), 0 },
|
size = { 102, '100%' },
|
||||||
position_anchor = { pc(50), 0 },
|
|
||||||
size = { 102, pc(100) },
|
|
||||||
background = "#0135",
|
|
||||||
|
|
||||||
Gui.Rect {
|
background = '#0135',
|
||||||
key = "logo",
|
|
||||||
position = { 8, 8 },
|
Gui.Box {
|
||||||
|
pos = { 8, 8 },
|
||||||
size = { 86, 30 },
|
size = { 86, 30 },
|
||||||
background = "zeus_logo"
|
|
||||||
|
background = 'zeus_logo'
|
||||||
},
|
},
|
||||||
|
|
||||||
Gui.Button {
|
Gui.Button {
|
||||||
key = "buttonPlay",
|
id = 'button_play',
|
||||||
|
|
||||||
callbacks = {
|
-- callbacks = {
|
||||||
primary = function() zepha.start_game_local() end
|
-- primary = function() zepha.start_game_local() end
|
||||||
},
|
-- },
|
||||||
|
|
||||||
position = { 6, 50 },
|
pos = { 6, 50 },
|
||||||
size = { 90, 20 },
|
size = { 90, 20 },
|
||||||
background = "crop(0, 0, 90, 20, zeus_button)",
|
|
||||||
background_hover = "crop(0, 20, 90, 20, zeus_button)",
|
content = 'Local Play',
|
||||||
content = "Local Play"
|
background = 'crop(0, 0, 90, 20, zeus_button)',
|
||||||
|
background_hover = 'crop(0, 20, 90, 20, zeus_button)'
|
||||||
},
|
},
|
||||||
|
|
||||||
Gui.Button {
|
Gui.Button {
|
||||||
key = "buttonServers",
|
id = 'button_servers',
|
||||||
|
|
||||||
callbacks = {
|
-- callbacks = {
|
||||||
primary = function() zepha.start_game() end
|
-- primary = function() zepha.start_game() end
|
||||||
},
|
-- },
|
||||||
|
|
||||||
position = { 6, 74 },
|
pos = { 6, 74 },
|
||||||
size = { 90, 20 },
|
size = { 90, 20 },
|
||||||
background = "crop(0, 0, 90, 20, zeus_button)",
|
content = 'Browse Servers',
|
||||||
background_hover = "crop(0, 20, 90, 20, zeus_button)",
|
background = 'crop(0, 0, 90, 20, zeus_button)',
|
||||||
content = "Browse Servers"
|
background_hover = 'crop(0, 20, 90, 20, zeus_button)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,35 +62,37 @@ end)
|
||||||
-- ) end)
|
-- ) end)
|
||||||
-- end, 1)
|
-- end, 1)
|
||||||
|
|
||||||
local particle_wrap = menu:get("particle_wrap")
|
local particle_wrap = menu:get('particle_wrap')
|
||||||
menu(function()
|
menu(function()
|
||||||
for _ = 1, 20 do
|
for _ = 1, 20 do
|
||||||
local scale = 6 + math.random() * 4
|
local scale = 6 + math.random() * 4
|
||||||
particle_wrap:append(Gui.Rect {
|
particle_wrap:append(Gui.Rect {
|
||||||
position = { math.floor(math.random() * 600), math.floor(math.random() * 320) },
|
pos = { math.floor(math.random() * 600), math.floor(math.random() * 320) },
|
||||||
background = "particle_dark",
|
background = 'particle_dark',
|
||||||
size = { scale, scale }
|
size = { scale, scale }
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local tick = 0
|
-- local tick = 0
|
||||||
zepha.after(function()
|
-- zepha.after(function()
|
||||||
local i = 1
|
-- local i = 1
|
||||||
local part = particle_wrap:get(i)
|
-- local part = particle_wrap:get(i)
|
||||||
tick = tick + 0.012
|
-- tick = tick + 0.012
|
||||||
while part ~= nil do
|
--
|
||||||
local xO = (-math.sin(tick) * 0.0125 - 0.0025) * part.size[1]
|
-- while part ~= nil do
|
||||||
local pos = { part.position[1] + xO, part.position[2] + 0.05 * part.size[1] }
|
-- local xO = (-math.sin(tick) * 0.0125 - 0.0025) * part.size[1]
|
||||||
if pos[2] > 320 then pos[2] = -12 end
|
-- local pos = { part.pos[1] + xO, part.pos[2] + 0.05 * part.size[1] }
|
||||||
if pos[1] < -12 then pos[1] = 600 end
|
--
|
||||||
part.position = pos
|
-- if pos[2] > 320 then pos[2] = -12 end
|
||||||
i = i + 1
|
-- if pos[1] < -12 then pos[1] = 600 end
|
||||||
part = particle_wrap:get(i)
|
--
|
||||||
end
|
-- i = i + 1
|
||||||
return true
|
-- part.pos = pos
|
||||||
end, 0.016)
|
-- part = particle_wrap:get(i)
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- return true
|
||||||
|
-- end, 0.016)
|
||||||
|
|
||||||
zepha.set_gui(menu)
|
zepha.set_gui(menu)
|
||||||
|
|
Loading…
Reference in New Issue