More Lua GuiElement functionality.

master
Auri 2021-08-24 01:48:53 -07:00
parent 1c58797480
commit 9aec996b74
27 changed files with 662 additions and 791 deletions

View File

@ -5,7 +5,7 @@ zepha.player:set_hud(zepha.gui(function()
Gui.Box {
-- id = "crosshair",
size = { "22dp", "22dp" },
size = { "22px", "22px" },
pos = { "50cw - 50sw", "50ch - 50sh" },
background = "base:crosshair"

View File

@ -184,8 +184,8 @@ add_library(Zepha_Core
lua/usertype/InventoryList.h
lua/usertype/ItemStack.cpp
lua/usertype/ItemStack.h
lua/usertype/LuaGuiElement.cpp
lua/usertype/LuaGuiElement.h
lua/usertype/GuiElement.cpp
lua/usertype/GuiElement.h
lua/usertype/Player.cpp
lua/usertype/Player.h
lua/usertype/SubgameUsertype.h

View File

@ -5,8 +5,8 @@
#include "client/graph/mesh/EntityMesh.h"
void Gui::BoxElement::updateElement() {
const let bgRule = hovered && getStyle(StyleRule::BACKGROUND_HOVER) ?
StyleRule::BACKGROUND_HOVER : StyleRule::BACKGROUND;
const let bgRule = hovered && getStyle(Prop::BACKGROUND_HOVER) ?
Prop::BACKGROUND_HOVER : Prop::BACKGROUND;
let rawBg = getStyle(bgRule);
@ -20,7 +20,7 @@ void Gui::BoxElement::updateElement() {
curBg = rawBg;
if (isDirty) {
const let bgColor = getStyle<vec4, ValueType::COLOR>(bgRule);
const let bgColor = getStyle<vec4, Type::COLOR>(bgRule);
const string bgImage = getStyle<string>(bgRule, "");
let mesh = std::make_unique<EntityMesh>();
@ -48,7 +48,7 @@ void Gui::BoxElement::updateElement() {
entity.setModel(model);
}
let margin = getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
let margin = getStyle<ivec4, Type::LENGTH>(Prop::MARGIN, {});
entity.setScale(vec3(getComputedSize(), 0));
entity.setPos(vec3(getComputedScreenPos() + ivec2 { margin.x, margin.y }, 0));

View File

@ -8,17 +8,17 @@ Gui::Element::~Element() {
for (let& child : children) child->parent = nullptr;
}
const Gui::Props& Gui::Element::getProps() const {
return props;
}
void Gui::Element::setProps(const Props& props) {
this->props = props;
updateElement();
}
void Gui::Element::setStyle(StyleRule style, const any& value) {
props.styles.rules[style] = value;
}
const optional<any> Gui::Element::getStyleRaw(Gui::StyleRule style) const {
return getStyle(style);
void Gui::Element::setProp(Gui::Prop prop, const any& value) {
props.props[prop] = value;
}
sptr<Gui::Element> Gui::Element::get(u32 ind) {
@ -26,10 +26,33 @@ sptr<Gui::Element> Gui::Element::get(u32 ind) {
return children[ind];
}
sptr<Gui::Element> Gui::Element::get(const string& id) {
for (let& child : children) {
if (child->props.get<string>(Prop::ID) == id) return child;
}
for (let& child : children) {
let res = child->get(id);
if (res != nullptr) return res;
}
return nullptr;
}
void Gui::Element::clear() {
children.clear();
}
void Gui::Element::remove() {
if (parent) {
for (let it = parent->children.begin(); it != parent->children.end(); it++) {
if (it->get() == this) {
parent->children.erase(it);
break;
}
}
parent = nullptr;
}
}
void Gui::Element::onClick(const std::function<void(i32, bool)>& cb) {
clickCb = cb;
}
@ -42,7 +65,7 @@ Gui::ExpressionInfo Gui::Element::getExpr() const {
}
ivec2 Gui::Element::getComputedSize() const {
let size = getStyleWithExpr<vec2, ValueType::LENGTH>(StyleRule::SIZE, vec2(nanf("")),
let size = getStyleWithExpr<vec2, Type::LENGTH>(Prop::SIZE, vec2(nanf("")),
{ parent ? parent->computedSize : 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);
@ -50,24 +73,18 @@ ivec2 Gui::Element::getComputedSize() const {
return size;
}
ivec2 Gui::Element::getComputedOuterSize() const {
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() const {
let size = getComputedSize();
let padding = getStyle<ivec4, ValueType::LENGTH>(StyleRule::PADDING, {});
let padding = getStyle<ivec4, Type::LENGTH>(Prop::PADDING, {});
return glm::max(ivec2 { size.x - padding.x - padding.z, size.y - padding.y - padding.w }, 0);
}
ivec2 Gui::Element::getExplicitSize() const {
return getStyle<ivec2, ValueType::LENGTH>(StyleRule::SIZE, ivec2(-1));
return getStyle<ivec2, Type::LENGTH>(Prop::SIZE, ivec2(-1));
}
ivec2 Gui::Element::getComputedPos() const {
return getStyle<ivec2, ValueType::LENGTH>(StyleRule::POS, layoutPosition);
return getStyle<ivec2, Type::LENGTH>(Prop::POS, layoutPosition);
}
ivec2 Gui::Element::getComputedScreenPos() const {
@ -91,7 +108,7 @@ bool Gui::Element::handleMouseHover(ivec2 mousePos, bool& pointer) {
bool intersects = mousePos.x >= pos.x && mousePos.x <= pos.x + size.x &&
mousePos.y >= pos.y && mousePos.y <= pos.y + size.y;
let cursor = getStyle<string>(StyleRule::CURSOR);
let cursor = getStyle<string>(Prop::CURSOR);
if (intersects && cursor) pointer = *cursor == "pointer";
if (hovered != intersects) {
@ -126,12 +143,12 @@ void Gui::Element::updateElement() {
}
void Gui::Element::layoutChildren() {
const string& layout = getStyle<string>(StyleRule::LAYOUT, "");
const string& layout = getStyle<string>(Prop::LAYOUT, "");
switch (Util::hash(layout.data())) {
default:
case Util::hash("flex"): {
const string& direction = getStyle<string>(StyleRule::DIRECTION, "");
const string& direction = getStyle<string>(Prop::DIRECTION, "");
/**
* The primary flex direction. Stored as a bool but interpreted as an index into a vec2.
@ -140,8 +157,8 @@ void Gui::Element::layoutChildren() {
const bool primary = direction != "row";
const string& hAlignRaw = getStyle<string>(StyleRule::H_ALIGN, "");
const string& vAlignRaw = getStyle<string>(StyleRule::V_ALIGN, "");
const string& hAlignRaw = getStyle<string>(Prop::H_ALIGN, "");
const string& vAlignRaw = getStyle<string>(Prop::V_ALIGN, "");
/**
* Parsed alignment of the horizontal and vertical axes.
@ -157,8 +174,8 @@ void Gui::Element::layoutChildren() {
* The element gap across the primary axis.
*/
const i32 gap = getStyle<ivec2, ValueType::LENGTH>(StyleRule::GAP, ivec2(0))[primary];
const ivec4& padding = getStyle<ivec4, ValueType::LENGTH>(StyleRule::PADDING, ivec4 {});
const i32 gap = getStyle<ivec2, Type::LENGTH>(Prop::GAP, ivec2(0))[primary];
const ivec4& padding = getStyle<ivec4, Type::LENGTH>(Prop::PADDING, ivec4 {});
/*
* Calculates the explicit spaced used up by children across the primary axis,
@ -174,7 +191,7 @@ void Gui::Element::layoutChildren() {
let childExplicitSize = child->getExplicitSize();
if (childExplicitSize[primary] != -1) explicitSize += childExplicitSize[primary];
else implicitCount++;
let childMargin = child->getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
let childMargin = child->getStyle<ivec4, Type::LENGTH>(Prop::MARGIN, {});
explicitSize += childMargin[primary] + childMargin[primary + 2];
}
@ -201,7 +218,7 @@ void Gui::Element::layoutChildren() {
for (const let& child : children) {
let childExplicitSize = child->getExplicitSize();
let childMargin = child->getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
let childMargin = child->getStyle<ivec4, Type::LENGTH>(Prop::MARGIN, {});
child->layoutSize[primary] =
(childExplicitSize[primary] == -1 && align[primary] == 2) ? implicitElemSize : 0;
@ -227,4 +244,4 @@ void Gui::Element::layoutChildren() {
break;
}
}
}
}

View File

@ -3,10 +3,10 @@
#include <functional>
#include "client/gui/Gui.h"
#include "client/gui/Style.h"
#include "world/dim/ent/DrawableEntity.h"
#include "util/Types.h"
#include "client/gui/Style.h"
class Window;
class Renderer;
@ -25,23 +25,18 @@ namespace Gui {
friend class TextElement;
public:
struct Props {;
string id {};
vec<string> classes {};
Style styles {};
};
Element(Root& root, vec<StyleSheet>& stylesheets): root(root), stylesheets(stylesheets) {}
~Element();
/** Gets a reference to the element's props. */
const Props& getProps() const;
/** Sets the element's props to the struct specified. */
virtual void setProps(const Props& props);
/** Sets a style rule on the element. */
virtual void setStyle(StyleRule style, const std::any& value);
virtual const optional<any> getStyleRaw(StyleRule style) const;
virtual void setProp(Prop prop, const std::any& value);
/** Recalculates the element based on its props. Call when props or stylesheets change. */
virtual void updateElement();
@ -51,6 +46,8 @@ namespace Gui {
sptr<Element> get(u32 ind);
sptr<Element> get(const string& id);
template<typename E, std::enable_if_t<std::is_base_of_v<Element, E>, bool> = true>
sptr<E> get(u32 ind) {
return std::dynamic_pointer_cast<E>(get(ind));
@ -92,14 +89,13 @@ namespace Gui {
void clear();
void remove();
void onClick(const std::function<void(i32, bool)>& cb);
/** Returns the element's computed size. */
virtual ivec2 getComputedSize() const;
/** Returns the element's computed size + margins. */
virtual ivec2 getComputedOuterSize() const;
/** Returns the element's computed content size, which is its size - padding. */
virtual ivec2 getComputedContentSize() const;
@ -113,11 +109,13 @@ namespace Gui {
virtual ivec2 getComputedScreenPos() const;
/** Gets a style value from the element's styles or the root's stylesheets. */
const optional<any> getStyle(StyleRule rule) const {
const optional<any> opt = props.styles.get(rule);
const optional<any> getStyle(Prop rule) const {
const optional<any> opt = props.get(rule);
if (opt) return *opt;
const let& classes = props.get<vec<string>>(Prop::CLASS);
if (!classes) return std::nullopt;
for (const let& ss : stylesheets) {
for (const string& className : props.classes) {
for (const string& className : *classes) {
const let& styles = ss.find(className);
if (styles == ss.end()) continue;
const optional<any> opt = styles->second.get(rule);
@ -128,14 +126,15 @@ namespace Gui {
}
/** Gets a generic 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>
template<typename V, Type T = Type::LITERAL, std::enable_if_t<T != Type::LENGTH, bool> = true>
const optional<V> getStyle(StyleRule rule) const {
const optional<V> opt = props.styles.get<V, T>(rule);
const optional<V> getStyle(Prop rule) const {
const optional<V> opt = props.get<V, T>(rule);
if (opt) return *opt;
const let& classes = props.get<vec<string>>(Prop::CLASS);
if (!classes) return std::nullopt;
for (const let& ss : stylesheets) {
for (const string& className : props.classes) {
for (const string& className : *classes) {
const let& styles = ss.find(className);
if (styles == ss.end()) continue;
const optional<V> opt = styles->second.get<V, T>(rule);
@ -146,15 +145,16 @@ namespace Gui {
}
/** 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>
template<typename V, Type T = Type::LITERAL, std::enable_if_t<T == Type::LENGTH, bool> = true>
const optional<V> getStyle(StyleRule rule) const {
const optional<V> getStyle(Prop rule) const {
ExpressionInfo info = getExpr();
const optional<V> opt = props.styles.get<V, T>(rule, info);
const optional<V> opt = props.get<V, T>(rule, info);
if (opt) return *opt;
const let& classes = props.get<vec<string>>(Prop::CLASS);
if (!classes) return std::nullopt;
for (const let& ss : stylesheets) {
for (const string& className : props.classes) {
for (const string& className : *classes) {
const let& styles = ss.find(className);
if (styles == ss.end()) continue;
const optional<V> opt = styles->second.get<V, T>(rule, info);
@ -165,14 +165,15 @@ namespace Gui {
}
/** 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>
template<typename V, Type T = Type::LITERAL, std::enable_if_t<T == Type::LENGTH, bool> = true>
const optional<V> getStyleWithExpr(StyleRule rule, const ExpressionInfo& expr) const {
const optional<V> opt = props.styles.get<V, T>(rule, expr);
const optional<V> getStyleWithExpr(Prop rule, const ExpressionInfo& expr) const {
const optional<V> opt = props.get<V, T>(rule, expr);
if (opt) return *opt;
const let& classes = props.get<vec<string>>(Prop::CLASS);
if (!classes) return std::nullopt;
for (const let& ss : stylesheets) {
for (const string& className : props.classes) {
for (const string& className : *classes) {
const let& styles = ss.find(className);
if (styles == ss.end()) continue;
const optional<V> opt = styles->second.get<V, T>(rule, expr);
@ -183,19 +184,18 @@ namespace Gui {
}
/** Gets a style value from the element's styles or the root's stylesheets. */
template<typename V, ValueType T = ValueType::LITERAL>
template<typename V, Type T = Type::LITERAL>
const V getStyle(StyleRule rule, V def) const {
const V getStyle(Prop rule, V def) const {
const optional<V> opt = getStyle<V, T>(rule);
if (opt) return *opt;
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>
template<typename V, Type T = Type::LITERAL, std::enable_if_t<T == Type::LENGTH, bool> = true>
const V getStyleWithExpr(StyleRule rule, V def, const ExpressionInfo& info) const {
const V getStyleWithExpr(Prop rule, V def, const ExpressionInfo& info) const {
const optional<V> opt = getStyleWithExpr<V, T>(rule, info);
if (opt) return *opt;
return def;

View File

@ -10,17 +10,15 @@ Gui::Root::Root(Window& window, TextureAtlas& atlas) :
body(make_shared<BoxElement>(*this, stylesheets)) {
const ivec2 size = window.getSize();
body->setProps({
.id = "body",
.styles = {{
{ StyleRule::SIZE, array<Expression, 2> {
Expression(std::to_string(size.x)), Expression(std::to_string(size.y)) }}
}}
});
using Expr = Expression;
body->setProps({{
{ Prop::ID, string("body") },
{ Prop::SIZE, array<Expr, 2> { Expr(std::to_string(size.x)), Expr(std::to_string(size.y)) }}
}});
callbacks.emplace_back(window.resize.bind([&](ivec2 size) {
body->setStyle(StyleRule::SIZE, array<Expression, 2> {
Expression(std::to_string(size.x)), Expression(std::to_string(size.y)) });
body->setProp(Prop::SIZE, array<Expr, 2> {
Expr(std::to_string(size.x)), Expr(std::to_string(size.y)) });
Timer t("Resize UI");
body->updateElement();
t.printElapsedMs();
@ -36,7 +34,7 @@ Gui::Root::~Root() {
window.setCursorHand(false);
}
void Gui::Root::addStylesheet(const std::unordered_map<string, Style>& sheet) {
void Gui::Root::addStylesheet(const StyleSheet& sheet) {
stylesheets.emplace_back(sheet);
}

View File

@ -25,7 +25,7 @@ namespace Gui {
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 Props& props = {}, const vec<sptr<Element>>& children = {}) {
let elem = make_shared<E>(*this, stylesheets);
elem->setProps(props);
@ -39,7 +39,7 @@ namespace Gui {
* styles for elements with specific classes.
*/
void addStylesheet(const std::unordered_map<string, Style>& sheet);
void addStylesheet(const StyleSheet& sheet);
/** Processes mouse events. */
void update();

View File

@ -1,20 +1,22 @@
#include "Style.h"
const std::unordered_map<string, Gui::StyleRule> Gui::Style::RULE_STRINGS_TO_ENUMS = {
{ "pos", StyleRule::POS },
{ "size", StyleRule::SIZE },
{ "margin", StyleRule::MARGIN },
{ "padding", StyleRule::PADDING },
{ "gap", StyleRule::GAP },
{ "layout", StyleRule::LAYOUT },
{ "direction", StyleRule::DIRECTION },
{ "h_align", StyleRule::H_ALIGN },
{ "v_align", StyleRule::V_ALIGN },
{ "cursor", StyleRule::CURSOR },
{ "overflow", StyleRule::OVERFLOW },
{ "background", StyleRule::BACKGROUND },
{ "background_hover", StyleRule::BACKGROUND_HOVER },
{ "content", StyleRule::CONTENT },
{ "text_size", StyleRule::TEXT_SIZE },
{ "text_color", StyleRule::TEXT_COLOR }
const std::unordered_map<string, Gui::Prop> Gui::Props::PROP_NAMES_TO_ENUM = {
{ "id", Prop::ID },
{ "class", Prop::CLASS },
{ "pos", Prop::POS },
{ "size", Prop::SIZE },
{ "margin", Prop::MARGIN },
{ "padding", Prop::PADDING },
{ "gap", Prop::GAP },
{ "layout", Prop::LAYOUT },
{ "direction", Prop::DIRECTION },
{ "h_align", Prop::H_ALIGN },
{ "v_align", Prop::V_ALIGN },
{ "cursor", Prop::CURSOR },
{ "overflow", Prop::OVERFLOW },
{ "background", Prop::BACKGROUND },
{ "background_hover", Prop::BACKGROUND_HOVER },
{ "content", Prop::CONTENT },
{ "text_size", Prop::TEXT_SIZE },
{ "text_color", Prop::TEXT_COLOR }
};

View File

@ -5,7 +5,10 @@
#include "client/gui/Expression.h"
namespace Gui {
enum class StyleRule {
enum class Prop {
ID,
CLASS,
POS,
SIZE,
MARGIN,
@ -27,25 +30,25 @@ namespace Gui {
TEXT_COLOR
};
enum class ValueType {
enum class Type {
LITERAL,
COLOR,
LENGTH
};
class Style {
class Props {
public:
Style() = default;
Style(const std::unordered_map<StyleRule, any>& rules): rules(rules) {}
Props() = default;
Props(const std::unordered_map<Prop, any>& props): props(props) {}
/**
* Simple get. Returns an optional containing an any of
* the Rule's value, or an empty optional if the rule is not defined.
*/
const optional<any> get(StyleRule rule) const {
const let it = rules.find(rule);
if (it == rules.end()) return std::nullopt;
const optional<any> get(Prop prop) const {
const let it = props.find(prop);
if (it == props.end()) return std::nullopt;
return optional<any>(it->second);
}
@ -54,14 +57,14 @@ namespace Gui {
* or an empty optional if the rule is not defined, or not the type V.
*/
template<typename V, ValueType T = ValueType::LITERAL, std::enable_if_t<
template<typename V, Type T = Type::LITERAL, std::enable_if_t<
!(std::is_integral_v<V> ||
std::is_floating_point_v<V> ||
std::is_same_v<V, string>) &&
T == ValueType::LITERAL, bool> = true>
T == Type::LITERAL, bool> = true>
const optional<V> get(StyleRule rule) const {
const optional<any> raw = get(rule);
const optional<V> get(Prop prop) const {
const optional<any> raw = get(prop);
if (!raw || raw->type() != typeid(V)) return std::nullopt;
return any_cast<V>(*raw);
}
@ -70,15 +73,14 @@ namespace Gui {
* get<O, T> specialization for string-like values.
*/
template<typename S, ValueType T = ValueType::LITERAL, std::enable_if_t<
template<typename S, Type T = Type::LITERAL, std::enable_if_t<
std::is_same_v<S, string> &&
T == ValueType::LITERAL, bool> = true>
T == Type::LITERAL, bool> = true>
const optional<S> get(StyleRule rule) const {
const optional<any> raw = get(rule);
const optional<S> get(Prop prop) const {
const optional<any> raw = get(prop);
if (!raw) return std::nullopt;
if (raw->type() == typeid(string)) return any_cast<string>(*raw);
// if (raw->type() == typeid(const char*)) return any_cast<const char*>(*raw);
return std::nullopt;
}
@ -86,26 +88,15 @@ namespace Gui {
* get<O, T> specialization for numeric values.
*/
template<typename N, ValueType T = ValueType::LITERAL, std::enable_if_t<
template<typename N, Type T = Type::LITERAL, std::enable_if_t<
(std::is_integral_v<N> ||
std::is_floating_point_v<N>) &&
T == ValueType::LITERAL, bool> = true>
T == Type::LITERAL, bool> = true>
const optional<N> get(StyleRule rule) const {
const optional<any> raw = get(rule);
const optional<N> get(Prop prop) const {
const optional<any> raw = get(prop);
if (!raw) return std::nullopt;
if (raw->type() == typeid(N)) return any_cast<N>(*raw);
// if (raw->type() == typeid(i8)) return static_cast<N>(any_cast<i8>(*raw));
// if (raw->type() == typeid(i16)) return static_cast<N>(any_cast<i16>(*raw));
// if (raw->type() == typeid(i32)) return static_cast<N>(any_cast<i32>(*raw));
// if (raw->type() == typeid(i64)) return static_cast<N>(any_cast<i64>(*raw));
// if (raw->type() == typeid(f32)) return static_cast<N>(any_cast<f32>(*raw));
// if (raw->type() == typeid(f64)) return static_cast<N>(any_cast<f64>(*raw));
// if (raw->type() == typeid(u8)) return static_cast<N>(any_cast<u8>(*raw));
// if (raw->type() == typeid(u16)) return static_cast<N>(any_cast<u16>(*raw));
// if (raw->type() == typeid(u32)) return static_cast<N>(any_cast<u32>(*raw));
// if (raw->type() == typeid(u64)) return static_cast<N>(any_cast<u64>(*raw));
// if (raw->type() == typeid(usize)) return static_cast<N>(any_cast<usize>(*raw));
return std::nullopt;
}
@ -114,23 +105,18 @@ namespace Gui {
* which is interpreted as a vec4 color from several different formats.
*/
template<typename V, ValueType C, std::enable_if_t<
template<typename V, Type C, std::enable_if_t<
std::is_same_v<V, vec4> &&
C == ValueType::COLOR, bool> = true>
C == Type::COLOR, bool> = true>
const optional<V> get(StyleRule rule, optional<V> def = std::nullopt) const {
const optional<any> raw = get(rule);
const optional<V> get(Prop prop, optional<V> def = std::nullopt) const {
const optional<any> raw = get(prop);
if (!raw) return std::nullopt;
if (raw->type() == typeid(void) && def) return *def;
if (raw->type() == typeid(void)) throw std::runtime_error("Field is missing with no default.");
if (raw->type() == typeid(vec4)) return any_cast<vec4>(*raw);
try {
if (raw->type() == typeid(string)) return Util::hexToColorVec(any_cast<string>(*raw));
// if (raw->type() == typeid(const char*)) return Util::hexToColorVec(string(any_cast<const char*>(*raw)));
}
catch (std::exception) {
return std::nullopt;
}
try { if (raw->type() == typeid(string)) return Util::hexToColorVec(any_cast<string>(*raw)); }
catch (std::exception) { return std::nullopt; }
return std::nullopt;
}
@ -139,12 +125,12 @@ namespace Gui {
* which is interpreted as a length.
*/
template<typename N, ValueType L, std::enable_if_t<
template<typename N, Type L, std::enable_if_t<
(std::is_integral_v<N> || std::is_floating_point_v<N>) &&
L == ValueType::LENGTH, bool> = true>
L == Type::LENGTH, bool> = true>
optional<N> get(StyleRule rule, const ExpressionInfo& info) const {
let raw = get<Gui::Expression>(rule);
optional<N> get(Prop prop, const ExpressionInfo& info) const {
let raw = get<Gui::Expression>(prop);
if (!raw) return std::nullopt;
return raw->eval(info);
}
@ -154,13 +140,13 @@ namespace Gui {
* which is interpreted as a length.
*/
template<typename VN, ValueType L, std::enable_if_t<
template<typename VN, Type 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>
L == Type::LENGTH, bool> = true>
optional<VN> get(StyleRule rule, const ExpressionInfo& info) const {
let raw = get<array<Gui::Expression, VN::length()>>(rule);
optional<VN> get(Prop prop, const ExpressionInfo& info) const {
let raw = get<array<Gui::Expression, VN::length()>>(prop);
if (!raw) return std::nullopt;
VN vec;
for (usize i = 0; i < VN::length(); i++) vec[i] = (*raw)[i].eval(info);
@ -173,18 +159,18 @@ namespace Gui {
* Throws if the rule is defined to a different type.
*/
template<typename V, ValueType T = ValueType::LITERAL>
template<typename V, Type T = Type::LITERAL>
const V get(StyleRule rule, V def) const {
const optional<V> raw = get<V, T>(rule);
const V get(Prop prop, V def) const {
const optional<V> raw = get<V, T>(prop);
if (!raw) return def;
return *raw;
}
std::unordered_map<StyleRule, any> rules {};
std::unordered_map<Prop, any> props {};
const static std::unordered_map<string, StyleRule> RULE_STRINGS_TO_ENUMS;
const static std::unordered_map<string, Prop> PROP_NAMES_TO_ENUM;
};
typedef std::unordered_map<string, Style> StyleSheet;
typedef std::unordered_map<string, Props> StyleSheet;
}

View File

@ -7,7 +7,7 @@
#include "client/graph/mesh/EntityMesh.h"
void Gui::TextElement::updateElement() {
const string text = getStyle<string>(StyleRule::CONTENT, "");
const string text = getStyle<string>(Prop::CONTENT, "");
if (!font) font = std::make_unique<Font>(root.atlas, root.atlas["font"]);
@ -15,8 +15,8 @@ void Gui::TextElement::updateElement() {
if (hash != newHash) {
hash = newHash;
vec4 textColor = getStyle<vec4, ValueType::COLOR>(StyleRule::TEXT_COLOR, vec4(1));
vec4 backgroundColor = getStyle<vec4, ValueType::COLOR>(StyleRule::BACKGROUND, vec4(0));
vec4 textColor = getStyle<vec4, Type::COLOR>(Prop::TEXT_COLOR, vec4(1));
vec4 backgroundColor = getStyle<vec4, Type::COLOR>(Prop::BACKGROUND, vec4(0));
u32 ind = 0;
u32 width = 0;
@ -190,8 +190,8 @@ void Gui::TextElement::updateElement() {
}
let scale = getStyle<f32, ValueType::LENGTH>(StyleRule::TEXT_SIZE, 3.f);
let margin = getStyle<ivec4, ValueType::LENGTH>(StyleRule::MARGIN, {});
let scale = getStyle<f32, Type::LENGTH>(Prop::TEXT_SIZE, 3.f);
let margin = getStyle<ivec4, Type::LENGTH>(Prop::MARGIN, {});
entity.setScale(vec3(scale, scale, 0));
entity.setPos(vec3(getComputedScreenPos() + ivec2 { margin.x, margin.y }, 0));

View File

@ -14,7 +14,7 @@
#include "lua/modules/Time.h"
#include "lua/modules/mSetGui.h"
#include "lua/modules/mStartGame.h"
#include "lua/usertype/LuaGuiElement.h"
#include "lua/usertype/GuiElement.h"
MenuSandbox::MenuSandbox(Client& client, Gui::Root& root, sptr<Gui::Element> sandboxRoot) :
LuaParser(*client.game),
@ -152,14 +152,12 @@ void MenuSandbox::showError(const string& err) {
using Expr = Gui::Expression;
sandboxRoot->clear();
sandboxRoot->append<Gui::TextElement>({
.styles = {{
{ Gui::StyleRule::CONTENT, errPrefixText + err },
{ Gui::StyleRule::TEXT_SIZE, Expr("2px") },
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr("100dp"), Expr("-1") } },
{ Gui::StyleRule::MARGIN, array<Expr, 4> { Expr("4dp"), Expr("4dp"), Expr("4dp"), Expr("4dp") } }
}}
});
sandboxRoot->append<Gui::TextElement>({{
{ Gui::Prop::CONTENT, errPrefixText + err },
{ Gui::Prop::TEXT_SIZE, Expr("2px") },
{ Gui::Prop::SIZE, array<Expr, 2> { Expr("100dp"), Expr("-1") } },
{ Gui::Prop::MARGIN, array<Expr, 4> { Expr("4dp"), Expr("4dp"), Expr("4dp"), Expr("4dp") } }
}});
}
sol::protected_function_result MenuSandbox::errorCallback(sol::protected_function_result r) {

View File

@ -19,14 +19,12 @@ ConnectScene::ConnectScene(Client& client, Address addr) : Scene(client),
client.renderer.setClearColor(10, 10, 10);
using Expr = Gui::Expression;
status = root.body->append<Gui::TextElement>({
.styles = {{
{ Gui::StyleRule::TEXT_SIZE, Expr("2px") },
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr("100dp"), Expr("-1") } },
{ Gui::StyleRule::CONTENT, string("`c1Connecting to `b`c0" + addr.toString() + "`r`c1...") },
{ Gui::StyleRule::MARGIN, array<Expr, 4> { Expr("4dp"), Expr("4dp"), Expr("4dp"), Expr("4dp") } }
}}
});
status = root.body->append<Gui::TextElement>({{
{ Gui::Prop::TEXT_SIZE, Expr("2px") },
{ Gui::Prop::SIZE, array<Expr, 2> { Expr("100dp"), Expr("-1") } },
{ Gui::Prop::CONTENT, string("`c1Connecting to `b`c0" + addr.toString() + "`r`c1...") },
{ Gui::Prop::MARGIN, array<Expr, 4> { Expr("4dp"), Expr("4dp"), Expr("4dp"), Expr("4dp") } }
}});
connection.attemptConnect(std::move(addr));
}
@ -50,8 +48,8 @@ void ConnectScene::update() {
PacketView p(e.packet);
if (p.type == Packet::Type::SERVER_INFO) {
status->setStyle(Gui::StyleRule::CONTENT, status->getStyle<string>(Gui::StyleRule::CONTENT, "")
+ "Received server properties.\n");
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+ "Received server properties.\n");
const u32 seed = p.d.read<u32>();
std::cout << seed << std::endl;
@ -76,8 +74,9 @@ void ConnectScene::update() {
resp.sendTo(connection.getPeer(), Packet::Channel::CONNECT);
}
else if (p.type == Packet::Type::BIOME_IDENTIFIER_LIST) {
status->setStyle(Gui::StyleRule::CONTENT, status->getStyle<string>(Gui::StyleRule::CONTENT, "")
+ "Received block & biome index-identifier table.\nDownloading mods... ");
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+
"Received block & biome index-identifier table.\nDownloading mods... ");
client.game->getBiomes().setIdentifiers(p.d.read<vec<string>>());
state = State::MODS;
@ -95,17 +94,19 @@ void ConnectScene::update() {
if (p.type == Packet::Type::MODS) {
auto mod = LuaMod(p);
status->setStyle(Gui::StyleRule::CONTENT, status->getStyle<string>(Gui::StyleRule::CONTENT, "") +
(modsFound == 0 ? "" : ", ") + ((modsFound) % 8 == 0 && modsFound != 0 ? "\n" : "") +
"`c0`u" + mod.config.name + "`r`c1");
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "") +
(modsFound == 0 ? "" : ", ") +
((modsFound) % 8 == 0 && modsFound != 0 ? "\n" : "") +
"`c0`u" + mod.config.name + "`r`c1");
modsFound++;
client.game->getParser().addMod(std::move(mod));
}
else if (p.type == Packet::Type::MOD_ORDER) {
client.game->getParser().setModLoadOrder(p.d.read<vec<string>>());
status->setStyle(Gui::StyleRule::CONTENT, status->getStyle<string>(Gui::StyleRule::CONTENT, "")
+ ".\n`c7Done`c1 downloading mods. Received the mods order.\nReceiving media");
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+
".\n`c7Done`c1 downloading mods. Received the mods order.\nReceiving media");
state = State::MEDIA;
Packet resp(Packet::Type::MEDIA);
@ -126,8 +127,8 @@ void ConnectScene::update() {
while (t != AssetType::END) {
string assetName = p.d.read<string>();
status->setStyle(Gui::StyleRule::CONTENT,
status->getStyle<string>(Gui::StyleRule::CONTENT, "") + ".");
status->setProp(Gui::Prop::CONTENT,
status->getStyle<string>(Gui::Prop::CONTENT, "") + ".");
if (t == AssetType::TEXTURE) {
u16 width = p.d.read<u16>();
@ -152,8 +153,8 @@ void ConnectScene::update() {
}
}
else if (p.type == Packet::Type::MEDIA_DONE) {
status->setStyle(Gui::StyleRule::CONTENT, status->getStyle<string>(Gui::StyleRule::CONTENT, "")
+ " `c7Done.`c1\nLoading complete. `c0`bJoining world...\n");
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+ " `c7Done.`c1\nLoading complete. `c0`bJoining world...\n");
state = State::DONE;
let gameScene = make_unique<GameScene>(client);
@ -179,8 +180,8 @@ void ConnectScene::handleConnecting() {
case ServerConnection::State::FAILED_CONNECT:
state = State::FAILED_CONNECT;
status->setStyle(Gui::StyleRule::CONTENT,
status->getStyle<string>(Gui::StyleRule::CONTENT, "") + " `cfFailed to init :(\n");
status->setProp(Gui::Prop::CONTENT,
status->getStyle<string>(Gui::Prop::CONTENT, "") + " `cfFailed to init :(\n");
break;
case ServerConnection::State::ATTEMPTING_CONNECT:
@ -189,15 +190,15 @@ void ConnectScene::handleConnecting() {
dotsTime += client.getDelta();
if (dotsTime > 1) {
dotsTime -= 1;
status->setStyle(Gui::StyleRule::CONTENT, status->getStyle<string>(Gui::StyleRule::CONTENT, "") + ".");
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "") + ".");
}
break;
case ServerConnection::State::CONNECTED: {
state = State::PROPERTIES;
status->setStyle(Gui::StyleRule::CONTENT,
status->getStyle<string>(Gui::StyleRule::CONTENT, "") + " `c7Connected!~`c1\n");
status->setProp(Gui::Prop::CONTENT,
status->getStyle<string>(Gui::Prop::CONTENT, "") + " `c7Connected!~`c1\n");
Packet resp(Packet::Type::SERVER_INFO);
resp.sendTo(connection.getPeer(), Packet::Channel::CONNECT);

View File

@ -13,14 +13,12 @@ LuaErrorScene::LuaErrorScene(Client& client, const std::string& err) : Scene(cli
client.renderer.window.input.setMouseLocked(false);
using Expr = Gui::Expression;
root.body->append<Gui::TextElement>({
.styles = {{
{ Gui::StyleRule::TEXT_SIZE, Expr("2px") },
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr("100dp"), Expr("-1") } },
{ Gui::StyleRule::CONTENT, string("`cfEncountered a fatal mod error ;-;\n\n`r") + err },
{ Gui::StyleRule::MARGIN, array<Expr, 4> { Expr("4dp"), Expr("4dp"), Expr("4dp"), Expr("4dp") } }
}}
});
root.body->append<Gui::TextElement>({{
{ Gui::Prop::TEXT_SIZE, Expr("2px") },
{ Gui::Prop::SIZE, array<Expr, 2> { Expr("100dp"), Expr("-1") } },
{ Gui::Prop::CONTENT, string("`cfEncountered a fatal mod error ;-;\n\n`r") + err },
{ Gui::Prop::MARGIN, array<Expr, 4> { Expr("4dp"), Expr("4dp"), Expr("4dp"), Expr("4dp") } }
}});
root.body->onClick([&](i32 button, bool down) {
if (button != GLFW_MOUSE_BUTTON_1 || !down) return;

View File

@ -18,7 +18,7 @@
MainMenuScene::MainMenuScene(Client& client) : Scene(client),
root(client.renderer.window, client.game->textures),
sandboxElem(root.create<Gui::BoxElement>({ .classes = { "sandbox" }})),
sandboxElem(root.create<Gui::BoxElement>()),
sandbox(client, root, sandboxElem) {
client.renderer.setClearColor(0, 0, 0);
@ -28,78 +28,68 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
root.addStylesheet({
{ "navigation", {{
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr(""), Expr("18dp") } }
{ Gui::Prop::SIZE, array<Expr, 2> { Expr(""), Expr("18dp") } }
}}},
{ "navigationWrap", {{
{ Gui::StyleRule::DIRECTION, string("row") },
{ Gui::StyleRule::POS, array<Expr, 2> { Expr("0"), Expr("0") } }
{ Gui::Prop::DIRECTION, string("row") },
{ Gui::Prop::POS, array<Expr, 2> { Expr("0"), Expr("0") } }
}}},
{ "navigationBackground", {{
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr("64dp"), Expr("18dp") } },
{ Gui::StyleRule::BACKGROUND, string("menu_bar_bg") }
{ Gui::Prop::SIZE, array<Expr, 2> { Expr("64dp"), Expr("18dp") } },
{ Gui::Prop::BACKGROUND, string("menu_bar_bg") }
}}},
{ "navigationButton", {{
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr("16dp"), Expr("16dp") } },
{ Gui::StyleRule::CURSOR, string("pointer") }
{ Gui::Prop::SIZE, array<Expr, 2> { Expr("16dp"), Expr("16dp") } },
{ Gui::Prop::CURSOR, string("pointer") }
}}}
});
root.body->append(sandboxElem);
let navigation = root.body->append<Gui::BoxElement>({ .classes = { "navigation" } });
let navigationBG = navigation->append<Gui::BoxElement>({ .classes = { "navigationWrap" } });
let navigation = root.body->append<Gui::BoxElement>({{{ Gui::Prop::CLASS, vec<string> { "navigation" } }}});
let navigationBG = navigation->append<Gui::BoxElement>({{{ Gui::Prop::CLASS, vec<string> { "navigationWrap" } }}});
for (usize i = 0; i < 2000 / Gui::PX_SCALE / 64; i++)
navigationBG->append<Gui::BoxElement>({ .classes = { "navigationBackground" } });
navigationBG->append<Gui::BoxElement>({{{ Gui::Prop::CLASS, vec<string> { "navigationBackground" } }}});
let navigationList = navigation->append<Gui::BoxElement>({
.classes = { "navigationWrap" },
.styles = {{
{ Gui::StyleRule::PADDING, array<Expr, 4> { Expr("1dp"), Expr("1dp"), Expr("1dp"), Expr("1dp") } },
{ Gui::StyleRule::GAP, array<Expr, 2> { Expr("1dp"), Expr("1dp") } }
}}
});
let navigationList = navigation->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationWrap" } },
{ Gui::Prop::GAP, array<Expr, 2> { Expr("1dp"), Expr("1dp") } },
{ Gui::Prop::PADDING, array<Expr, 4> { Expr("1dp"), Expr("1dp"), Expr("1dp"), Expr("1dp") } }
}});
navigationList->append<Gui::BoxElement>({
.classes = { "navigationButton" },
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_multiplayer)") },
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_multiplayer)") }
}}
});
navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationButton" } },
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_multiplayer)") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_multiplayer)") }
}});
let contentButton = navigationList->append<Gui::BoxElement>({
.classes = { "navigationButton" },
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_content)") },
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_content)") }
}}
});
let contentButton = navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationButton" } },
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_content)") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_content)") }
}});
contentButton->onClick([&](u32 button, bool down) {
if (button != GLFW_MOUSE_BUTTON_1 || !down) return;
client.scene.setScene(make_unique<ConnectScene>(client, Address { "127.0.0.1" }));
});
navigationList->append<Gui::BoxElement>({
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("#fff5") },
{ Gui::StyleRule::SIZE, array<Expr, 2> { Expr("1dp"), Expr("10dp") } },
{ Gui::StyleRule::MARGIN, array<Expr, 4> { Expr("2dp"), Expr("3dp"), Expr("2dp"), Expr("3dp") } }
}}
});
navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::BACKGROUND, string("#fff5") },
{ Gui::Prop::SIZE, array<Expr, 2> { Expr("1dp"), Expr("10dp") } },
{ Gui::Prop::MARGIN, array<Expr, 4> { Expr("2dp"), Expr("3dp"), Expr("2dp"), Expr("3dp") } }
}});
findSubgames();
for (usize i = 0; i < subgames.size(); i++) {
let& subgame = subgames[i];
let elem = 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 + ")") }
}}
});
let elem = navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationButton" } },
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, " + subgame.iconRef->name + ")") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, " + subgame.iconRef->name + ")") }
}});
elem->onClick([&](u32 button, bool down) {
if (button != GLFW_MOUSE_BUTTON_1 || !down) return;
@ -115,21 +105,17 @@ MainMenuScene::MainMenuScene(Client& client) : Scene(client),
navigationList->append<Gui::BoxElement>();
navigationList->append<Gui::BoxElement>({
.classes = { "navigationButton" },
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_settings)") },
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_settings)") }
}}
});
navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationButton" } },
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_settings)") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_settings)") }
}});
let closeButton = navigationList->append<Gui::BoxElement>({
.classes = { "navigationButton" },
.styles = {{
{ Gui::StyleRule::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_quit)") },
{ Gui::StyleRule::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_quit)") }
}}
});
let closeButton = navigationList->append<Gui::BoxElement>({{
{ Gui::Prop::CLASS, vec<string> { "navigationButton" } },
{ Gui::Prop::BACKGROUND, string("crop(0, 0, 16, 16, menu_flag_quit)") },
{ Gui::Prop::BACKGROUND_HOVER, string("crop(16, 0, 16, 16, menu_flag_quit)") }
}});
closeButton->onClick([](u32 button, bool down) {
if (button != GLFW_MOUSE_BUTTON_1 || !down) return;

View File

@ -22,7 +22,7 @@
#include "usertype/InventoryList.h"
#include "usertype/AnimationManager.h"
#include "usertype/LuaGuiElement.h"
#include "usertype/GuiElement.h"
// Modules
#include "modules/Time.h"
@ -57,17 +57,18 @@ void LocalLuaParser::init(WorldPtr world, PlayerPtr player, Client& client) {
}
for (const let& line : lines) {
usize lineNumStart = line.find(':');
usize fileNameStart = line.find('/');
if (fileNameStart == string::npos) continue;
usize lineNumStart = line.find(':', fileNameStart + 1);
if (lineNumStart == string::npos) continue;
usize lineNumEnd = line.find(':', lineNumStart + 1);
if (lineNumEnd == string::npos) continue;
string fullPath = line.substr(0, lineNumStart);
fullPath.erase(std::remove_if(fullPath.begin(), fullPath.end(), isspace), fullPath.end());
usize slashPos = fullPath.find('/');
if (slashPos == string::npos) continue;
string modName = fullPath.substr(0, slashPos);
fileNameStart = fullPath.find('/');
string modName = fullPath.substr(0, fileNameStart);
if (modName == "base") continue;
let iter = mods.find(modName);

View File

@ -0,0 +1,212 @@
#include "GuiElement.h"
#include "client/gui/Gui.h"
#include "client/gui/Root.h"
#include "client/gui/Element.h"
#include "client/gui/BoxElement.h"
#include "client/gui/TextElement.h"
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<string>()) return Gui::Expression(value.as<string>());
throw std::invalid_argument("Object cannot be converted to an expression.");
}
template <usize D>
static const array<Gui::Expression, D> parseLengthTableVal(sol::object value) {
array<Gui::Expression, D> arr {};
if (value.is<sol::table>()) {
const let& t = value.as<sol::table>();
vec<Gui::Expression> exprs {};
exprs.reserve(t.size());
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 j = 0; j < exprs.size(); j++)
arr[j + i * exprs.size()] = exprs[j];
return arr;
}
let v = parseObjectToExpr(value);
for (usize i = 0; i < arr.size(); i++) arr[i] = v;
return arr;
}
Gui::Prop Api::Usertype::GuiElement::nameToProp(const string& str) {
using namespace Gui;
const let ruleIt = Props::PROP_NAMES_TO_ENUM.find(str);
if (ruleIt == Props::PROP_NAMES_TO_ENUM.end())
throw std::invalid_argument("Style rule '" + str + "' doesn't exist.");
return ruleIt->second;
}
any Api::Usertype::GuiElement::objectToProp(Gui::Prop prop, const sol::object& value) {
using namespace Gui;
switch (prop) {
default:
throw std::invalid_argument("Unhandled rule! This is an engine error! [1]");
case Prop::ID:
case Prop::LAYOUT:
case Prop::DIRECTION:
case Prop::H_ALIGN:
case Prop::V_ALIGN:
case Prop::CURSOR:
case Prop::OVERFLOW:
case Prop::TEXT_COLOR:
case Prop::BACKGROUND:
case Prop::BACKGROUND_HOVER:
case Prop::CONTENT:
return value.as<string>();
case Prop::POS:
case Prop::SIZE:
case Prop::GAP:
return parseLengthTableVal<2>(value);
case Prop::MARGIN:
case Prop::PADDING:
return parseLengthTableVal<4>(value);
case Prop::TEXT_SIZE:
return parseObjectToExpr(value);
}
}
sol::object Api::Usertype::GuiElement::propToObject(Gui::Prop prop, any value, sol::state_view s) {
using namespace Gui;
switch (prop) {
default:
throw std::invalid_argument("Unhandled rule! This is an engine error! [1]");
case Prop::LAYOUT:
case Prop::DIRECTION:
case Prop::H_ALIGN:
case Prop::V_ALIGN:
case Prop::CURSOR:
case Prop::OVERFLOW:
// case Prop::TEXT_COLOR:
case Prop::BACKGROUND:
case Prop::BACKGROUND_HOVER:
case Prop::CONTENT:
return sol::make_object(s, std::any_cast<string>(value));
case Prop::POS:
case Prop::SIZE:
case Prop::GAP:
// return sol::make_object(s, s.create_table_with(
//
// ));
// return sol::make_object(s, std::any_cast<string>(value));
case Prop::MARGIN:
case Prop::PADDING:
// return parseLengthTableVal<4>(value);
case Prop::TEXT_SIZE:
break;
throw std::invalid_argument("Unhandled rule! This is an engine error! [1]");
// return parseObjectToExpr(value);
}
}
sptr<Gui::Element> Api::Usertype::GuiElement::create(const string& type, sol::table data, Gui::Root& root) {
Gui::Props props {};
for (let& style : data) {
if (!style.first.is<string>()) continue;
let rule = GuiElement::nameToProp(style.first.as<string>());
let value = GuiElement::objectToProp(rule, style.second);
props.props[rule] = value;
}
sptr<Gui::Element> elem = nullptr;
switch (Util::hash(type.data())) {
default: throw std::invalid_argument("Invalid element type '" + type + "'.");
case Util::hash("Box"): elem = root.create<Gui::BoxElement>(props); break;
case Util::hash("Text"): elem = root.create<Gui::TextElement>(props); break;
}
for (usize i = 1; i <= data.size(); i++) {
const sol::object& child = data.get<sol::object>(i);
if (!child.is<sptr<Gui::Element>>()) continue;
elem->append(child.as<sptr<Gui::Element>>());
}
return elem;
}
void Api::Usertype::GuiElement::bind(sol::state& lua, sol::table& core, Gui::Root& root) {
lua.new_usertype<Gui::Element>("GuiElement",
sol::meta_function::construct, sol::factories([&](const string& type, sol::table data) {
return GuiElement::create(type, data, root);
}),
"prepend", [&](sol::this_state s, Gui::Element& self, sol::object child) {
if (child.is<sptr<Gui::Element>>()) self.prepend(child.as<sptr<Gui::Element>>());
else if (child.is<sol::protected_function>()) {
sol::protected_function fn = child.as<sol::protected_function>();
sol::table tbl = sol::state_view(s)["zepha"]["__builtin"]["gui_env"];
sol::environment env(s, sol::create, tbl);
sol::set_environment(env, fn);
self.prepend(static_cast<sol::object>(fn()).as<sptr<Gui::Element>>());
}
else throw std::invalid_argument(
"Cannot append() an item that is not an element or a function that returns one.");
},
"append", [&](sol::this_state s, Gui::Element& self, sol::object child) {
if (child.is<sptr<Gui::Element>>()) self.append(child.as<sptr<Gui::Element>>());
else if (child.is<sol::protected_function>()) {
let fn = child.as<sol::protected_function>();
sol::table tbl = sol::state_view(s)["zepha"]["__builtin"]["gui_env"];
sol::environment env(s, sol::create, tbl);
sol::set_environment(env, fn);
self.append(static_cast<sol::object>(fn()).as<sptr<Gui::Element>>());
}
else throw std::invalid_argument(
"append() parameter must be an element or a function that returns one.");
},
"get", [&](sol::this_state s, Gui::Element& self, sol::object query) -> sol::object {
let found = query.is<u32>() ? self.get(query.as<u32>() - 1) : self.get(query.as<string>());
if (!found) return sol::nil;
return sol::make_object(s, found);
},
"remove", [&](sol::this_state s, Gui::Element& self, sol::object query) {
if (query == sol::nil) self.remove();
else if (query.is<u32>()) {
let child = self.get(query.as<u32>());
if (child) child->remove();
}
else if (query.is<std::string>()) {
let child = self.get(query.as<std::string>());
if (child) child->remove();
}
else if (query.is<sptr<Gui::Element>>()) {
query.as<sptr<Gui::Element>>()->remove();
}
else throw std::invalid_argument(
"remove() parameter must be nil, a number, an string, or a Gui element.");
},
"clear", &Gui::Element::clear,
sol::meta_function::new_index, [&](Gui::Element& self, const string& ruleStr, sol::object rawValue) {
let rule = GuiElement::nameToProp(ruleStr);
let value = GuiElement::objectToProp(rule, rawValue);
self.setProp(rule, value);
self.updateElement();
},
sol::meta_function::index, [&](sol::this_state s, Gui::Element& self, const string& ruleStr) {
let rule = GuiElement::nameToProp(ruleStr);
let value = self.getProps().get(rule);
if (!value) return sol::make_object(s, sol::nil);
return GuiElement::propToObject(rule, *value, s);
}
);
}

View File

@ -8,13 +8,13 @@ namespace Gui {
class Style;
class Root;
class Element;
enum class StyleRule;
enum class Prop;
}
namespace Api::Usertype::GuiElement {
Gui::StyleRule ruleFromStr(const string& str);
any parseRuleValue(Gui::StyleRule rule, const sol::object& value);
sol::object styleAnyToObject(Gui::StyleRule rule, optional<any> value, sol::this_state s);
Gui::Prop nameToProp(const string& str);
any objectToProp(Gui::Prop prop, const sol::object& value);
sol::object propToObject(Gui::Prop prop, any value, sol::state_view s);
std::shared_ptr<Gui::Element> create(const string& type, sol::table data, Gui::Root& root);

View File

@ -1,333 +0,0 @@
#include "LuaGuiElement.h"
#include "client/gui/Gui.h"
#include "client/gui/Root.h"
#include "client/gui/Element.h"
#include "client/gui/BoxElement.h"
#include "client/gui/TextElement.h"
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<string>()) return Gui::Expression(value.as<string>());
throw std::invalid_argument("Object cannot be converted to an expression.");
}
template <usize D>
static const array<Gui::Expression, D> parseLengthTableVal(sol::object value) {
array<Gui::Expression, D> arr {};
if (value.is<sol::table>()) {
const let& t = value.as<sol::table>();
vec<Gui::Expression> exprs {};
exprs.reserve(t.size());
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 j = 0; j < exprs.size(); j++)
arr[j + i * exprs.size()] = exprs[j];
return arr;
}
let v = parseObjectToExpr(value);
for (usize i = 0; i < arr.size(); i++) arr[i] = v;
return arr;
}
Gui::StyleRule Api::Usertype::GuiElement::ruleFromStr(const string& str) {
using namespace Gui;
const let ruleIt = Style::RULE_STRINGS_TO_ENUMS.find(str);
if (ruleIt == Style::RULE_STRINGS_TO_ENUMS.end())
throw std::invalid_argument("Style rule '" + str + "' doesn't exist.");
return ruleIt->second;
}
any Api::Usertype::GuiElement::parseRuleValue(Gui::StyleRule rule, const sol::object& value) {
using namespace Gui;
switch (rule) {
default:
throw std::invalid_argument("Unhandled rule! This is an engine error! [1]");
case StyleRule::LAYOUT:
case StyleRule::DIRECTION:
case StyleRule::H_ALIGN:
case StyleRule::V_ALIGN:
case StyleRule::CURSOR:
case StyleRule::OVERFLOW:
case StyleRule::TEXT_COLOR:
case StyleRule::BACKGROUND:
case StyleRule::BACKGROUND_HOVER:
case StyleRule::CONTENT:
return value.as<string>();
case StyleRule::POS:
case StyleRule::SIZE:
case StyleRule::GAP:
return parseLengthTableVal<2>(value);
case StyleRule::MARGIN:
case StyleRule::PADDING:
return parseLengthTableVal<4>(value);
case StyleRule::TEXT_SIZE:
return parseObjectToExpr(value);
}
}
sol::object Api::Usertype::GuiElement::styleAnyToObject(Gui::StyleRule rule, optional<any> value, sol::this_state s) {
using namespace Gui;
switch (rule) {
default:
throw std::invalid_argument("Unhandled rule! This is an engine error! [1]");
case StyleRule::LAYOUT:
case StyleRule::DIRECTION:
case StyleRule::H_ALIGN:
case StyleRule::V_ALIGN:
case StyleRule::CURSOR:
case StyleRule::OVERFLOW:
case StyleRule::TEXT_COLOR:
case StyleRule::BACKGROUND:
case StyleRule::BACKGROUND_HOVER:
case StyleRule::CONTENT:
return sol::make_object(s, std::any_cast<string>(value));
case StyleRule::POS:
case StyleRule::SIZE:
case StyleRule::GAP:
// return sol::make_object(s, std::any_cast<string>(value));
case StyleRule::MARGIN:
case StyleRule::PADDING:
// return parseLengthTableVal<4>(value);
case StyleRule::TEXT_SIZE:
break;
// return parseObjectToExpr(value);
}
}
sptr<Gui::Element> Api::Usertype::GuiElement::create(const string& type, sol::table data, Gui::Root& root) {
Gui::Element::Props props {};
for (let& style : data) {
if (!style.first.is<string>()) continue;
let rule = GuiElement::ruleFromStr(style.first.as<string>());
let value = GuiElement::parseRuleValue(rule, style.second);
props.styles.rules[rule] = value;
}
sptr<Gui::Element> elem = nullptr;
switch (Util::hash(type.data())) {
default: throw std::invalid_argument("Invalid element type '" + type + "'.");
case Util::hash("Box"): elem = root.create<Gui::BoxElement>(props); break;
case Util::hash("Text"): elem = root.create<Gui::TextElement>(props); break;
}
for (usize i = 1; i <= data.size(); i++) {
const sol::object& child = data.get<sol::object>(i);
if (!child.is<sptr<Gui::Element>>()) continue;
elem->append(child.as<sptr<Gui::Element>>());
}
return elem;
}
void Api::Usertype::GuiElement::bind(sol::state& lua, sol::table& core, Gui::Root& root) {
lua.new_usertype<Gui::Element>("GuiElement",
sol::meta_function::construct, sol::factories([&](const string& type, sol::table data) {
return GuiElement::create(type, data, root);
}),
"append", [&](Gui::Element& self, sptr<Gui::Element> child) {
self.append(child);
},
"get", [&](sol::this_state s, Gui::Element& self, sol::object query) -> sol::object {
if (query.is<u32>()) {
let found = self.get(query.as<u32>() - 1);
if (!found) return sol::nil;
return sol::make_object(s, found);
}
else throw std::runtime_error("ID get is unimplemented.");
},
sol::meta_function::new_index, [&](Gui::Element& self, const string& ruleStr, sol::object rawValue) {
let rule = GuiElement::ruleFromStr(ruleStr);
let value = GuiElement::parseRuleValue(rule, rawValue);
self.setStyle(rule, value);
self.updateElement();
}
);
}
//
//// sol::meta_function::index, &LuaGuiElement::get_trait,
//// sol::meta_function::new_index, &LuaGuiElement::set_trait,
//
//// sol::meta_function::call, &LuaGuiElement::call,
//
//// "get", &LuaGuiElement::get_child,
//// "append", &LuaGuiElement::append,
//// "prepend", &LuaGuiElement::prepend,
//// "remove", &LuaGuiElement::remove,
//// "clear", &LuaGuiElement::clear
//
//sol::object LuaGuiElement::get_trait(sol::this_state s, const std::string& key) {
// if (key == "key") return sol::make_object<std::string>(s, this->key);
// if (key == "type") return sol::make_object<std::string>(s, this->type);
//
// if (traits.count(key)) return traits.at(key);
// return sol::nil;
//}
//
//sol::object LuaGuiElement::set_trait(const std::string& key, sol::object val) {
// if (key == "callbacks") {
// callbacks.clear();
// for (auto pair : val.as<sol::table>()) callbacks[pair.first.as<std::string>()] = pair.second.as<sol::function>();
// }
// else if (key == "key") {
// this->key = val.as<std::string>();
// }
// else {
// traits.erase(key);
// traits.emplace(key, val);
// }
//
// if (updateFunction) updateFunction();
// return val;
//}
//
//sol::object LuaGuiElement::call(sol::this_state s, sol::protected_function fun) {
// sol::table tbl = sol::state_view(s)["zepha"]["__builtin"]["gui_env"];
// sol::environment env(s, sol::create, tbl);
//
// sol::set_environment(env, fun);
// return fun(this);
//}
//
//sol::object LuaGuiElement::get_child(sol::this_state s, sol::object key) {
// if (key.is<float>() && key.as<float>() <= children.size()) {
// auto begin = children.begin();
// std::advance(begin, key.as<float>() - 1);
// return sol::make_object<std::shared_ptr<LuaGuiElement>>(s, *begin);
// }
// else if (key.is<std::string>()) {
// for (auto& child : children) {
// if (child->key == key.as<std::string>()) return sol::make_object<std::shared_ptr<LuaGuiElement>>(s, child);
// }
//
// for (auto& child : children) {
// auto recurse = child->get_child(s, key);
// if (recurse) return recurse;
// }
// }
//
// return sol::nil;
//}
//
//void LuaGuiElement::append(sol::this_state s, sol::object elem) {
// if (elem.is<std::shared_ptr<LuaGuiElement>>()) children.push_back(elem.as<std::shared_ptr<LuaGuiElement>>());
// else if (elem.is<sol::protected_function>())
// children.push_back(call(s, elem.as<sol::protected_function>()).as<std::shared_ptr<LuaGuiElement>>());
// else throw std::runtime_error("Append arg is not an element or a function to generate one.");
//
// children.back()->parent = this;
// if (updateFunction) updateFunction();
//}
//
//void LuaGuiElement::prepend(sol::this_state s, sol::object elem) {
// if (elem.is<std::shared_ptr<LuaGuiElement>>())
// children.insert(children.begin(), elem.as<std::shared_ptr<LuaGuiElement>>());
// else if (elem.is<sol::function>())
// children.insert(children.begin(), call(s, elem.as<sol::function>()).as<std::shared_ptr<LuaGuiElement>>());
// else throw std::runtime_error("Append arg is not an element or a function to generate one.");
//
// children.front()->parent = this;
// if (updateFunction) updateFunction();
//}
//
//void LuaGuiElement::remove(sol::this_state s, sol::object elem) {
// if (!elem) {
// if (parent != nullptr) parent->remove(s, sol::make_object<std::string>(s, key));
// else throw std::runtime_error("Tried to remove self from nil parent.");
// }
// else if (elem.is<std::string>()) {
// auto child = this->get_child(s, sol::make_object<std::string>(s, elem.as<std::string>()));
// if (child) remove(s, child);
// }
// else if (elem.is<std::shared_ptr<LuaGuiElement>>()) {
// auto parent = elem.as<std::shared_ptr<LuaGuiElement>>()->parent;
//
// for (auto it = parent->children.cbegin(); it != parent->children.cend(); it++) {
// if ((*it)->key == elem.as<std::shared_ptr<LuaGuiElement>>()->key) {
// (*it)->parent = nullptr;
// (*it)->updateFunction = nullptr;
//
// parent->children.erase(it);
// if (parent->updateFunction) parent->updateFunction();
// return;
// }
// }
// }
//}
//
//void LuaGuiElement::clear(sol::this_state s) {
// for (auto it = children.cbegin(); it != children.cend();) {
// (*it)->parent = nullptr;
// (*it)->updateFunction = nullptr;
// it = children.erase(it);
// }
//
// if (updateFunction) updateFunction();
//}
//
//Any LuaGuiElement::getAsAny(const std::string& key) const {
// if (!traits.count(key)) return Any();
//// auto object = traits.at(key);
////
//// if (object.is<float>()) return Any::from<float>(object.as<float>());
//// else if (object.is<bool>()) return Any::from<bool>(object.as<bool>());
//// else if (object.is<std::string>()) return Any::from<std::string>(object.as<std::string>());
//// else if (object.is<sol::table>()) {
//// auto table = object.as<sol::table>();
////
//// if (table.size() == 2) {
//// auto x = table.get<sol::object>(1);
//// auto y = table.get<sol::object>(2);
////
//// glm::vec2 values = {};
//// if (x.is<float>()) values.x = x.as<float>();
//// else if (x.is<std::string>()) values.x = SerialGui::toDouble(x.as<std::string>());
//// if (y.is<float>()) values.y = y.as<float>();
//// else if (y.is<std::string>()) values.y = SerialGui::toDouble(y.as<std::string>());
////
//// return Any::from<glm::vec2>(values);
//// }
//// else if (table.size() == 4) {
//// auto x = table.get<sol::object>(1);
//// auto y = table.get<sol::object>(2);
//// auto z = table.get<sol::object>(3);
//// auto w = table.get<sol::object>(4);
////
//// glm::vec4 values = {};
//// if (x.is<float>()) values.x = x.as<float>();
//// else if (x.is<std::string>()) values.x = SerialGui::toDouble(x.as<std::string>());
//// if (y.is<float>()) values.y = y.as<float>();
//// else if (y.is<std::string>()) values.y = SerialGui::toDouble(y.as<std::string>());
//// if (z.is<float>()) values.z = z.as<float>();
//// else if (z.is<std::string>()) values.z = SerialGui::toDouble(z.as<std::string>());
//// if (w.is<float>()) values.w = w.as<float>();
//// else if (w.is<std::string>()) values.w = SerialGui::toDouble(w.as<std::string>());
////
//// return Any::from<glm::vec4>(values);
//// }
//// }
//
// throw std::runtime_error("Invalid type requested in getAsAny");
//}

View File

@ -21,15 +21,15 @@ LocalPlayer::LocalPlayer(SubgamePtr game, LocalWorld& world, DimensionPtr dim, R
renderer(renderer) {
handItemModel.parent = &handModel;
hud = root.body->append<Gui::BoxElement>({ .styles {{
{ Gui::StyleRule::POS, array<Gui::Expression, 2> { Gui::Expression("0"), Gui::Expression("0") }},
{ Gui::StyleRule::SIZE, array<Gui::Expression, 2> { Gui::Expression("100cw"), Gui::Expression("100ch") }}
}}});
hud = root.body->append<Gui::BoxElement>({{
{ Gui::Prop::POS, array<Gui::Expression, 2> { Gui::Expression("0"), Gui::Expression("0") }},
{ Gui::Prop::SIZE, array<Gui::Expression, 2> { Gui::Expression("100cw"), Gui::Expression("100ch") }}
}});
menu = root.body->append<Gui::BoxElement>({ .styles {{
{ Gui::StyleRule::POS, array<Gui::Expression, 2> { Gui::Expression("0"), Gui::Expression("0") }},
{ Gui::StyleRule::SIZE, array<Gui::Expression, 2> { Gui::Expression("100cw"), Gui::Expression("100ch") }}
}}});
menu = root.body->append<Gui::BoxElement>({{
{ Gui::Prop::POS, array<Gui::Expression, 2> { Gui::Expression("0"), Gui::Expression("0") }},
{ Gui::Prop::SIZE, array<Gui::Expression, 2> { Gui::Expression("100cw"), Gui::Expression("100ch") }}
}});
}
void LocalPlayer::update(f64 delta, vec2 mouseDelta) {

View File

@ -66,13 +66,15 @@ for _ = 1, 100 do
end
local particle_wrap = menu:get(1)
for i, pos in ipairs(positions) do
particle_wrap:append(zepha.Gui.Box {
pos = pos,
background = 'particle_dark',
size = sizes[i]
})
end
zepha.gui(function()
for i, pos in ipairs(positions) do
particle_wrap:append(Gui.Box {
pos = pos,
background = 'particle_dark',
size = sizes[i]
})
end
end)
local tick = 0
zepha.after(function()

View File

@ -1,25 +1,24 @@
local max_messages = 8
local chat_wrap = zepha.build_gui(function()
return Gui.Rect {
position = { 4, '100%' },
position_anchor = { 0, '200%' },
local chat_wrap = zepha.gui(function()
return Gui.Box {
pos = { 4, '100ch - 100sh - 100dp' },
size = { 256, 23 + max_messages * 8 },
Gui.Rect {
key = 'chat_tabs',
position = { 0, -10 }
Gui.Box {
id = 'chat_tabs',
pos = { 0, -10 }
},
Gui.Rect {
key = 'chat_box',
Gui.Box {
id = 'chat_box',
size = { 256, 2 + max_messages * 8 }
},
Gui.Rect {
key = 'chat_input',
Gui.Box {
id = 'chat_input',
size = { 256, 10 },
position = { 0, 3 + max_messages * 8 }
pos = { 0, 3 + max_messages * 8 }
}
}
end)

View File

@ -1,5 +1,5 @@
require(_PATH .. 'api')
if zepha.client then runfile(_PATH .. 'gui') end
if zepha.client then require(_PATH .. 'gui') end
if zepha.server then
chat.create_channel('chat', { name = 'Chat', icon = '@auri:chat:chat_icon' })

View File

@ -1,6 +1,6 @@
_G['health'] = {}
health.internal = {}
require(_PATH .. 'api')
require(_PATH .. 'interface')
require(_PATH .. 'hooks')
-- _G['health'] = {}
-- health.internal = {}
--
-- require(_PATH .. 'api')
-- require(_PATH .. 'interface')
-- require(_PATH .. 'hooks')

View File

@ -14,7 +14,7 @@ function health.internal.update()
health.internal._wrapper:remove('@health:component')
health.internal._wrapper:append(function()
local elem = Gui.Rect {
local elem = Gui.Box {
id = '@health:component',
size = { health.internal._width, 9 },
}

View File

@ -1,10 +1,10 @@
_G["hot_wheel"] = {}
if zepha.server then
require(_PATH .. "register")
end
if zepha.client then
require(_PATH .. "interface")
require(_PATH .. "keys")
end
-- _G["hot_wheel"] = {}
--
-- if zepha.server then
-- require(_PATH .. "register")
-- end
--
-- if zepha.client then
-- require(_PATH .. "interface")
-- require(_PATH .. "keys")
-- end

View File

@ -1,49 +1,45 @@
local chest = zepha.build_gui(function()
return Gui.Body {
local chest = zepha.gui(function()
return Gui.Box {
background = "#0003",
callbacks = {
primary = function() zepha.player:close_menu() end,
},
Gui.Rect {
key = "inventory",
position = { pc(50), pc(50) },
position_anchor = { pc(50), pc(32) },
size = { 218, 160 },
Gui.Rect {
key = "inventory_background",
position = { 0, 50 },
size = { 218, 100 },
padding = { 20, 10, 8, 10 },
background = "zeus:inventory:inventory",
Gui.InventoryList {
position = { 1, 1 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "main",
}
},
Gui.Rect {
key = "chest_background",
position = { 0, -48 },
size = { 218, 100 },
padding = { 20, 10, 8, 10 },
background = "zeus:inventory:chest",
Gui.InventoryList {
position = { 1, 1 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "main",
}
}
}
-- Gui.Rect {
-- key = "inventory",
-- position = { pc(50), pc(50) },
-- position_anchor = { pc(50), pc(32) },
-- size = { 218, 160 },
--
-- Gui.Rect {
-- key = "inventory_background",
--
-- position = { 0, 50 },
-- size = { 218, 100 },
-- padding = { 20, 10, 8, 10 },
-- background = "zeus:inventory:inventory",
--
-- Gui.InventoryList {
-- position = { 1, 1 },
-- slot_spacing = { 2, 2 },
-- source = "current_player",
-- list = "main",
-- }
-- },
--
-- Gui.Rect {
-- key = "chest_background",
--
-- position = { 0, -48 },
-- size = { 218, 100 },
-- padding = { 20, 10, 8, 10 },
-- background = "zeus:inventory:chest",
--
-- Gui.InventoryList {
-- position = { 1, 1 },
-- slot_spacing = { 2, 2 },
-- source = "current_player",
-- list = "main",
-- }
-- }
-- }
}
end)

View File

@ -1,127 +1,135 @@
local menu = zepha.build_gui(function()
return Gui.Body {
local menu = zepha.gui(function()
return Gui.Box {
background = "#0003",
callbacks = {
primary = function() zepha.player:close_menu() end,
},
Gui.Box {
id = "inventory",
Gui.Rect {
key = "inventory",
position = { pc(50), pc(50) },
position_anchor = { pc(50), pc(50) },
size = { 342, 187 },
pos = { "50cw - 50sw", "50ch - 50sh" },
Gui.Rect {
key = "backpack",
Gui.Box {
id = "backpack",
position = { 1, 0 },
pos = { 1, 0 },
size = { 106, 187 },
padding = { 19, 9, 8, 9 },
background = "zeus:inventory:backpack",
Gui.InventoryList {
position = { 0, 0 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "main",
},
Gui.InventoryList {
position = { 0, 18*3 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "hot_wheel_1",
},
Gui.InventoryList {
position = { 0, 18*4 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "hot_wheel_2",
},
Gui.InventoryList {
position = { 0, 18*5 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "hot_wheel_3",
},
Gui.InventoryList {
position = { 0, 18*6 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "hot_wheel_4",
},
Gui.InventoryList {
position = { 0, 18*7 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "hot_wheel_5",
},
Gui.InventoryList {
position = { 0, 18*8 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "hot_wheel_6",
}
background = "zeus:inventory:backpack"
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 0 },
--
-- list = "main",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 18 * 3 },
--
-- list = "hot_wheel_1",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 18 * 4 },
--
-- list = "hot_wheel_2",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 18 * 5 },
--
-- list = "hot_wheel_3",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 18 * 6 },
--
-- list = "hot_wheel_4",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 18 * 7 },
--
-- list = "hot_wheel_5",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 0, 18*8 },
--
-- list = "hot_wheel_6",
-- source = "current_player"
-- }
},
Gui.Rect {
key = "player_frame",
position = { 105, 0 },
Gui.Box {
id = "player_frame",
pos = { 105, 0 },
size = { 106, 187 },
background = "zeus:inventory:player_frame",
Gui.Model {
position = { 52, 150 },
scale = { 64, 64 },
type = "model",
source = "zeus:default:player",
texture = "zeus:default:player",
anim_range = { 0, 100 }
}
-- Gui.Model {
-- pos = { 52, 150 },
-- size = { 64, 64 },
--
-- type = "model",
-- anim_range = { 0, 100 },
-- source = "zeus:default:player",
-- texture = "zeus:default:player"
-- }
},
Gui.Rect {
key = "equipment",
Gui.Box {
id = "equipment",
position = { 209, 1 },
pos = { 209, 1 },
size = { 132, 80 },
padding = { 18, 8, 8, 8 },
background = "zeus:inventory:equipment",
Gui.Rect {
key = "player_clamp",
Gui.Box {
id = "player_clamp",
position = { 41, 1 },
pos = { 41, 1 },
size = { 34, 52 },
overflow = "hidden"
}
},
Gui.Rect {
key = "dynamic",
Gui.Box {
id = "dynamic",
position = { 209, 80 },
pos = { 209, 80 },
size = { 132, 107 },
padding = { 8, 8, 8, 8 },
background = "zeus:inventory:dynamic",
background = "zeus:inventory:dynamic"
Gui.InventoryList {
position = { 1, 1 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "mod",
},
Gui.InventoryList {
position = { 41, 32 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "craft",
},
Gui.InventoryList {
position = { 81, 41 },
slot_spacing = { 2, 2 },
source = "current_player",
list = "craft_result",
}
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 1, 1 },
--
-- list = "mod",
-- source = "current_player",
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 41, 32 },
--
-- list = "craft",
-- source = "current_player"
-- },
-- Gui.InventoryList {
-- gap = { 2, 2 },
-- pos = { 81, 41 },
--
-- list = "craft_result",
-- source = "current_player"
-- }
}
}
}