diff --git a/res/scripts/hud.lua b/res/scripts/hud.lua index d10d00e1e..8529ef991 100644 --- a/res/scripts/hud.lua +++ b/res/scripts/hud.lua @@ -127,11 +127,11 @@ function on_hud_open() hud.default_hand_controller = update_hand - local name = "gui/no_icon" - gui.create_frame(name, {16, 16}) + local name = "misc/moon" + gui.create_frame(name, name, {128, 128}) local document = Document.new(name) - document.root:add("") + document.root:add("") document.root.color = {255, 0, 0, 255} end diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 683f27e8a..8d6ac26c0 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -232,6 +232,7 @@ _GUI_ROOT = Document.new("core:root") _MENU = _GUI_ROOT.menu menu = _MENU gui.root = _GUI_ROOT +gui.main_frame_id = "core:main" do local status, err = pcall(function() @@ -435,6 +436,7 @@ function __vc_on_hud_open() if menu.page ~= "" then if not menu:back() then menu:reset() + gui.set_active_frame("") end elseif hud.is_inventory_open() then hud.close_inventory() diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 70bb5dae9..4799e9d77 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -302,10 +302,12 @@ void Engine::startPauseLoop() { } void Engine::renderFrame() { + if (input->isCursorLocked() != (gui->getActiveFrame() == nullptr)) { + input->toggleCursor(); + } screen->draw(time.getDelta()); DrawContext ctx(nullptr, *window, nullptr); - gui->renderFrames(ctx, *assets->getStorage()); gui->draw(ctx, *assets->getStorage()); } diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index d33c67ec6..6f3593610 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -207,9 +207,6 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player) } Hud::~Hud() { - if (input.isCursorLocked()) { - input.toggleCursor(); - } // removing all controlled ui for (auto& element : elements) { onRemove(element); @@ -342,9 +339,6 @@ void Hud::update(bool visible) { if (!gui.isFocusCaught()) { processInput(visible); } - if ((isMenuOpen || inventoryOpen) == input.isCursorLocked()) { - input.toggleCursor(); - } if (blockUI) { voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z); @@ -395,6 +389,7 @@ void Hud::openInventory(bool playerInventory) { inventoryView->bind(inventory, &content); add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false)); } + gui.setActiveFrame(GUI::CORE_MAIN); } std::shared_ptr Hud::openInventory( @@ -548,6 +543,7 @@ void Hud::closeInventory() { } } cleanup(); + gui.setActiveFrame(""); } void Hud::add(const HudElement& element, const dv::value& argsArray) { @@ -710,9 +706,11 @@ void Hud::setPause(bool pause) { if (!pause && menu.hasOpenPage()) { menu.reset(); + gui.setActiveFrame(""); } if (pause && !menu.hasOpenPage()) { menu.setPage("pause"); + gui.setActiveFrame(GUI::CORE_MAIN); } } diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 0dd1894b0..dfeb1dfb4 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -56,6 +56,7 @@ LevelScreen::LevelScreen( auto& assets = *engine.getAssets(); auto menu = engine.getGUI().getMenu(); menu->reset(); + gui.setActiveFrame(""); auto player = level->players->get(localPlayer); assert(player != nullptr); diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 7677ca9f8..73bf73e2c 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -1,10 +1,10 @@ #include "GUI.hpp" #include -#include #include #include "assets/Assets.hpp" +#include "debug/Logger.hpp" #include "elements/Label.hpp" #include "elements/Menu.hpp" #include "elements/Panel.hpp" @@ -27,13 +27,15 @@ #include #include +static debug::Logger logger("gui"); + using namespace gui; GUI::GUI(Engine& engine) : engine(engine), input(engine.getInput()), batch2D(std::make_unique(1024)), - container(std::make_shared(*this, glm::vec2(1000))) { + container(std::make_shared(*this, CORE_MAIN, "")) { container->setId("root"); uicamera = std::make_unique(glm::vec3(), engine.getWindow().getSize().y); @@ -64,6 +66,8 @@ GUI::GUI(Engine& engine) std::dynamic_pointer_cast(container), nullptr ); + addFrame(container); + activeFrame = container; } GUI::~GUI() = default; @@ -118,7 +122,9 @@ void GUI::updateTooltip(float delta) { } /// @brief Mouse related input and logic handling -void GUI::actMouse(float delta, const CursorState& cursor) { +void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) { + auto& state = frame.getState(); + float mouseDelta = glm::length(cursor.delta); doubleClicked = false; doubleClickTimer += delta + mouseDelta * 0.1f; @@ -158,13 +164,13 @@ void GUI::actMouse(float delta, const CursorState& cursor) { } for (auto it = mouseOver.begin(); it != mouseOver.end(); ) { - auto node = it->lock(); - if (node) { - if (node->isInside(cursor.pos)) { + auto mouseOverNode = it->lock(); + if (mouseOverNode) { + if (mouseOverNode->isInside(cursor.pos)) { ++it; continue; } - node->setMouseOver(false); + mouseOverNode->setMouseOver(false); } it = mouseOver.erase(it); } @@ -233,14 +239,16 @@ void GUI::actFocused() { void GUI::act(float delta, const glm::uvec2& vp) { container->setSize(vp); - container->act(delta); + for (auto& pair : frames) { + pair.second->act(delta); + } auto prevfocus = focus; updateTooltip(delta); const auto& cursor = input.getCursor(); - if (!cursor.locked) { - actMouse(delta, cursor); + if (!cursor.locked && activeFrame) { + actMouse(*activeFrame, delta, cursor); } else { if (hover) { hover->setMouseEnter(false); @@ -263,22 +271,7 @@ void GUI::postAct() { } } -void GUI::renderFrames(const DrawContext& pctx, Assets& assets) { - auto ctx = pctx.sub(batch2D.get()); - - auto uishader = assets.get("ui"); - uishader->use(); - uishader->uniformMatrix("u_projview", uicamera->getProjView()); - - batch2D->begin(); - for (auto& [outputTexture, frame] : frames) { - frame->updateOutput(assets); - frame->draw(ctx, assets); - } - batch2D->flush(); -} - -void GUI::draw(const DrawContext& pctx, const Assets& assets) { +void GUI::draw(const DrawContext& pctx, Assets& assets) { auto ctx = pctx.sub(batch2D.get()); auto& viewport = ctx.getViewport(); @@ -300,7 +293,10 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) { uishader->uniformMatrix("u_projview", uicamera->getProjView()); batch2D->begin(); - container->draw(ctx, assets); + for (auto& [outputTexture, frame] : frames) { + frame->updateOutput(assets); + frame->draw(ctx, assets); + } if (hover) { engine.getWindow().setCursor(hover->getCursor()); @@ -354,7 +350,26 @@ void GUI::add(std::shared_ptr node) { } void GUI::addFrame(std::shared_ptr frame) { - frames[frame->getOutputTexture()] = std::move(frame); + const auto& id = frame->getFrameId(); + frames[id] = std::move(frame); +} + +void GUI::setActiveFrame(const std::string& id) { + if (id.empty()) { + activeFrame = nullptr; + return; + } + const auto& found = frames.find(id); + if (found == frames.end()) { + logger.error() << "attempted to make non-existing frame '" << id + << "' as active"; + return; + } + activeFrame = found->second; +} + +std::shared_ptr GUI::getActiveFrame() const { + return activeFrame; } void GUI::remove(UINode* node) noexcept { diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index b990d93ab..ee3fc1cd5 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -68,7 +68,7 @@ namespace gui { Engine& engine; Input& input; std::unique_ptr batch2D; - std::shared_ptr container; + std::shared_ptr container; std::shared_ptr hover; std::shared_ptr pressed; std::shared_ptr focus; @@ -76,6 +76,7 @@ namespace gui { std::shared_ptr rootDocument; std::unique_ptr syntaxColorScheme; std::unordered_map> storage; + std::shared_ptr activeFrame; std::unique_ptr uicamera; std::shared_ptr menu; @@ -89,11 +90,12 @@ namespace gui { bool doubleClicked = false; bool debug = false; - void actMouse(float delta, const CursorState& cursor); + void actMouse(Frame& frame, float delta, const CursorState& cursor); void actFocused(); void updateTooltip(float delta); void resetTooltip(); public: + static inline std::string CORE_MAIN = "core:main"; static constexpr int CONTEXT_MENU_ZINDEX = 999; GUI(Engine& engine); @@ -114,12 +116,10 @@ namespace gui { /// @param viewport window size void act(float delta, const glm::uvec2& viewport); - void renderFrames(const DrawContext& pctx, Assets& assets); - /// @brief Draw all visible elements on main container /// @param pctx parent graphics context /// @param assets active assets storage - void draw(const DrawContext& pctx, const Assets& assets); + void draw(const DrawContext& pctx, Assets& assets); void postAct(); @@ -129,6 +129,10 @@ namespace gui { void addFrame(std::shared_ptr frame); + void setActiveFrame(const std::string& id); + + std::shared_ptr getActiveFrame() const; + /// @brief Remove node from the main container void remove(UINode* node) noexcept; diff --git a/src/graphics/ui/elements/Frame.cpp b/src/graphics/ui/elements/Frame.cpp index 8f6840c9c..41e928965 100644 --- a/src/graphics/ui/elements/Frame.cpp +++ b/src/graphics/ui/elements/Frame.cpp @@ -13,15 +13,20 @@ using namespace gui; static inline constexpr int MAX_TEXTURE_SIZE = 2048; -gui::Frame::Frame(GUI& gui, std::string outputTexture) +gui::Frame::Frame(GUI& gui, std::string id, std::string outputTexture) : Container(gui, {}), fbo(nullptr), outputTexture(std::move(outputTexture)) { + frameId = std::move(id); } gui::Frame::~Frame() = default; void gui::Frame::draw(const DrawContext& pctx, const Assets& assets) { + if (outputTexture.empty()) { + Container::draw(pctx, assets); + return; + } if (fbo == nullptr) { return; } @@ -53,3 +58,11 @@ void gui::Frame::updateOutput(Assets& assets) { const std::string& gui::Frame::getOutputTexture() const { return outputTexture; } + +const std::string& gui::Frame::getFrameId() const { + return frameId; +} + +gui::Frame::State& gui::Frame::getState() { + return state; +} diff --git a/src/graphics/ui/elements/Frame.hpp b/src/graphics/ui/elements/Frame.hpp index fc61b37a2..ec34a3c69 100644 --- a/src/graphics/ui/elements/Frame.hpp +++ b/src/graphics/ui/elements/Frame.hpp @@ -8,7 +8,10 @@ class UiDocument; namespace gui { class Frame final : public Container { public: - Frame(GUI& gui, std::string outputTexture); + struct State { + }; + + Frame(GUI& gui, std::string id, std::string outputTexture); virtual ~Frame(); void draw(const DrawContext& pctx, const Assets& assets) override; @@ -16,7 +19,14 @@ namespace gui { void updateOutput(Assets& assets); const std::string& getOutputTexture() const; + + const std::string& getFrameId() const; + + /// @brief Frame state is controlled in gui::GUI + State& getState(); private: + std::string frameId; + State state {}; std::unique_ptr fbo; std::string outputTexture; }; diff --git a/src/graphics/ui/gui_util.cpp b/src/graphics/ui/gui_util.cpp index 9eab2742d..6c240e601 100644 --- a/src/graphics/ui/gui_util.cpp +++ b/src/graphics/ui/gui_util.cpp @@ -78,6 +78,7 @@ void guiutil::alert( })); menu.addPage("", panel, true); menu.setPage(""); + gui.setActiveFrame(GUI::CORE_MAIN); } void guiutil::confirm( @@ -155,6 +156,7 @@ void guiutil::confirm( panel->refresh(); menu->addPage("", container, true); menu->setPage(""); + gui.setActiveFrame(GUI::CORE_MAIN); } void guiutil::confirm_with_memo( @@ -201,4 +203,5 @@ void guiutil::confirm_with_memo( panel->refresh(); menu->addPage("", panel, true); menu->setPage(""); + gui.setActiveFrame(GUI::CORE_MAIN); } diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index c71eb7e80..0d6292d7a 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -1154,21 +1154,44 @@ static int l_create_frame(lua::State* L) { if (engine->isHeadless()) { return 0; } - std::string outputTexture = lua::require_string(L, 1); - auto size = lua::tovec2(L, 2); + std::string id = lua::require_string(L, 1); + std::string outputTexture = lua::require_string(L, 2); + auto size = lua::tovec2(L, 3); auto& gui = engine->getGUI(); - auto frame = std::make_shared(gui, outputTexture); + auto frame = std::make_shared(gui, id, outputTexture); frame->setSize(std::move(size)); auto& assets = *engine->getAssets(); auto document = std::make_shared( - outputTexture, UiDocScript {}, frame, nullptr + id, UiDocScript {}, frame, nullptr ); - assets.store(document, document->getId()); + assets.store(document, id); gui.addFrame(std::move(frame)); return 0; } +static int l_set_active_frame(lua::State* L) { + if (engine->isHeadless()) { + return 0; + } + std::string id = lua::require_string(L, 1); + auto& gui = engine->getGUI(); + gui.setActiveFrame(id); + return 0; +} + +static int l_get_active_frame(lua::State* L) { + if (engine->isHeadless()) { + return 0; + } + auto& gui = engine->getGUI(); + auto frame = gui.getActiveFrame(); + if (frame == nullptr) { + return 0; + } + return lua::pushstring(L, frame->getId()); +} + const luaL_Reg guilib[] = { {"get_viewport", lua::wrap}, {"getattr", lua::wrap}, @@ -1183,6 +1206,8 @@ const luaL_Reg guilib[] = { {"load_document", lua::wrap}, {"set_syntax_styles", lua::wrap}, {"create_frame", lua::wrap}, + {"set_active_frame", lua::wrap}, + {"get_active_frame", lua::wrap}, {"__reindex", lua::wrap}, {nullptr, nullptr} };