Container and self widths, implementation is recursive and slow, fix getComputedSize() by storing elem size in a const.

master
Auri 2021-08-21 02:17:09 -07:00
parent 5bf82c4963
commit 6caef42565
13 changed files with 229 additions and 122 deletions

View File

@ -17,46 +17,56 @@ void Gui::Element::setStyle(StyleRule style, const std::any& value) {
props.styles.rules[style] = value; props.styles.rules[style] = value;
} }
ivec2 Gui::Element::getComputedSize() {
let size = getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, ivec2(-1));
if (size.x == -1) size.x = std::max(layoutSize.x, 0);
if (size.y == -1) size.y = std::max(layoutSize.y, 0);
return size;
}
void Gui::Element::clear() { void Gui::Element::clear() {
children.clear(); children.clear();
} }
ivec2 Gui::Element::getComputedOuterSize() { void Gui::Element::onClick(const std::function<void(i32, bool)>& cb) {
clickCb = cb;
}
Gui::ExpressionInfo Gui::Element::getExpr() const {
return {
parent ? parent->getComputedSize() : ivec2 {},
getComputedSize()
};
}
ivec2 Gui::Element::getComputedSize() const {
let size = getStyleWithExpr<vec2, ValueType::LENGTH>(StyleRule::SIZE, vec2(nanf("")),
{ parent ? parent->getComputedSize() : ivec2 {}, {} });
if (std::isnan(size.x)) size.x = std::max(layoutSize.x, 0);
if (std::isnan(size.y)) size.y = std::max(layoutSize.y, 0);
return size;
}
ivec2 Gui::Element::getComputedOuterSize() const {
let size = getComputedSize(); let size = getComputedSize();
let margin = getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {}); let margin = getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
return ivec2 { size.x + margin.x + margin.z, size.y + margin.y + margin.w }; return ivec2 { size.x + margin.x + margin.z, size.y + margin.y + margin.w };
} }
ivec2 Gui::Element::getComputedContentSize() { ivec2 Gui::Element::getComputedContentSize() const {
let size = getComputedSize(); let size = getComputedSize();
let padding = getStyle<ivec4, ValueType::LENGTH>(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 }, 0); 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() const {
return getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, ivec2(-1)); return getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, ivec2(-1));
} }
ivec2 Gui::Element::getComputedPos() { ivec2 Gui::Element::getComputedPos() const {
return getStyle<ivec2, ValueType::LENGTH>(StyleRule::POS, layoutPosition); return getStyle<ivec2, ValueType::LENGTH>(StyleRule::POS, layoutPosition);
} }
ivec2 Gui::Element::getComputedScreenPos() { ivec2 Gui::Element::getComputedScreenPos() const {
return getComputedPos() + parentOffset; return getComputedPos() + parentOffset;
} }
bool Gui::Element::handleMouseHover(ivec2 mousePos, bool& pointer) { 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, pointer)) childIntersects = true;
if (child->handleMouseHover(mousePos, pointer))
childIntersects = true;
if (childIntersects) { if (childIntersects) {
if (hovered) { if (hovered) {
@ -81,9 +91,17 @@ bool Gui::Element::handleMouseHover(ivec2 mousePos, bool& pointer) {
return intersects; return intersects;
} }
bool Gui::Element::handleMouseClick(u32 button, bool down) { bool Gui::Element::handleMouseClick(ivec2 mousePos, u32 button, bool down) {
for (let& child: children) if (child->handleMouseClick(button, down)) return true; for (let& child : children) if (child->handleMouseClick(mousePos, button, down)) return true;
return false;
ivec2 size = getComputedSize();
ivec2 pos = getComputedScreenPos();
bool intersects = mousePos.x >= pos.x && mousePos.x <= pos.x + size.x &&
mousePos.y >= pos.y && mousePos.y <= pos.y + size.y;
if (!intersects) return false;
if (clickCb) clickCb(button, down);
return clickCb != nullptr;
} }
void Gui::Element::draw(Renderer& renderer) { void Gui::Element::draw(Renderer& renderer) {
@ -160,8 +178,6 @@ void Gui::Element::layoutChildren() {
/** /**
* The amount of size each implicitly sized element should occupy. * The amount of size each implicitly sized element should occupy.
*/ */
// std::cout << selfSize << ": " << (selfSize[primary] - explicitSize) << std::endl;
i32 implicitElemSize = floor((selfSize[primary] - explicitSize) / (std::max)(implicitCount, 1)); i32 implicitElemSize = floor((selfSize[primary] - explicitSize) / (std::max)(implicitCount, 1));
@ -193,10 +209,10 @@ void Gui::Element::layoutChildren() {
+ gap + childMargin[primary] + childMargin[primary + 2]; + gap + childMargin[primary] + childMargin[primary + 2];
child->parentOffset = selfOffset; child->parentOffset = selfOffset;
child->updateElement(); child->updateElement();
} }
break; break;
} }
} }
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <list> #include <list>
#include <functional>
#include "client/gui/Gui.h" #include "client/gui/Gui.h"
#include "client/gui/Style.h" #include "client/gui/Style.h"
@ -20,7 +21,9 @@ namespace Gui {
*/ */
class Element { class Element {
friend class Root;
friend class BoxElement; friend class BoxElement;
friend class TextElement;
public: public:
struct Props {; struct Props {;
@ -81,23 +84,25 @@ namespace Gui {
void clear(); void clear();
void onClick(const std::function<void(i32, bool)>& cb);
/** Returns the element's computed size. */ /** Returns the element's computed size. */
virtual ivec2 getComputedSize(); virtual ivec2 getComputedSize() const;
/** Returns the element's computed size + margins. */ /** Returns the element's computed size + margins. */
virtual ivec2 getComputedOuterSize(); virtual ivec2 getComputedOuterSize() const;
/** Returns the element's computed content size, which is its size - padding. */ /** Returns the element's computed content size, which is its size - padding. */
virtual ivec2 getComputedContentSize(); virtual ivec2 getComputedContentSize() const;
/** Returns the element's explicit size. Unspecified dimensions are -1. */ /** Returns the element's explicit size. Unspecified dimensions are -1. */
virtual ivec2 getExplicitSize(); virtual ivec2 getExplicitSize() const;
/** Returns the element's computed position relative to its parent. */ /** Returns the element's computed position relative to its parent. */
virtual ivec2 getComputedPos(); virtual ivec2 getComputedPos() const;
/** Returns the element's computed position relative to the screen. */ /** Returns the element's computed position relative to the screen. */
virtual ivec2 getComputedScreenPos(); virtual ivec2 getComputedScreenPos() const;
/** Gets a style value from the element's styles or the root's stylesheets. */ /** 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 {
@ -114,8 +119,10 @@ namespace Gui {
return std::nullopt; return std::nullopt;
} }
/** Gets a style value from the element's styles or the root's stylesheets. */ /** Gets a generic 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,
std::enable_if_t<T != ValueType::LENGTH, bool> = true>
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);
if (opt) return *opt; if (opt) return *opt;
@ -129,14 +136,67 @@ namespace Gui {
} }
return std::nullopt; return std::nullopt;
} }
/** Gets a LENGTH value from the element's styles or the root's stylesheets. */
template<typename V, ValueType T = ValueType::LITERAL,
std::enable_if_t<T == ValueType::LENGTH, bool> = true>
const optional<V> getStyle(StyleRule rule) const {
ExpressionInfo info = getExpr();
const optional<V> opt = props.styles.get<V, T>(rule, info);
if (opt) return *opt;
for (const let& ss : stylesheets) {
for (const string& className : props.classes) {
const let& styles = ss.find(className);
if (styles == ss.end()) continue;
const optional<V> opt = styles->second.get<V, T>(rule, info);
if (opt) return *opt;
}
}
return std::nullopt;
}
/** Gets a LENGTH value from the element's styles or the root's stylesheets. */
template<typename V, ValueType T = ValueType::LITERAL,
std::enable_if_t<T == ValueType::LENGTH, bool> = true>
const optional<V> getStyleWithExpr(StyleRule rule, const ExpressionInfo& expr) const {
const optional<V> opt = props.styles.get<V, T>(rule, expr);
if (opt) return *opt;
for (const let& ss : stylesheets) {
for (const string& className : props.classes) {
const let& styles = ss.find(className);
if (styles == ss.end()) continue;
const optional<V> opt = styles->second.get<V, T>(rule, expr);
if (opt) return *opt;
}
}
return std::nullopt;
}
/** Gets a style value from the element's styles or the root's stylesheets. */ /** 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);
if (opt) return *opt; if (opt) return *opt;
return def; return def;
} }
/** Gets a LENGTH value from the element's styles or the root's stylesheets, with a custom ExpressionInfo. */
template<typename V, ValueType T = ValueType::LITERAL,
std::enable_if_t<T == ValueType::LENGTH, bool> = true>
const V getStyleWithExpr(StyleRule rule, V def, const ExpressionInfo& info) const {
const optional<V> opt = getStyleWithExpr<V, T>(rule, info);
if (opt) return *opt;
return def;
}
protected:
/** Returns an ExpressionInfo object for evaluating Lengths. */
virtual ExpressionInfo getExpr() const;
/** /**
* Called by the root when the mouse position changes. * Called by the root when the mouse position changes.
@ -150,9 +210,8 @@ namespace Gui {
* Triggers a click interaction on the hovered element. * Triggers a click interaction on the hovered element.
*/ */
bool handleMouseClick(u32 button, bool down); bool handleMouseClick(ivec2 mousePos, u32 button, bool down);
protected:
Root& root; Root& root;
Props props; Props props;
vec<StyleSheet>& stylesheets; vec<StyleSheet>& stylesheets;
@ -162,6 +221,7 @@ namespace Gui {
std::list<sptr<Element>> children; std::list<sptr<Element>> children;
bool hovered = false; bool hovered = false;
std::function<void(u32, bool)> clickCb = nullptr;
/** The screen offset of the parent. */ /** The screen offset of the parent. */
ivec2 parentOffset {}; ivec2 parentOffset {};

View File

@ -31,14 +31,8 @@ void Gui::Expression::setExpression(string exp) {
while (exp.size()) { while (exp.size()) {
let& c = exp[0]; let& c = exp[0];
// Number or Unit or Keyword
if ((c >= '0' && c <= '9') || c == '.' || (c >= 97 && c <= 122) ||
(nextOperatorIsUnary && (c == '+' || c == '-'))) {
temp += c;
nextOperatorIsUnary = false;
}
// Binary Operator // Binary Operator
else if (!nextOperatorIsUnary && (c == '+' || c == '-' || c == '*' || c == '/' || c == '^')) { if (!nextOperatorIsUnary && (c == '+' || c == '-' || c == '*' || c == '/' || c == '^')) {
if (temp.size()) { if (temp.size()) {
queue.emplace(temp); queue.emplace(temp);
temp = {}; temp = {};
@ -81,6 +75,11 @@ void Gui::Expression::setExpression(string exp) {
operators.pop(); operators.pop();
nextOperatorIsUnary = false; nextOperatorIsUnary = false;
} }
// Number or Unit or Keyword
else {
temp += c;
nextOperatorIsUnary = false;
}
exp.erase(0, 1); exp.erase(0, 1);
} }
@ -105,7 +104,11 @@ void Gui::Expression::setExpression(string exp) {
} }
} }
f32 Gui::Expression::eval() { f32 Gui::Expression::eval(const ExpressionInfo& info) {
if (!expression.size()) {
return nanf("");
}
std::stack<Token> eval {}; std::stack<Token> eval {};
for (usize i = 0; i < expression.size(); i++) { for (usize i = 0; i < expression.size(); i++) {
@ -124,28 +127,28 @@ f32 Gui::Expression::eval() {
switch (t.unit) { switch (t.unit) {
default: default:
throw std::logic_error("Tried to operate with a non-operator token."); throw std::logic_error("Tried to operate with a non-operator token! This is an engine error!");
case UnitOrOperator::ADD: case UnitOrOperator::ADD:
eval.emplace(a.evalValue() + b.evalValue(), UnitOrOperator::REAL_PIXEL); eval.emplace(a.eval(info) + b.eval(info), UnitOrOperator::RAW);
break; break;
case UnitOrOperator::SUBTRACT: case UnitOrOperator::SUBTRACT:
eval.emplace(a.evalValue() - b.evalValue(), UnitOrOperator::REAL_PIXEL); eval.emplace(a.eval(info) - b.eval(info), UnitOrOperator::RAW);
break; break;
case UnitOrOperator::MULTIPLY: case UnitOrOperator::MULTIPLY:
eval.emplace(a.evalValue() * b.evalValue(), UnitOrOperator::REAL_PIXEL); eval.emplace(a.eval(info) * b.eval(info), UnitOrOperator::RAW);
break; break;
case UnitOrOperator::DIVIDE: case UnitOrOperator::DIVIDE:
eval.emplace(a.evalValue() / b.evalValue(), UnitOrOperator::REAL_PIXEL); eval.emplace(a.eval(info) / b.eval(info), UnitOrOperator::RAW);
break; break;
case UnitOrOperator::EXPONENT: case UnitOrOperator::EXPONENT:
eval.emplace(pow(a.evalValue(), b.evalValue()), UnitOrOperator::REAL_PIXEL); eval.emplace(pow(a.eval(info), b.eval(info)), UnitOrOperator::RAW);
break; break;
} }
} }
} }
if (!eval.size()) throw std::runtime_error("Eval stack is empty! This is an engine error!"); if (!eval.size()) throw std::runtime_error("Eval stack is empty! This is an engine error!");
return eval.top().evalValue(); return eval.top().eval(info);
} }
const std::unordered_map<char, u8> Gui::Expression::PRECEDENCE { const std::unordered_map<char, u8> Gui::Expression::PRECEDENCE {
@ -178,9 +181,13 @@ Gui::Expression::Token::Token(const string& str) {
switch (Util::hash(unitStr.data())) { switch (Util::hash(unitStr.data())) {
default: throw std::logic_error("Unknown unit '" + unitStr + "'."); default: throw std::logic_error("Unknown unit '" + unitStr + "'.");
case Util::hash("dp"): unit = UnitOrOperator::DISPLAY_PIXEL; return;
case Util::hash(""): case Util::hash(""):
case Util::hash("px"): unit = UnitOrOperator::REAL_PIXEL; return; case Util::hash("px"): unit = UnitOrOperator::RAW; return;
case Util::hash("dp"): unit = UnitOrOperator::DISPLAY_PIXEL; return;
case Util::hash("cw"): unit = UnitOrOperator::CONTAINER_WIDTH; return;
case Util::hash("ch"): unit = UnitOrOperator::CONTAINER_HEIGHT; return;
case Util::hash("sw"): unit = UnitOrOperator::SELF_WIDTH; return;
case Util::hash("sh"): unit = UnitOrOperator::SELF_HEIGHT; return;
case Util::hash("deg"): unit = UnitOrOperator::DEGREE; return; case Util::hash("deg"): unit = UnitOrOperator::DEGREE; return;
} }
} }
@ -189,12 +196,19 @@ bool Gui::Expression::Token::isOperator() {
return static_cast<u8>(unit) >= 128; return static_cast<u8>(unit) >= 128;
} }
f32 Gui::Expression::Token::evalValue() { f32 Gui::Expression::Token::eval(const ExpressionInfo& info) {
switch (unit) { switch (unit) {
default: throw std::logic_error("Tried to evalValue() on an Operator token."); default: throw std::logic_error("Tried to eval() on an Operator token! This is an engine error!");
case UnitOrOperator::DISPLAY_PIXEL: return val * Gui::PX_SCALE; case UnitOrOperator::DISPLAY_PIXEL: return val * Gui::PX_SCALE;
case UnitOrOperator::REAL_PIXEL: return val; case UnitOrOperator::RAW: return val;
case UnitOrOperator::CONTAINER_WIDTH: {
// std::cout << info.containerSize << ":" << val << std::endl;
return (val / 100.f) * info.containerSize.x;
}
case UnitOrOperator::CONTAINER_HEIGHT: return (val / 100.f) * info.containerSize.y;
case UnitOrOperator::SELF_WIDTH: return (val / 100.f) * info.selfSize.x;
case UnitOrOperator::SELF_HEIGHT: return (val / 100.f) * info.selfSize.y;
case UnitOrOperator::DEGREE: return val * M_PI / 180.f; case UnitOrOperator::DEGREE: return val * M_PI / 180.f;
} }
} }

View File

@ -6,19 +6,31 @@
#include "util/Types.h" #include "util/Types.h"
namespace Gui { namespace Gui {
enum class UnitOrOperator: u8 { struct ExpressionInfo {
DISPLAY_PIXEL, ExpressionInfo() = default;
REAL_PIXEL, ExpressionInfo(ivec2 containerSize, ivec2 selfSize): containerSize(containerSize), selfSize(selfSize) {}
DEGREE,
ADD = 128, ivec2 containerSize;
SUBTRACT, ivec2 selfSize;
MULTIPLY,
DIVIDE,
EXPONENT
}; };
class Expression { class Expression {
enum class UnitOrOperator: u8 {
RAW,
DISPLAY_PIXEL,
CONTAINER_WIDTH,
CONTAINER_HEIGHT,
SELF_WIDTH,
SELF_HEIGHT,
DEGREE,
ADD = 128,
SUBTRACT,
MULTIPLY,
DIVIDE,
EXPONENT
};
struct Token { struct Token {
Token() = default; Token() = default;
explicit Token(const string& str); explicit Token(const string& str);
@ -26,7 +38,7 @@ namespace Gui {
bool isOperator(); bool isOperator();
f32 evalValue(); f32 eval(const ExpressionInfo& info);
f32 val = 0; f32 val = 0;
UnitOrOperator unit; UnitOrOperator unit;
@ -38,12 +50,12 @@ namespace Gui {
void setExpression(string exp); void setExpression(string exp);
f32 eval(); f32 eval(const ExpressionInfo& info);
private: private:
usize hash = 0; usize hash = 0;
std::vector<Token> expression; vec<Token> expression;
const static std::unordered_map<char, u8> PRECEDENCE; const static std::unordered_map<char, u8> PRECEDENCE;
}; };

View File

@ -26,7 +26,10 @@ Gui::Root::Root(Window& window, TextureAtlas& atlas) :
t.printElapsedMs(); t.printElapsedMs();
}); });
// window.input.bindMouseCallback() window.input.bindMouseCallback([&](u32 button, i32 state) {
let pos = window.input.getMousePos();
body->handleMouseClick(pos, button, state == GLFW_PRESS);
});
} }
Gui::Root::~Root() { Gui::Root::~Root() {

View File

@ -5,7 +5,7 @@ const std::unordered_map<string, Gui::StyleRule> Gui::Style::RULE_STRINGS_TO_ENU
{ "size", StyleRule::SIZE }, { "size", StyleRule::SIZE },
{ "margin", StyleRule::MARGIN }, { "margin", StyleRule::MARGIN },
{ "padding", StyleRule::PADDING }, { "padding", StyleRule::PADDING },
{ "GAP", StyleRule::GAP }, { "gap", StyleRule::GAP },
{ "layout", StyleRule::LAYOUT }, { "layout", StyleRule::LAYOUT },
{ "direction", StyleRule::DIRECTION }, { "direction", StyleRule::DIRECTION },
{ "h_align", StyleRule::H_ALIGN }, { "h_align", StyleRule::H_ALIGN },

View File

@ -143,10 +143,10 @@ namespace Gui {
(std::is_integral_v<N> || std::is_floating_point_v<N>) && (std::is_integral_v<N> || std::is_floating_point_v<N>) &&
L == ValueType::LENGTH, bool> = true> L == ValueType::LENGTH, bool> = true>
optional<N> get(StyleRule rule) const { optional<N> get(StyleRule rule, const ExpressionInfo& info) const {
let raw = get<Gui::Expression>(rule); let raw = get<Gui::Expression>(rule);
if (!raw) return std::nullopt; if (!raw) return std::nullopt;
return raw->eval(); return raw->eval(info);
} }
/** /**
@ -159,11 +159,11 @@ namespace Gui {
std::is_same_v<VN, glm::vec<VN::length(), typename VN::value_type>> && std::is_same_v<VN, glm::vec<VN::length(), typename VN::value_type>> &&
L == ValueType::LENGTH, bool> = true> L == ValueType::LENGTH, bool> = true>
optional<VN> get(StyleRule rule) const { optional<VN> get(StyleRule rule, const ExpressionInfo& info) const {
let raw = get<array<Gui::Expression, VN::length()>>(rule); let raw = get<array<Gui::Expression, VN::length()>>(rule);
if (!raw) return std::nullopt; if (!raw) return std::nullopt;
VN vec; VN vec;
for (usize i = 0; i < VN::length(); i++) vec[i] = (*raw)[i].eval(); for (usize i = 0; i < VN::length(); i++) vec[i] = (*raw)[i].eval(info);
return vec; return vec;
} }

View File

@ -23,7 +23,6 @@ MenuSandbox::MenuSandbox(Client& client, Gui::Root& root, sptr<Gui::Element> san
sandboxRoot(sandboxRoot) {} sandboxRoot(sandboxRoot) {}
void MenuSandbox::reset() { void MenuSandbox::reset() {
// container->remove("error");
sandboxRoot->clear(); sandboxRoot->clear();
core = {}; core = {};
mod = {}; mod = {};
@ -58,22 +57,12 @@ void MenuSandbox::loadApi() {
void MenuSandbox::load(const SubgameDef& subgame) { void MenuSandbox::load(const SubgameDef& subgame) {
reset(); reset();
subgameName = subgame.config.name; subgameName = subgame.config.name;
// try {
loadAndRunMod(subgame.subgamePath + "/../../assets/base"); loadAndRunMod(subgame.subgamePath + "/../../assets/base");
loadAndRunMod(subgame.subgamePath + "/menu"); loadAndRunMod(subgame.subgamePath + "/menu");
// }
// catch (const std::runtime_error& e) {
// showError(e.what(), subgame.config.name);
// }
}
void MenuSandbox::windowResized() {
// builder.build(win);
} }
void MenuSandbox::update(double delta) { void MenuSandbox::update(double delta) {
// builder.update();
core["__builtin"]["update_delayed_functions"](); core["__builtin"]["update_delayed_functions"]();
} }

View File

@ -25,8 +25,6 @@ public:
void update(double delta) override; void update(double delta) override;
void windowResized();
using LuaParser::update; using LuaParser::update;
private: private:

View File

@ -87,19 +87,20 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
for (usize i = 0; i < subgames.size(); i++) { for (usize i = 0; i < subgames.size(); i++) {
let& subgame = subgames[i]; let& subgame = subgames[i];
navigationList->append<Gui::BoxElement>({
let elem = navigationList->append<Gui::BoxElement>({
.classes = { "navigationButton" }, .classes = { "navigationButton" },
.styles = {{ .styles = {{
{ Gui::StyleRule::BACKGROUND, string("crop(0, 0, 16, 16, " + subgame.iconRef->name + ")") }, { 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 + ")") } { Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, " + subgame.iconRef->name + ")") }
}} }}
}); });
//
// button->setCallback(Element::CallbackType::PRIMARY, [&](bool down, ivec2) { elem->onClick([&](u32 button, bool down) {
// if (!down) return; if (button != GLFW_MOUSE_BUTTON_1) return;
// selectedSubgame = &subgame; selectedSubgame = &subgame;
// sandbox.load(*selectedSubgame); sandbox.load(*selectedSubgame);
// }); });
} }
if (subgames.size() > 0) { if (subgames.size() > 0) {

View File

@ -7,6 +7,7 @@
#include "client/gui/TextElement.h" #include "client/gui/TextElement.h"
static const Gui::Expression parseObjectToExpr(sol::object value) { static const Gui::Expression parseObjectToExpr(sol::object value) {
if (!value.valid()) return Gui::Expression("");
if (value.is<f32>()) return Gui::Expression(std::to_string(value.as<f32>()) + "dp"); if (value.is<f32>()) return Gui::Expression(std::to_string(value.as<f32>()) + "dp");
if (value.is<string>()) return Gui::Expression(value.as<string>()); if (value.is<string>()) return Gui::Expression(value.as<string>());
throw std::invalid_argument("Object cannot be converted to an expression."); throw std::invalid_argument("Object cannot be converted to an expression.");
@ -21,7 +22,8 @@ static const array<Gui::Expression, D> parseLengthTableVal(sol::object value) {
vec<Gui::Expression> exprs {}; vec<Gui::Expression> exprs {};
exprs.reserve(t.size()); exprs.reserve(t.size());
for (let& v : t) exprs.emplace_back(parseObjectToExpr(v.second)); for (usize i = 1; i <= t.size(); i++)
exprs.emplace_back(parseObjectToExpr(t.get<sol::object>(i)));
for (usize i = 0; i < arr.size() / exprs.size(); i++) for (usize i = 0; i < arr.size() / exprs.size(); i++)
for (usize j = 0; j < exprs.size(); j++) for (usize j = 0; j < exprs.size(); j++)

View File

@ -6,6 +6,7 @@ zepha.set_gui(zepha.gui(function()
Gui.Text { Gui.Text {
size = { 64, 4 }, size = { 64, 4 },
pos = { "50cw", 50 },
content = "Parentheses" content = "Parentheses"
} }
} }

View File

@ -1,52 +1,63 @@
local menu = zepha.build_gui(function() local menu = zepha.gui(function()
return Gui.Box { return Gui.Box {
background = 'zeus_background_christmas_night', background = 'zeus_background_christmas_night',
Gui.Box { Gui.Box {
key = 'particle_wrap', -- id = 'particle_wrap',
size = { '100%', '100%' } size = { '100cw', '100ch' }
}, },
Gui.Box { Gui.Box {
pos = { '20% - 50s%', 0 }, gap = 4,
size = { 102, '100%' }, padding = 8,
size = { 102, '100ch' },
pos = { '20cw - 50sw', 0 },
background = '#0135', background = '#0135',
Gui.Box { Gui.Box {
pos = { 8, 8 }, size = { nil, 30 },
size = { 86, 30 }, margin = { 0, 0, 0, 8 },
background = 'zeus_logo' background = 'zeus_logo'
}, },
Gui.Button { Gui.Box {
id = 'button_play', -- id = 'button_play',
-- callbacks = { -- callbacks = {
-- primary = function() zepha.start_game_local() end -- primary = function() zepha.start_game_local() end
-- }, -- },
pos = { 6, 50 }, padding = 5,
size = { 90, 20 }, size = { nil, 20 },
content = 'Local Play', cursor = "pointer",
background = 'crop(0, 0, 90, 20, zeus_button)', background = 'crop(0, 0, 90, 20, zeus_button)',
background_hover = 'crop(0, 20, 90, 20, zeus_button)' background_hover = 'crop(0, 20, 90, 20, zeus_button)',
Gui.Text {
content = 'Local Play'
}
}, },
Gui.Button { Gui.Box {
id = 'button_servers', -- id = 'button_servers',
-- callbacks = { -- callbacks = {
-- primary = function() zepha.start_game() end -- primary = function() zepha.start_game() end
-- }, -- },
pos = { 6, 74 }, padding = 5,
size = { 90, 20 }, size = { nil, 20 },
content = 'Browse Servers',
cursor = "pointer",
background = 'crop(0, 0, 90, 20, zeus_button)', background = 'crop(0, 0, 90, 20, zeus_button)',
background_hover = 'crop(0, 20, 90, 20, zeus_button)' background_hover = 'crop(0, 20, 90, 20, zeus_button)',
Gui.Text {
content = 'Browse Servers'
}
} }
} }
} }
@ -62,17 +73,17 @@ 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 {
pos = { 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()