LocalGuiElement

master
Nicole Collings 2020-04-13 16:33:15 -07:00
parent 000e07101d
commit afa1b5621d
13 changed files with 208 additions and 132 deletions

View File

@ -1,90 +1,12 @@
local env = { Gui = {}}
local Gui = env.Gui
zepha.__builtin.gui_env = { Gui = {}}
local env = zepha.__builtin.gui_env
setmetatable(env, {__index = _G})
elem_mt = {
__index = function(elem, key)
-- Don't directly access the traits table.
if key == "traits" then return nil end
-- Attempt to get a value from the table, e.g. type, key, children, etc.
local val = rawget(elem, key)
-- Attempt to get a value from the metatable, e.g. a function.
if val == nil then val = rawget(getmetatable(elem), key) end
-- Attempt to get a value from the traits array.
if val == nil then val = rawget(rawget(elem, "traits"), key) end
return val
end,
__newindex = function(elem, key, val)
if key == "type" or key == "key" or key == "callbacks" then rawset(elem, key, val) end
rawset(rawget(elem, "traits"), key, val)
end,
__call = function(tbl, fn)
setfenv(fn, env)
return fn(tbl)
end,
find = function(tbl, key)
for _,v in pairs(rawget(tbl, "children")) do
if v.key == key then return v end
local c = v:find(key)
if c ~= nil then return c end
end
end,
append = function(tbl, elem)
if type(elem) == "function" then elem = zepha.build_gui(elem) end
rawset(elem, "parent", tbl)
table.insert(rawget(tbl, "children"), elem)
end,
prepend = function(tbl, elem)
if type(elem) == "function" then elem = zepha.build_gui(elem) end
rawset(elem, "parent", tbl)
table.insert(rawget(tbl, "children"), 1, elem)
end,
remove = function(tbl)
if tbl.parent == nil then return end
local ind = 0
for k,v in pairs(tbl.parent.children) do
if v == tbl then ind = k end
end
if ind == 0 then return end
table.remove(tbl.parent.children, ind)
return tbl
end
}
-- create_element
-- Build a GUI Element with the provided constructor data, apply the metatable.
local function create_element(elem_type, data)
local element = {
type = elem_type,
key = "",
parent = nil,
children = {},
callbacks = {},
traits = {}
}
setmetatable(element, elem_mt)
for k, v in pairs(data) do
if type(k) == "number" then
rawset(v, "parent", element)
table.insert(element.children, v)
else
element[k] = v
end
end
return element
local elem = GuiElement.new(elem_type, data)
return elem
end
-- register_element
@ -94,7 +16,7 @@ local function register_element(key)
for _,v in pairs(key) do register_element(v) end
return
end
Gui[key] = function(data) return create_element(key, data) end
env.Gui[key] = function(data) return create_element(key, data) end
end
register_element({"Body", "Rect", "Text", "Model", "Button", "InventoryList"})
@ -105,7 +27,7 @@ env.pc = function(num)
return tostring(num) .. "%"
end
-- zepha.build_ui
-- zepha.build_gui
-- Allows you to Build UI Elements with the GUI namespace outside of a callback.
zepha.build_gui = function(fn)
setfenv(fn, env)

View File

@ -320,6 +320,6 @@ set(ZEPHA_SRC
game/inventory/InventoryList.h
lua/api/modules/time.h
util/net/PacketView.cpp
util/net/PacketView.h lua/api/modules/create_structure.h util/Any.h game/scene/world/graph/MeshFarMap.cpp game/scene/world/graph/MeshFarMap.h def/gen/MapGenProps.cpp def/gen/MapGenProps.h def/gen/FarMapGen.cpp def/gen/FarMapGen.h def/gen/FarMapJob.h game/scene/world/graph/FarMeshGenerator.cpp game/scene/world/graph/FarMeshGenerator.h game/scene/world/FarMapMeshDetails.h)
util/net/PacketView.h lua/api/modules/create_structure.h util/Any.h game/scene/world/graph/MeshFarMap.cpp game/scene/world/graph/MeshFarMap.h def/gen/MapGenProps.cpp def/gen/MapGenProps.h def/gen/FarMapGen.cpp def/gen/FarMapGen.h def/gen/FarMapJob.h game/scene/world/graph/FarMeshGenerator.cpp game/scene/world/graph/FarMeshGenerator.h game/scene/world/FarMapMeshDetails.h lua/api/usertype/cGuiElement.h lua/api/class/LocalGuiElement.cpp lua/api/class/LocalGuiElement.h)
add_library (Zepha_Core ${ZEPHA_SRC})

View File

@ -45,7 +45,7 @@ void GameGui::winResized(glm::ivec2 win) {
}
void GameGui::buildMenu(sol::state_view state, sol::table menu) {
menuBuilder.setGuiTable(state, menu);
// menuBuilder.setGuiTable(state, menu);
menuBuilder.build(win);
inMenu = true;
}

View File

@ -13,19 +13,17 @@
GuiBuilder::GuiBuilder(TextureAtlas& textures, ModelStore& models, std::shared_ptr<GuiContainer> root) :
textures(textures), models(models), root(root) {}
void GuiBuilder::setGuiTable(sol::state_view state, sol::table menu) {
void GuiBuilder::setGuiRoot(sol::state_view state, LocalGuiElement& menu) {
keyInd = 0;
serialized = rDeserialize(state, menu);
serialized = recursivelyDeserialize(state, menu);
}
SerialGui::Element GuiBuilder::rDeserialize(sol::state_view state, sol::table menu) {
std::string type = menu.get<std::string>("type");
std::string key = (menu.get<sol::optional<std::string>>("key") ? menu.get<std::string>("key") : "__UNKEYED_COMPONENT_" + std::to_string(keyInd++));
SerialGui::Element GuiBuilder::recursivelyDeserialize(sol::state_view state, LocalGuiElement& elem) {
std::string key = (elem.key.size() ? elem.key : "__UNKEYED_COMPONENT_" + std::to_string(keyInd++));
SerialGui::Element element(elem.type, key);
SerialGui::Element element(type, key);
for (auto trait : menu.get<sol::table>("traits")) {
auto key = trait.first.as<std::string>();
for (const auto& trait : elem.traits) {
auto key = trait.first;
if (trait.second.is<std::string>()) {
element.addTrait(key, Any::from<std::string>(trait.second.as<std::string>()));
}
@ -63,55 +61,49 @@ SerialGui::Element GuiBuilder::rDeserialize(sol::state_view state, sol::table me
}
}
auto callbacks = menu.get<sol::optional<sol::table>>("callbacks");
if (callbacks) {
if (callbacks->get<sol::optional<sol::function>>("primary"))
element.callbacks[static_cast<unsigned int>(GuiComponent::CallbackType::PRIMARY)] = [=](bool down, glm::ivec2 pos) {
callbacks->get<sol::function>("primary")(down, LuaParser::luaVec(state, {pos.x, pos.y, 0})); };
if (elem.callbacks.count("primary"))
element.callbacks[static_cast<unsigned int>(GuiComponent::CallbackType::PRIMARY)] = [=](bool down, glm::ivec2 pos) {
elem.callbacks.at("primary")(down, LuaParser::luaVec(state, {pos.x, pos.y, 0})); };
if (callbacks->get<sol::optional<sol::function>>("secondary"))
element.callbacks[static_cast<unsigned int>(GuiComponent::CallbackType::SECONDARY)] = [=](bool down, glm::ivec2 pos) {
callbacks->get<sol::function>("secondary")(down, LuaParser::luaVec(state, {pos.x, pos.y, 0})); };
if (elem.callbacks.count("secondary"))
element.callbacks[static_cast<unsigned int>(GuiComponent::CallbackType::SECONDARY)] = [=](bool down, glm::ivec2 pos) {
elem.callbacks.at("secondary")(down, LuaParser::luaVec(state, {pos.x, pos.y, 0})); };
if (callbacks->get<sol::optional<sol::function>>("hover"))
element.callbacks[static_cast<unsigned int>(GuiComponent::CallbackType::HOVER)] = [=](bool down, glm::ivec2 pos) {
callbacks->get<sol::function>("hover")(down, LuaParser::luaVec(state, {pos.x, pos.y, 0})); };
}
auto children = menu.get<sol::optional<sol::table>>("children");
if (children) for (auto& pair : *children) element.children.push_back(rDeserialize(state, pair.second.as<sol::table>()));
if (elem.callbacks.count("secondary"))
element.callbacks[static_cast<unsigned int>(GuiComponent::CallbackType::HOVER)] = [=](bool down, glm::ivec2 pos) {
elem.callbacks.at("hover")(down, LuaParser::luaVec(state, {pos.x, pos.y, 0})); };
for (auto& child : elem.children) element.children.push_back(recursivelyDeserialize(state, child));
return element;
}
void GuiBuilder::build(glm::ivec2 winBounds) {
clear(false);
if (serialized.type != "")
rCreate(serialized, root, winBounds);
recursivelyCreate(serialized, root, winBounds);
}
void GuiBuilder::clear(bool deleteRoot) {
rClearCallbacks(root);
recursivelyClearCallbacks(root);
root->empty();
if (deleteRoot) serialized = {"", ""};
}
void GuiBuilder::rCreate(const SerialGui::Element& element, std::shared_ptr<GuiComponent> parent, glm::ivec2 bounds) {
void GuiBuilder::recursivelyCreate(const SerialGui::Element& element, std::shared_ptr<GuiComponent> parent, glm::ivec2 bounds) {
auto component = createComponent(element, bounds);
if (!component) throw std::runtime_error("GuiBuilder failed to create component: " + element.key);
parent->add(component);
for (auto& child : element.children) rCreate(child, component, component->getScale());
for (auto& child : element.children) recursivelyCreate(child, component, component->getScale());
}
void GuiBuilder::rClearCallbacks(std::shared_ptr<GuiComponent> component) {
void GuiBuilder::recursivelyClearCallbacks(std::shared_ptr<GuiComponent> component) {
component->setCallback(GuiComponent::CallbackType::PRIMARY, nullptr);
component->setCallback(GuiComponent::CallbackType::SECONDARY, nullptr);
component->setCallback(GuiComponent::CallbackType::HOVER, nullptr);
for (auto& child : component->getChildren()) {
rClearCallbacks(child);
recursivelyClearCallbacks(child);
}
}

View File

@ -5,8 +5,9 @@
#pragma once
#include "SerialGui.h"
#include "../../def/ClientGame.h"
#include "components/GuiComponent.h"
#include "../../def/ClientGame.h"
#include "../../lua/api/class/LocalGuiElement.h"
class GuiContainer;
@ -15,15 +16,15 @@ public:
struct ComponentCallbacks { GuiComponent::callback left {}, right {}, hover {}; };
GuiBuilder(TextureAtlas& textures, ModelStore& models, std::shared_ptr<GuiContainer> root);
void setGuiTable(sol::state_view state, const sol::table menu);
void setGuiRoot(sol::state_view state, LocalGuiElement& menu);
void build(glm::ivec2 winBounds);
void clear(bool deleteRoot = true);
~GuiBuilder();
protected:
SerialGui::Element rDeserialize(sol::state_view state, sol::table menu);
void rCreate(const SerialGui::Element& element, std::shared_ptr<GuiComponent> parent, glm::ivec2 bounds);
static void rClearCallbacks(std::shared_ptr<GuiComponent> component);
SerialGui::Element recursivelyDeserialize(sol::state_view state, LocalGuiElement& elem);
void recursivelyCreate(const SerialGui::Element& element, std::shared_ptr<GuiComponent> parent, glm::ivec2 bounds);
static void recursivelyClearCallbacks(std::shared_ptr<GuiComponent> component);
virtual std::shared_ptr<GuiComponent> createComponent(const SerialGui::Element& elem, glm::ivec2 bounds);
TextureAtlas& textures;

View File

@ -3,11 +3,13 @@
//
#include "MenuSandbox.h"
#include "../../../lua/ErrorFormatter.h"
#include "../../../lua/api/menu/mDelay.h"
#include "../../../lua/api/menu/mSetGui.h"
#include "../../../lua/api/menu/mStartGame.h"
#include "../../../lua/ErrorFormatter.h"
#include "../../../lua/api/usertype/cGuiElement.h"
MenuSandbox::MenuSandbox(glm::ivec2 &win, ClientState& state, std::shared_ptr<GuiContainer> container) :
win(win),
@ -32,6 +34,8 @@ void MenuSandbox::loadApi() {
lua["zepha"] = core;
core["__builtin"] = lua.create_table();
ClientApi::gui_element(lua);
MenuApi::delay (core, delayed_functions);
MenuApi::set_gui (builder, win, lua, core);
MenuApi::start_game (state, core);

View File

@ -0,0 +1,87 @@
//
// Created by aurailus on 2020-04-12.
//
#include "LocalGuiElement.h"
LocalGuiElement::LocalGuiElement(const std::string& type, sol::table data) :
type(type) {
for (const auto& pair : data) {
if (pair.first.is<float>()) {
if (!pair.second.is<LocalGuiElement>()) throw std::runtime_error("Child is not a GuiElement.");
children.push_back(pair.second.as<LocalGuiElement>());
}
else if (pair.first.is<std::string>()) set(pair.first.as<std::string>(), pair.second);
}
}
sol::object LocalGuiElement::get(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[key];
return sol::nil;
}
sol::object LocalGuiElement::set(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>();
return val;
}
if ((!traits.count(key) || traits.at(key) != val) && updateFunction) updateFunction(id);
traits[key] = val;
return val;
}
sol::object LocalGuiElement::call(sol::this_state s, sol::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 LocalGuiElement::find(sol::this_state s, const std::string& key) {
for (auto& child : children) if (child.key == key) return sol::make_object<LocalGuiElement>(s, child);
for (auto& child : children) {
auto recurse = child.find(s, key);
if (recurse) return recurse;
}
return sol::nil;
}
void LocalGuiElement::append(sol::this_state s, sol::object elem) {
if (elem.is<LocalGuiElement>()) children.push_back(elem.as<LocalGuiElement>());
else if (elem.is<sol::function>()) children.push_back(call(s, elem.as<sol::function>()).as<LocalGuiElement>());
else throw std::runtime_error("Append arg is not an element or a function to generate one.");
if (updateFunction) updateFunction(id);
}
void LocalGuiElement::prepend(sol::this_state s, sol::object elem) {
if (elem.is<LocalGuiElement>()) children.insert(children.begin(), elem.as<LocalGuiElement>());
else if (elem.is<sol::function>()) children.insert(children.begin(), call(s, elem.as<sol::function>()).as<LocalGuiElement>());
else throw std::runtime_error("Append arg is not an element or a function to generate one.");
if (updateFunction) updateFunction(id);
}
void LocalGuiElement::remove(sol::optional<LocalGuiElement> elem) {
if (!elem) {
if (parent != nullptr) parent->remove(sol::make_optional<LocalGuiElement>(*this));
else throw std::runtime_error("Tried to remove self from nil parent.");
}
else {
for (const auto it = children.cbegin(); it != children.cend();) {
if (it->key == elem->key) {
children.erase(it);
if (updateFunction) updateFunction(id);
return;
}
}
}
}

View File

@ -0,0 +1,36 @@
//
// Created by aurailus on 2020-04-12.
//
#pragma once
#include <list>
#include <sol2/sol.hpp>
class LocalGuiElement {
public:
LocalGuiElement(const std::string& type, sol::table data);
sol::object get(sol::this_state s, const std::string& key);
sol::object set(const std::string& key, sol::object val);
sol::object call(sol::this_state s, sol::function fun);
sol::object find(sol::this_state s, const std::string& key);
void append(sol::this_state s, sol::object elem);
void prepend(sol::this_state s, sol::object elem);
void remove(sol::optional<LocalGuiElement> elem);
std::string type {}, key {};
LocalGuiElement* parent = nullptr;
std::list<LocalGuiElement> children {};
std::unordered_map<std::string, sol::function> callbacks {};
std::unordered_map<std::string, sol::object> traits {};
// Internal data
unsigned int id = 0;
std::function<void(unsigned int id)> updateFunction = nullptr;
};

View File

@ -5,12 +5,13 @@
#pragma once
#include <sol2/sol.hpp>
#include "../class/LocalGuiElement.h"
#include "../../../game/hud/GuiBuilder.h"
namespace MenuApi {
void set_gui(GuiBuilder& builder, glm::ivec2& win, sol::state& lua, sol::table& core) {
core.set_function("set_gui", [&](sol::this_state s, sol::table gui) {
builder.setGuiTable(sol::state_view(s), gui);
core.set_function("set_gui", [&](sol::this_state s, LocalGuiElement& gui) {
builder.setGuiRoot(sol::state_view(s), gui);
builder.build(win);
});
}

View File

@ -0,0 +1,26 @@
//
// Created by aurailus on 2020-04-12.
//
#pragma once
#include <sol2/sol.hpp>
#include "../class/LocalGuiElement.h"
namespace ClientApi {
static void gui_element(sol::state& lua) {
lua.new_usertype<LocalGuiElement>("GuiElement",
sol::constructors<LocalGuiElement(std::string, sol::object)>(),
sol::meta_function::index, &LocalGuiElement::get,
sol::meta_function::new_index, &LocalGuiElement::set,
sol::meta_function::call, &LocalGuiElement::call,
"find", &LocalGuiElement::find,
"append", &LocalGuiElement::append,
"prepend", &LocalGuiElement::prepend,
"remove", &LocalGuiElement::remove
);
}
}

View File

@ -11,6 +11,7 @@
#include "LocalLuaParser.h"
// Usertypes
#include "../api/usertype/cGuiElement.h"
#include "../api/usertype/cItemStack.h"
#include "../api/usertype/cLocalPlayer.h"
#include "../api/usertype/cLuaEntity.h"
@ -75,6 +76,7 @@ void LocalLuaParser::loadApi(ClientGame &defs, LocalWorld &world, Player& player
ClientApi::local_player (lua);
ClientApi::inventory (lua);
ClientApi::item_stack (lua);
ClientApi::gui_element (lua);
core["client"] = true;
core["player"] = LocalLuaPlayer(player);

View File

@ -1,6 +1,7 @@
local menu = zepha.build_gui(function()
return Gui.Body {
background = "#214a21",
-- background = "#214a21",
background = "#334",
Gui.Text {
position = { 4, 4 },
@ -35,4 +36,12 @@ menu(function(e)
})
end)
menu:append(function()
return Gui.Text {
position = { 300, 32 },
color = "#f39",
content = "AAAAAA"
}
end)
zepha.set_gui(menu)

View File

@ -20,9 +20,7 @@ zepha.set_gui(zepha.build_gui(function()
key = "buttonPlay",
callbacks = {
primary = function()
zepha.start_game_local()
end
primary = function() zepha.start_game_local() end
},
position = { 6, 50 },
@ -36,9 +34,7 @@ zepha.set_gui(zepha.build_gui(function()
key = "buttonServers",
callbacks = {
primary = function()
zepha.start_game()
end
primary = function() zepha.start_game() end
},
position = { 6, 74 },