add gui.get_active_frame, set_active_frame & update cursor lock behavior

This commit is contained in:
MihailRis 2026-02-06 14:16:30 +03:00
parent 463098aa1e
commit 8468ab184d
11 changed files with 123 additions and 50 deletions

View file

@ -127,11 +127,11 @@ function on_hud_open()
hud.default_hand_controller = update_hand hud.default_hand_controller = update_hand
local name = "gui/no_icon" local name = "misc/moon"
gui.create_frame(name, {16, 16}) gui.create_frame(name, name, {128, 128})
local document = Document.new(name) local document = Document.new(name)
document.root:add("<label>Hello</label>") document.root:add("<button>Hello, World</button>")
document.root.color = {255, 0, 0, 255} document.root.color = {255, 0, 0, 255}
end end

View file

@ -232,6 +232,7 @@ _GUI_ROOT = Document.new("core:root")
_MENU = _GUI_ROOT.menu _MENU = _GUI_ROOT.menu
menu = _MENU menu = _MENU
gui.root = _GUI_ROOT gui.root = _GUI_ROOT
gui.main_frame_id = "core:main"
do do
local status, err = pcall(function() local status, err = pcall(function()
@ -435,6 +436,7 @@ function __vc_on_hud_open()
if menu.page ~= "" then if menu.page ~= "" then
if not menu:back() then if not menu:back() then
menu:reset() menu:reset()
gui.set_active_frame("")
end end
elseif hud.is_inventory_open() then elseif hud.is_inventory_open() then
hud.close_inventory() hud.close_inventory()

View file

@ -302,10 +302,12 @@ void Engine::startPauseLoop() {
} }
void Engine::renderFrame() { void Engine::renderFrame() {
if (input->isCursorLocked() != (gui->getActiveFrame() == nullptr)) {
input->toggleCursor();
}
screen->draw(time.getDelta()); screen->draw(time.getDelta());
DrawContext ctx(nullptr, *window, nullptr); DrawContext ctx(nullptr, *window, nullptr);
gui->renderFrames(ctx, *assets->getStorage());
gui->draw(ctx, *assets->getStorage()); gui->draw(ctx, *assets->getStorage());
} }

View file

@ -207,9 +207,6 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
} }
Hud::~Hud() { Hud::~Hud() {
if (input.isCursorLocked()) {
input.toggleCursor();
}
// removing all controlled ui // removing all controlled ui
for (auto& element : elements) { for (auto& element : elements) {
onRemove(element); onRemove(element);
@ -342,9 +339,6 @@ void Hud::update(bool visible) {
if (!gui.isFocusCaught()) { if (!gui.isFocusCaught()) {
processInput(visible); processInput(visible);
} }
if ((isMenuOpen || inventoryOpen) == input.isCursorLocked()) {
input.toggleCursor();
}
if (blockUI) { if (blockUI) {
voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z); voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z);
@ -395,6 +389,7 @@ void Hud::openInventory(bool playerInventory) {
inventoryView->bind(inventory, &content); inventoryView->bind(inventory, &content);
add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false)); add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false));
} }
gui.setActiveFrame(GUI::CORE_MAIN);
} }
std::shared_ptr<Inventory> Hud::openInventory( std::shared_ptr<Inventory> Hud::openInventory(
@ -548,6 +543,7 @@ void Hud::closeInventory() {
} }
} }
cleanup(); cleanup();
gui.setActiveFrame("");
} }
void Hud::add(const HudElement& element, const dv::value& argsArray) { void Hud::add(const HudElement& element, const dv::value& argsArray) {
@ -710,9 +706,11 @@ void Hud::setPause(bool pause) {
if (!pause && menu.hasOpenPage()) { if (!pause && menu.hasOpenPage()) {
menu.reset(); menu.reset();
gui.setActiveFrame("");
} }
if (pause && !menu.hasOpenPage()) { if (pause && !menu.hasOpenPage()) {
menu.setPage("pause"); menu.setPage("pause");
gui.setActiveFrame(GUI::CORE_MAIN);
} }
} }

View file

@ -56,6 +56,7 @@ LevelScreen::LevelScreen(
auto& assets = *engine.getAssets(); auto& assets = *engine.getAssets();
auto menu = engine.getGUI().getMenu(); auto menu = engine.getGUI().getMenu();
menu->reset(); menu->reset();
gui.setActiveFrame("");
auto player = level->players->get(localPlayer); auto player = level->players->get(localPlayer);
assert(player != nullptr); assert(player != nullptr);

View file

@ -1,10 +1,10 @@
#include "GUI.hpp" #include "GUI.hpp"
#include <algorithm> #include <algorithm>
#include <iostream>
#include <utility> #include <utility>
#include "assets/Assets.hpp" #include "assets/Assets.hpp"
#include "debug/Logger.hpp"
#include "elements/Label.hpp" #include "elements/Label.hpp"
#include "elements/Menu.hpp" #include "elements/Menu.hpp"
#include "elements/Panel.hpp" #include "elements/Panel.hpp"
@ -27,13 +27,15 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
static debug::Logger logger("gui");
using namespace gui; using namespace gui;
GUI::GUI(Engine& engine) GUI::GUI(Engine& engine)
: engine(engine), : engine(engine),
input(engine.getInput()), input(engine.getInput()),
batch2D(std::make_unique<Batch2D>(1024)), batch2D(std::make_unique<Batch2D>(1024)),
container(std::make_shared<Container>(*this, glm::vec2(1000))) { container(std::make_shared<Frame>(*this, CORE_MAIN, "")) {
container->setId("root"); container->setId("root");
uicamera = uicamera =
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y); std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
@ -64,6 +66,8 @@ GUI::GUI(Engine& engine)
std::dynamic_pointer_cast<gui::UINode>(container), std::dynamic_pointer_cast<gui::UINode>(container),
nullptr nullptr
); );
addFrame(container);
activeFrame = container;
} }
GUI::~GUI() = default; GUI::~GUI() = default;
@ -118,7 +122,9 @@ void GUI::updateTooltip(float delta) {
} }
/// @brief Mouse related input and logic handling /// @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); float mouseDelta = glm::length(cursor.delta);
doubleClicked = false; doubleClicked = false;
doubleClickTimer += delta + mouseDelta * 0.1f; 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(); ) { for (auto it = mouseOver.begin(); it != mouseOver.end(); ) {
auto node = it->lock(); auto mouseOverNode = it->lock();
if (node) { if (mouseOverNode) {
if (node->isInside(cursor.pos)) { if (mouseOverNode->isInside(cursor.pos)) {
++it; ++it;
continue; continue;
} }
node->setMouseOver(false); mouseOverNode->setMouseOver(false);
} }
it = mouseOver.erase(it); it = mouseOver.erase(it);
} }
@ -233,14 +239,16 @@ void GUI::actFocused() {
void GUI::act(float delta, const glm::uvec2& vp) { void GUI::act(float delta, const glm::uvec2& vp) {
container->setSize(vp); container->setSize(vp);
container->act(delta); for (auto& pair : frames) {
pair.second->act(delta);
}
auto prevfocus = focus; auto prevfocus = focus;
updateTooltip(delta); updateTooltip(delta);
const auto& cursor = input.getCursor(); const auto& cursor = input.getCursor();
if (!cursor.locked) { if (!cursor.locked && activeFrame) {
actMouse(delta, cursor); actMouse(*activeFrame, delta, cursor);
} else { } else {
if (hover) { if (hover) {
hover->setMouseEnter(false); hover->setMouseEnter(false);
@ -263,22 +271,7 @@ void GUI::postAct() {
} }
} }
void GUI::renderFrames(const DrawContext& pctx, Assets& assets) { void GUI::draw(const DrawContext& pctx, Assets& assets) {
auto ctx = pctx.sub(batch2D.get());
auto uishader = assets.get<Shader>("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) {
auto ctx = pctx.sub(batch2D.get()); auto ctx = pctx.sub(batch2D.get());
auto& viewport = ctx.getViewport(); auto& viewport = ctx.getViewport();
@ -300,7 +293,10 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
uishader->uniformMatrix("u_projview", uicamera->getProjView()); uishader->uniformMatrix("u_projview", uicamera->getProjView());
batch2D->begin(); batch2D->begin();
container->draw(ctx, assets); for (auto& [outputTexture, frame] : frames) {
frame->updateOutput(assets);
frame->draw(ctx, assets);
}
if (hover) { if (hover) {
engine.getWindow().setCursor(hover->getCursor()); engine.getWindow().setCursor(hover->getCursor());
@ -354,7 +350,26 @@ void GUI::add(std::shared_ptr<UINode> node) {
} }
void GUI::addFrame(std::shared_ptr<Frame> frame) { void GUI::addFrame(std::shared_ptr<Frame> 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::Frame> GUI::getActiveFrame() const {
return activeFrame;
} }
void GUI::remove(UINode* node) noexcept { void GUI::remove(UINode* node) noexcept {

View file

@ -68,7 +68,7 @@ namespace gui {
Engine& engine; Engine& engine;
Input& input; Input& input;
std::unique_ptr<Batch2D> batch2D; std::unique_ptr<Batch2D> batch2D;
std::shared_ptr<Container> container; std::shared_ptr<Frame> container;
std::shared_ptr<UINode> hover; std::shared_ptr<UINode> hover;
std::shared_ptr<UINode> pressed; std::shared_ptr<UINode> pressed;
std::shared_ptr<UINode> focus; std::shared_ptr<UINode> focus;
@ -76,6 +76,7 @@ namespace gui {
std::shared_ptr<UiDocument> rootDocument; std::shared_ptr<UiDocument> rootDocument;
std::unique_ptr<FontStylesScheme> syntaxColorScheme; std::unique_ptr<FontStylesScheme> syntaxColorScheme;
std::unordered_map<std::string, std::shared_ptr<UINode>> storage; std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
std::shared_ptr<Frame> activeFrame;
std::unique_ptr<Camera> uicamera; std::unique_ptr<Camera> uicamera;
std::shared_ptr<Menu> menu; std::shared_ptr<Menu> menu;
@ -89,11 +90,12 @@ namespace gui {
bool doubleClicked = false; bool doubleClicked = false;
bool debug = false; bool debug = false;
void actMouse(float delta, const CursorState& cursor); void actMouse(Frame& frame, float delta, const CursorState& cursor);
void actFocused(); void actFocused();
void updateTooltip(float delta); void updateTooltip(float delta);
void resetTooltip(); void resetTooltip();
public: public:
static inline std::string CORE_MAIN = "core:main";
static constexpr int CONTEXT_MENU_ZINDEX = 999; static constexpr int CONTEXT_MENU_ZINDEX = 999;
GUI(Engine& engine); GUI(Engine& engine);
@ -114,12 +116,10 @@ namespace gui {
/// @param viewport window size /// @param viewport window size
void act(float delta, const glm::uvec2& viewport); void act(float delta, const glm::uvec2& viewport);
void renderFrames(const DrawContext& pctx, Assets& assets);
/// @brief Draw all visible elements on main container /// @brief Draw all visible elements on main container
/// @param pctx parent graphics context /// @param pctx parent graphics context
/// @param assets active assets storage /// @param assets active assets storage
void draw(const DrawContext& pctx, const Assets& assets); void draw(const DrawContext& pctx, Assets& assets);
void postAct(); void postAct();
@ -129,6 +129,10 @@ namespace gui {
void addFrame(std::shared_ptr<Frame> frame); void addFrame(std::shared_ptr<Frame> frame);
void setActiveFrame(const std::string& id);
std::shared_ptr<Frame> getActiveFrame() const;
/// @brief Remove node from the main container /// @brief Remove node from the main container
void remove(UINode* node) noexcept; void remove(UINode* node) noexcept;

View file

@ -13,15 +13,20 @@ using namespace gui;
static inline constexpr int MAX_TEXTURE_SIZE = 2048; 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, {}), : Container(gui, {}),
fbo(nullptr), fbo(nullptr),
outputTexture(std::move(outputTexture)) { outputTexture(std::move(outputTexture)) {
frameId = std::move(id);
} }
gui::Frame::~Frame() = default; gui::Frame::~Frame() = default;
void gui::Frame::draw(const DrawContext& pctx, const Assets& assets) { void gui::Frame::draw(const DrawContext& pctx, const Assets& assets) {
if (outputTexture.empty()) {
Container::draw(pctx, assets);
return;
}
if (fbo == nullptr) { if (fbo == nullptr) {
return; return;
} }
@ -53,3 +58,11 @@ void gui::Frame::updateOutput(Assets& assets) {
const std::string& gui::Frame::getOutputTexture() const { const std::string& gui::Frame::getOutputTexture() const {
return outputTexture; return outputTexture;
} }
const std::string& gui::Frame::getFrameId() const {
return frameId;
}
gui::Frame::State& gui::Frame::getState() {
return state;
}

View file

@ -8,7 +8,10 @@ class UiDocument;
namespace gui { namespace gui {
class Frame final : public Container { class Frame final : public Container {
public: public:
Frame(GUI& gui, std::string outputTexture); struct State {
};
Frame(GUI& gui, std::string id, std::string outputTexture);
virtual ~Frame(); virtual ~Frame();
void draw(const DrawContext& pctx, const Assets& assets) override; void draw(const DrawContext& pctx, const Assets& assets) override;
@ -16,7 +19,14 @@ namespace gui {
void updateOutput(Assets& assets); void updateOutput(Assets& assets);
const std::string& getOutputTexture() const; const std::string& getOutputTexture() const;
const std::string& getFrameId() const;
/// @brief Frame state is controlled in gui::GUI
State& getState();
private: private:
std::string frameId;
State state {};
std::unique_ptr<Framebuffer> fbo; std::unique_ptr<Framebuffer> fbo;
std::string outputTexture; std::string outputTexture;
}; };

View file

@ -78,6 +78,7 @@ void guiutil::alert(
})); }));
menu.addPage("<alert>", panel, true); menu.addPage("<alert>", panel, true);
menu.setPage("<alert>"); menu.setPage("<alert>");
gui.setActiveFrame(GUI::CORE_MAIN);
} }
void guiutil::confirm( void guiutil::confirm(
@ -155,6 +156,7 @@ void guiutil::confirm(
panel->refresh(); panel->refresh();
menu->addPage("<confirm>", container, true); menu->addPage("<confirm>", container, true);
menu->setPage("<confirm>"); menu->setPage("<confirm>");
gui.setActiveFrame(GUI::CORE_MAIN);
} }
void guiutil::confirm_with_memo( void guiutil::confirm_with_memo(
@ -201,4 +203,5 @@ void guiutil::confirm_with_memo(
panel->refresh(); panel->refresh();
menu->addPage("<confirm>", panel, true); menu->addPage("<confirm>", panel, true);
menu->setPage("<confirm>"); menu->setPage("<confirm>");
gui.setActiveFrame(GUI::CORE_MAIN);
} }

View file

@ -1154,21 +1154,44 @@ static int l_create_frame(lua::State* L) {
if (engine->isHeadless()) { if (engine->isHeadless()) {
return 0; return 0;
} }
std::string outputTexture = lua::require_string(L, 1); std::string id = lua::require_string(L, 1);
auto size = lua::tovec2(L, 2); std::string outputTexture = lua::require_string(L, 2);
auto size = lua::tovec2(L, 3);
auto& gui = engine->getGUI(); auto& gui = engine->getGUI();
auto frame = std::make_shared<gui::Frame>(gui, outputTexture); auto frame = std::make_shared<gui::Frame>(gui, id, outputTexture);
frame->setSize(std::move(size)); frame->setSize(std::move(size));
auto& assets = *engine->getAssets(); auto& assets = *engine->getAssets();
auto document = std::make_shared<UiDocument>( auto document = std::make_shared<UiDocument>(
outputTexture, UiDocScript {}, frame, nullptr id, UiDocScript {}, frame, nullptr
); );
assets.store(document, document->getId()); assets.store(document, id);
gui.addFrame(std::move(frame)); gui.addFrame(std::move(frame));
return 0; 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[] = { const luaL_Reg guilib[] = {
{"get_viewport", lua::wrap<l_gui_getviewport>}, {"get_viewport", lua::wrap<l_gui_getviewport>},
{"getattr", lua::wrap<l_gui_getattr>}, {"getattr", lua::wrap<l_gui_getattr>},
@ -1183,6 +1206,8 @@ const luaL_Reg guilib[] = {
{"load_document", lua::wrap<l_gui_load_document>}, {"load_document", lua::wrap<l_gui_load_document>},
{"set_syntax_styles", lua::wrap<l_set_syntax_styles>}, {"set_syntax_styles", lua::wrap<l_set_syntax_styles>},
{"create_frame", lua::wrap<l_create_frame>}, {"create_frame", lua::wrap<l_create_frame>},
{"set_active_frame", lua::wrap<l_set_active_frame>},
{"get_active_frame", lua::wrap<l_get_active_frame>},
{"__reindex", lua::wrap<l_gui_reindex>}, {"__reindex", lua::wrap<l_gui_reindex>},
{nullptr, nullptr} {nullptr, nullptr}
}; };