From 1dd0e9f9395e2564e4e262e4f06062ef383ec5fa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 7 Feb 2024 19:29:20 +0300 Subject: [PATCH] UiDocument --- src/content/ContentLoader.cpp | 4 +- src/frontend/InventoryView.cpp | 5 ++- src/frontend/InventoryView.h | 7 +++- src/frontend/UiDocument.cpp | 47 ++++++++++++++++++++++ src/frontend/UiDocument.h | 49 +++++++++++++++++++++++ src/frontend/gui/UINode.cpp | 37 +++++++++-------- src/frontend/gui/UINode.h | 4 ++ src/frontend/gui/gui_xml.cpp | 11 +++++- src/frontend/gui/gui_xml.h | 8 +++- src/frontend/gui/panels.cpp | 4 ++ src/frontend/gui/panels.h | 4 +- src/frontend/hud.cpp | 4 +- src/frontend/hud.h | 1 - src/logic/scripting/LuaState.cpp | 22 +++++++++++ src/logic/scripting/LuaState.h | 3 ++ src/logic/scripting/scripting.cpp | 66 +++++++++++++++++++++++++------ src/logic/scripting/scripting.h | 23 ++++++++++- 17 files changed, 257 insertions(+), 42 deletions(-) create mode 100644 src/frontend/UiDocument.cpp create mode 100644 src/frontend/UiDocument.h diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 54f585f9..02cb648f 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -255,7 +255,7 @@ void ContentLoader::loadBlock(Block* def, std::string full, std::string name) { fs::path scriptfile = folder/fs::path("scripts/"+def->scriptName+".lua"); if (fs::is_regular_file(scriptfile)) { - scripting::load_block_script(full, scriptfile, &def->rt.funcsset); + scripting::load_block_script(full, scriptfile, def->rt.funcsset); } } @@ -267,7 +267,7 @@ void ContentLoader::loadItem(ItemDef* def, std::string full, std::string name) { fs::path scriptfile = folder/fs::path("scripts/"+def->scriptName+".lua"); if (fs::is_regular_file(scriptfile)) { - scripting::load_item_script(full, scriptfile, &def->rt.funcsset); + scripting::load_item_script(full, scriptfile, def->rt.funcsset); } } diff --git a/src/frontend/InventoryView.cpp b/src/frontend/InventoryView.cpp index dfc3608a..ff4b80e1 100644 --- a/src/frontend/InventoryView.cpp +++ b/src/frontend/InventoryView.cpp @@ -344,11 +344,12 @@ std::shared_ptr InventoryView::readXML( LevelFrontend* frontend, InventoryInteraction& interaction, const std::string& src, - const std::string& file + const std::string& file, + const scripting::Environment& env ) { auto view = std::make_shared(frontend, interaction); - gui::UiXmlReader reader; + gui::UiXmlReader reader(env); reader.add("inventory", [=](gui::UiXmlReader& reader, xml::xmlelement element) { reader.readUINode(reader, element, *view); return view; diff --git a/src/frontend/InventoryView.h b/src/frontend/InventoryView.h index 7fcd1912..84cbf234 100644 --- a/src/frontend/InventoryView.h +++ b/src/frontend/InventoryView.h @@ -18,6 +18,10 @@ class ContentIndices; class LevelFrontend; class Inventory; +namespace scripting { + class Environment; +} + using itemsharefunc = std::function; using slotcallback = std::function; @@ -105,7 +109,8 @@ public: LevelFrontend* frontend, InventoryInteraction& interaction, const std::string& src, - const std::string& file + const std::string& file, + const scripting::Environment& env ); static const int SLOT_INTERVAL = 4; diff --git a/src/frontend/UiDocument.cpp b/src/frontend/UiDocument.cpp new file mode 100644 index 00000000..d7a036e4 --- /dev/null +++ b/src/frontend/UiDocument.cpp @@ -0,0 +1,47 @@ +#include "UiDocument.h" + +#include "gui/UINode.h" +#include "gui/panels.h" +#include "InventoryView.h" +#include "../logic/scripting/scripting.h" +#include "../files/files.h" + +UiDocument::UiDocument( + std::string namesp, + uidocscript script, + std::shared_ptr root +) { + collect(map, root); +} + +void UiDocument::collect(uinodes_map& map, std::shared_ptr node) { + const std::string& id = node->getId(); + if (!id.empty()) { + map[id] = node; + } + auto container = dynamic_cast(node.get()); + if (container) { + for (auto subnode : container->getNodes()) { + collect(map, subnode); + } + } +} + +std::unique_ptr UiDocument::readInventory( + std::string namesp, + fs::path file, + LevelFrontend* frontend, + InventoryInteraction& interaction +) { + const std::string text = files::read_string(file); + auto env = scripting::create_environment(); + auto view = InventoryView::readXML( + frontend, interaction, text, file.u8string(), *env + ); + uidocscript script {}; + auto scriptFile = fs::path(file.u8string()+".lua"); + if (fs::is_regular_file(scriptFile)) { + scripting::load_layout_script(scriptFile, script); + } + return std::make_unique(namesp, script, view); +} diff --git a/src/frontend/UiDocument.h b/src/frontend/UiDocument.h new file mode 100644 index 00000000..83317a3c --- /dev/null +++ b/src/frontend/UiDocument.h @@ -0,0 +1,49 @@ +#ifndef FRONTEND_UI_DOCUMENT_H_ +#define FRONTEND_UI_DOCUMENT_H_ + +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace gui { + class UINode; +} + +class InventoryInteraction; +class LevelFrontend; + +struct uidocscript { + int environment; + bool onopen : 1; + bool onclose : 1; +}; + +using uinodes_map = std::unordered_map>; + +class UiDocument { + std::string namesp; + uidocscript script; + uinodes_map map; + std::shared_ptr root; +public: + UiDocument(std::string namesp, uidocscript script, std::shared_ptr root); + + const uinodes_map& getMap() const; + const std::string& getNamespace() const; + + /* Collect map of all uinodes having identifiers */ + static void collect(uinodes_map& map, std::shared_ptr node); + + /* @return root node is always an InventoryView */ + static std::unique_ptr readInventory ( + std::string namesp, + fs::path file, + LevelFrontend* frontend, + InventoryInteraction& interaction + ); +}; + +#endif // FRONTEND_UI_DOCUMENT_H_ diff --git a/src/frontend/gui/UINode.cpp b/src/frontend/gui/UINode.cpp index 653e0f97..dd788398 100644 --- a/src/frontend/gui/UINode.cpp +++ b/src/frontend/gui/UINode.cpp @@ -5,10 +5,7 @@ using gui::UINode; using gui::Align; -using glm::vec2; -using glm::vec4; - -UINode::UINode(vec2 coord, vec2 size) : coord(coord), size(size) { +UINode::UINode(glm::vec2 coord, glm::vec2 size) : coord(coord), size(size) { } UINode::~UINode() { @@ -67,13 +64,13 @@ bool UINode::isFocused() const { } bool UINode::isInside(glm::vec2 pos) { - vec2 coord = calcCoord(); - vec2 size = getSize(); + glm::vec2 coord = calcCoord(); + glm::vec2 size = getSize(); return (pos.x >= coord.x && pos.y >= coord.y && pos.x < coord.x + size.x && pos.y < coord.y + size.y); } -std::shared_ptr UINode::getAt(vec2 pos, std::shared_ptr self) { +std::shared_ptr UINode::getAt(glm::vec2 pos, std::shared_ptr self) { if (!interactive) { return nullptr; } @@ -96,7 +93,7 @@ bool UINode::isResizing() const { return resizing; } -vec2 UINode::calcCoord() const { +glm::vec2 UINode::calcCoord() const { if (parent) { return coord + parent->calcCoord() + parent->contentOffset(); } @@ -109,19 +106,19 @@ void UINode::scrolled(int value) { } } -void UINode::setCoord(vec2 coord) { +void UINode::setCoord(glm::vec2 coord) { this->coord = coord; } -vec2 UINode::getSize() const { +glm::vec2 UINode::getSize() const { return size; } -void UINode::setSize(vec2 size) { +void UINode::setSize(glm::vec2 size) { this->size = size; } -void UINode::setColor(vec4 color) { +void UINode::setColor(glm::vec4 color) { this->color = color; this->hoverColor = color; } @@ -134,17 +131,25 @@ glm::vec4 UINode::getHoverColor() const { return hoverColor; } -vec4 UINode::getColor() const { +glm::vec4 UINode::getColor() const { return color; } -void UINode::setMargin(vec4 margin) { +void UINode::setMargin(glm::vec4 margin) { this->margin = margin; } -vec4 UINode::getMargin() const { +glm::vec4 UINode::getMargin() const { return margin; } void UINode::lock() { -} \ No newline at end of file +} + +void UINode::setId(const std::string& id) { + this->id = id; +} + +const std::string& UINode::getId() const { + return id; +} diff --git a/src/frontend/gui/UINode.h b/src/frontend/gui/UINode.h index 630bb54b..cdbb4c24 100644 --- a/src/frontend/gui/UINode.h +++ b/src/frontend/gui/UINode.h @@ -21,6 +21,7 @@ namespace gui { }; class UINode { + std::string id = ""; protected: glm::vec2 coord; glm::vec2 size; @@ -110,6 +111,9 @@ namespace gui { virtual void setSize(glm::vec2 size); virtual void refresh() {}; virtual void lock(); + + void setId(const std::string& id); + const std::string& getId() const; }; } diff --git a/src/frontend/gui/gui_xml.cpp b/src/frontend/gui/gui_xml.cpp index 3499b825..179da876 100644 --- a/src/frontend/gui/gui_xml.cpp +++ b/src/frontend/gui/gui_xml.cpp @@ -21,6 +21,9 @@ static Align align_from_string(const std::string& str, Align def) { /* Read basic UINode properties */ static void _readUINode(xml::xmlelement element, UINode& node) { + if (element->has("id")) { + node.setId(element->attr("id").getText()); + } if (element->has("coord")) { node.setCoord(element->attr("coord").asVec2()); } @@ -111,7 +114,7 @@ static std::shared_ptr readButton(UiXmlReader& reader, xml::xmlelement e _readPanel(reader, element, *button); if (element->has("onclick")) { - auto callback = scripting::create_runnable("", element->attr("onclick").getText()); + auto callback = scripting::create_runnable("", element->attr("onclick").getText(), reader.getEnvironment()); button->listenAction([callback](GUI*) { callback(); }); @@ -139,7 +142,7 @@ static std::shared_ptr readTextBox(UiXmlReader& reader, xml::xmlelement return textbox; } -UiXmlReader::UiXmlReader() { +UiXmlReader::UiXmlReader(const scripting::Environment& env) : env(env) { add("label", readLabel); add("button", readButton); add("textbox", readTextBox); @@ -181,3 +184,7 @@ std::shared_ptr UiXmlReader::readXML( const std::string& UiXmlReader::getFilename() const { return filename; } + +const scripting::Environment& UiXmlReader::getEnvironment() const { + return env; +} diff --git a/src/frontend/gui/gui_xml.h b/src/frontend/gui/gui_xml.h index 0b1371c4..0637be00 100644 --- a/src/frontend/gui/gui_xml.h +++ b/src/frontend/gui/gui_xml.h @@ -7,6 +7,10 @@ #include "GUI.h" #include "../../coders/xml.h" +namespace scripting { + class Environment; +} + namespace gui { class UiXmlReader; @@ -15,8 +19,9 @@ namespace gui { class UiXmlReader { std::unordered_map readers; std::string filename; + const scripting::Environment& env; public: - UiXmlReader(); + UiXmlReader(const scripting::Environment& env); void add(const std::string& tag, uinode_reader reader); @@ -44,6 +49,7 @@ namespace gui { xml::xmlelement root ); + const scripting::Environment& getEnvironment() const; const std::string& getFilename() const; }; } diff --git a/src/frontend/gui/panels.cpp b/src/frontend/gui/panels.cpp index 23f82b0a..171b721c 100644 --- a/src/frontend/gui/panels.cpp +++ b/src/frontend/gui/panels.cpp @@ -144,6 +144,10 @@ void Container::setSize(glm::vec2 size) { refresh(); } +const std::vector>& Container::getNodes() const { + return nodes; +} + Panel::Panel(vec2 size, glm::vec4 padding, float interval) : Container(vec2(), size), padding(padding), diff --git a/src/frontend/gui/panels.h b/src/frontend/gui/panels.h index 6c7b225c..d60dc59c 100644 --- a/src/frontend/gui/panels.h +++ b/src/frontend/gui/panels.h @@ -46,7 +46,9 @@ namespace gui { virtual void setScrollable(bool flag); void listenInterval(float interval, ontimeout callback, int repeat=-1); virtual glm::vec2 contentOffset() override {return glm::vec2(0.0f, scroll);}; - virtual void setSize(glm::vec2 size); + virtual void setSize(glm::vec2 size) override; + + const std::vector>& getNodes() const; }; class Panel : public Container { diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 9cb0a87f..a1e8c6af 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -31,6 +31,7 @@ #include "../objects/Player.h" #include "../physics/Hitbox.h" #include "../maths/voxmaths.h" +#include "../files/files.h" #include "gui/controls.h" #include "gui/panels.h" #include "gui/UINode.h" @@ -172,7 +173,7 @@ std::shared_ptr HudRenderer::createContentAccess() { auto indices = content->getIndices(); auto player = level->player; auto inventory = player->getInventory(); - + int itemsCount = indices->countItemDefs(); auto accessInventory = std::make_shared(0, itemsCount); for (int id = 1; id < itemsCount; id++) { @@ -376,7 +377,6 @@ void HudRenderer::draw(const GfxContext& ctx){ uishader->use(); uishader->uniformMatrix("u_projview", uicamera->getProjView()); - // Draw selected item preview hotbarView->setCoord(glm::vec2(width/2, height-65)); hotbarView->setSelected(player->getChosenSlot()); diff --git a/src/frontend/hud.h b/src/frontend/hud.h index 320da109..fe2e45a2 100644 --- a/src/frontend/hud.h +++ b/src/frontend/hud.h @@ -49,7 +49,6 @@ class HudRenderer { LevelFrontend* frontend; std::shared_ptr createDebugPanel(Engine* engine); - std::shared_ptr createContentAccess(); std::shared_ptr createHotbar(); std::shared_ptr createInventory(); diff --git a/src/logic/scripting/LuaState.cpp b/src/logic/scripting/LuaState.cpp index d890c59b..5e4b6983 100644 --- a/src/logic/scripting/LuaState.cpp +++ b/src/logic/scripting/LuaState.cpp @@ -51,6 +51,16 @@ bool lua::LuaState::getglobal(const std::string& name) { return true; } +bool lua::LuaState::hasglobal(const std::string& name) { + lua_getglobal(L, name.c_str()); + if (lua_isnil(L, lua_gettop(L))) { + lua_pop(L, lua_gettop(L)); + return false; + } + lua_pop(L, lua_gettop(L)); + return true; +} + void lua::LuaState::setglobal(const std::string& name) { lua_setglobal(L, name.c_str()); } @@ -196,6 +206,18 @@ int lua::LuaState::createEnvironment() { return id; } +void lua::LuaState::removeEnvironment(int id) { + if (currentEnvironment == id) { + setEnvironment(0); + } + lua_pushnil(L); + setglobal("_N"+util::mangleid(id)); +} + +int lua::LuaState::getEnvironment() const { + return currentEnvironment; +} + void lua::LuaState::setEnvironment(int id) { if (id == 0) { getglobal(":G"); diff --git a/src/logic/scripting/LuaState.h b/src/logic/scripting/LuaState.h index 7d43d996..d2f64da7 100644 --- a/src/logic/scripting/LuaState.h +++ b/src/logic/scripting/LuaState.h @@ -41,10 +41,13 @@ namespace lua { void addfunc(const std::string& name, lua_CFunction func); bool getglobal(const std::string& name); void setglobal(const std::string& name); + bool hasglobal(const std::string& name); bool rename(const std::string& from, const std::string& to); void remove(const std::string& name);; void createFuncs(); int createEnvironment(); + void removeEnvironment(int id); + int getEnvironment() const; void setEnvironment(int id); const std::string storeAnonymous(); diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index edd11c5b..985fd173 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -11,6 +11,7 @@ #include "../../voxels/Block.h" #include "../../items/ItemDef.h" #include "../../logic/BlocksController.h" +#include "../../frontend/UiDocument.h" #include "../../engine.h" #include "LuaState.h" #include "../../util/stringutil.h" @@ -28,6 +29,19 @@ const Content* scripting::content = nullptr; const ContentIndices* scripting::indices = nullptr; BlocksController* scripting::blocks = nullptr; +Environment::Environment(int env) : env(env) { +} + +Environment::~Environment() { + if (env) { + state->removeEnvironment(env); + } +} + +int Environment::getId() const { + return env; +} + void load_script(fs::path name) { auto paths = scripting::engine->getPaths(); fs::path file = paths->getResources()/fs::path("scripts")/name; @@ -54,6 +68,19 @@ runnable scripting::create_runnable( }; } +runnable scripting::create_runnable( + const std::string& file, + const std::string& src, + const Environment& env +) { + return [=](){ + int previous = state->getEnvironment(); + state->setEnvironment(env.getId()); + state->execute(src, file); + state->setEnvironment(previous); + }; +} + wstringconsumer scripting::create_wstring_consumer( const std::string& src, const std::string& file @@ -75,6 +102,10 @@ wstringconsumer scripting::create_wstring_consumer( }; } +std::unique_ptr scripting::create_environment() { + return std::make_unique(state->createEnvironment()); +} + void scripting::on_world_load(Level* level, BlocksController* blocks) { scripting::level = level; scripting::content = level->content; @@ -186,27 +217,27 @@ bool scripting::on_item_break_block(Player* player, const ItemDef* item, int x, return false; } -void scripting::load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset) { +void scripting::load_block_script(std::string prefix, fs::path file, block_funcs_set& funcsset) { std::string src = files::read_string(file); std::cout << "loading script " << file.u8string() << std::endl; state->execute(src, file.u8string()); - funcsset->init=state->rename("init", prefix+".init"); - funcsset->update=state->rename("on_update", prefix+".update"); - funcsset->randupdate=state->rename("on_random_update", prefix+".randupdate"); - funcsset->onbroken=state->rename("on_broken", prefix+".broken"); - funcsset->onplaced=state->rename("on_placed", prefix+".placed"); - funcsset->oninteract=state->rename("on_interact", prefix+".oninteract"); - funcsset->onblockstick=state->rename("on_blocks_tick", prefix+".blockstick"); + funcsset.init=state->rename("init", prefix+".init"); + funcsset.update=state->rename("on_update", prefix+".update"); + funcsset.randupdate=state->rename("on_random_update", prefix+".randupdate"); + funcsset.onbroken=state->rename("on_broken", prefix+".broken"); + funcsset.onplaced=state->rename("on_placed", prefix+".placed"); + funcsset.oninteract=state->rename("on_interact", prefix+".oninteract"); + funcsset.onblockstick=state->rename("on_blocks_tick", prefix+".blockstick"); } -void scripting::load_item_script(std::string prefix, fs::path file, item_funcs_set* funcsset) { +void scripting::load_item_script(std::string prefix, fs::path file, item_funcs_set& funcsset) { std::string src = files::read_string(file); std::cout << "loading script " << file.u8string() << std::endl; state->execute(src, file.u8string()); - funcsset->init=state->rename("init", prefix+".init"); - funcsset->on_use_on_block=state->rename("on_use_on_block", prefix+".useon"); - funcsset->on_block_break_by=state->rename("on_block_break_by", prefix+".blockbreakby"); + funcsset.init=state->rename("init", prefix+".init"); + funcsset.on_use_on_block=state->rename("on_use_on_block", prefix+".useon"); + funcsset.on_block_break_by=state->rename("on_block_break_by", prefix+".blockbreakby"); } void scripting::load_world_script(std::string prefix, fs::path file) { @@ -222,6 +253,17 @@ void scripting::load_world_script(std::string prefix, fs::path file) { state->rename("on_world_quit", prefix+".worldquit"); } +void scripting::load_layout_script(fs::path file, uidocscript& script) { + std::string src = files::read_string(file); + std::cout << "loading script " << file.u8string() << std::endl; + + script.environment = state->createEnvironment(); + state->loadbuffer(src, file.u8string()); + state->callNoThrow(0); + script.onopen = state->hasglobal("on_open"); + script.onclose = state->hasglobal("on_close"); +} + void scripting::close() { delete state; diff --git a/src/logic/scripting/scripting.h b/src/logic/scripting/scripting.h index dde825ff..b8fe6f55 100644 --- a/src/logic/scripting/scripting.h +++ b/src/logic/scripting/scripting.h @@ -16,6 +16,7 @@ class Player; class ItemDef; struct block_funcs_set; struct item_funcs_set; +struct uidocscript; class BlocksController; namespace scripting { @@ -25,18 +26,35 @@ namespace scripting { extern Level* level; extern BlocksController* blocks; + class Environment { + int env; + public: + Environment(int env); + ~Environment(); + + int getId() const; + }; + void initialize(Engine* engine); runnable create_runnable( const std::string& filename, const std::string& source ); + + runnable create_runnable( + const std::string& filename, + const std::string& source, + const Environment& env + ); wstringconsumer create_wstring_consumer( const std::string& src, const std::string& file="" ); + std::unique_ptr create_environment(); + void on_world_load(Level* level, BlocksController* blocks); void on_world_save(); void on_world_quit(); @@ -48,8 +66,9 @@ namespace scripting { bool on_block_interact(Player* player, const Block* block, int x, int y, int z); bool on_item_use_on_block(Player* player, const ItemDef* item, int x, int y, int z); bool on_item_break_block(Player* player, const ItemDef* item, int x, int y, int z); - void load_block_script(std::string prefix, fs::path file, block_funcs_set* funcsset); - void load_item_script(std::string prefix, fs::path file, item_funcs_set* funcsset); + void load_block_script(std::string prefix, fs::path file, block_funcs_set& funcsset); + void load_item_script(std::string prefix, fs::path file, item_funcs_set& funcsset); void load_world_script(std::string prefix, fs::path file); + void load_layout_script(fs::path file, uidocscript& script); void close(); }