feat: key pressed event interception & 'owner' input.add_callback third argument

This commit is contained in:
MihailRis 2025-01-02 20:51:43 +03:00
parent 7fce735ca1
commit b9ff1db086
17 changed files with 192 additions and 63 deletions

View File

@ -201,6 +201,17 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
"' pos='0' size='256' gravity='top-right' margin='0,20,0,0'/>"
);
add(HudElement(hud_element_mode::permanent, nullptr, debugMinimap, true));
keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([this]() -> bool {
if (pause) {
setPause(false);
} else if (inventoryOpen) {
closeInventory();
} else {
setPause(true);
}
return false;
}));
}
Hud::~Hud() {
@ -223,15 +234,6 @@ void Hud::cleanup() {
}
void Hud::processInput(bool visible) {
if (Events::jpressed(keycode::ESCAPE)) {
if (pause) {
setPause(false);
} else if (inventoryOpen) {
closeInventory();
} else {
setPause(true);
}
}
if (!Window::isFocused() && !pause && !isInventoryOpen()) {
setPause(true);
}

View File

@ -83,6 +83,7 @@ LevelScreen::LevelScreen(Engine& engine, std::unique_ptr<Level> levelPtr)
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
player->chunks->saveAndClear();
worldRenderer->clear();
return false;
}));
animator = std::make_unique<TextureAnimator>();

View File

@ -2,11 +2,12 @@
#include "UINode.hpp"
#include "commons.hpp"
#include "util/ObjectsKeeper.hpp"
#include <vector>
namespace gui {
class Container : public UINode {
class Container : public UINode, public util::ObjectsKeeper {
int prevScrollY = -1;
protected:
std::vector<std::shared_ptr<UINode>> nodes;

View File

@ -13,11 +13,15 @@ bool Menu::has(const std::string& name) {
pageSuppliers.find(name) != pageSuppliers.end();
}
void Menu::addPage(const std::string& name, const std::shared_ptr<UINode> &panel) {
pages[name] = Page{name, panel};
void Menu::addPage(const std::string& name, const std::shared_ptr<UINode>& panel) {
pages[name] = Page {name, panel};
}
void Menu::addSupplier(const std::string &name, const supplier<std::shared_ptr<UINode>> &pageSupplier) {
void Menu::removePage(const std::string& name) {
pages.erase(name);
}
void Menu::addSupplier(const std::string& name, const supplier<std::shared_ptr<UINode>>& pageSupplier) {
pageSuppliers[name] = pageSupplier;
}
@ -89,6 +93,6 @@ void Menu::reset() {
clearHistory();
if (current.panel) {
Container::remove(current.panel);
current = Page{"", nullptr};
current = Page {"", nullptr};
}
}

View File

@ -32,6 +32,7 @@ namespace gui {
void setPage(const std::string &name, bool history=true);
void setPage(Page page, bool history=true);
void addPage(const std::string& name, const std::shared_ptr<UINode>& panel);
void removePage(const std::string& name);
std::shared_ptr<UINode> fetchPage(const std::string& name);
/// @brief Add page supplier used if page is not found

View File

@ -11,6 +11,9 @@
#include "util/stringutil.hpp"
#include "delegates.hpp"
#include "window/Events.hpp"
#include "engine/Engine.hpp"
#include <glm/glm.hpp>
using namespace gui;
@ -50,7 +53,7 @@ void guiutil::alert(
}
void guiutil::confirm(
const std::shared_ptr<gui::Menu>& menu,
Engine& engine,
const std::wstring& text,
const runnable& on_confirm,
const runnable& on_deny,
@ -66,19 +69,45 @@ void guiutil::confirm(
auto subpanel = std::make_shared<Panel>(glm::vec2(600, 53));
subpanel->setColor(glm::vec4(0));
subpanel->add(std::make_shared<Button>(yestext, glm::vec4(8.f), [=](GUI*){
if (on_confirm)
auto menu = engine.getGUI()->getMenu();
runnable on_confirm_final = [on_confirm, menu, &engine]() {
if (on_confirm) {
on_confirm();
}
menu->back();
engine.postRunnable([menu]() {
menu->removePage("<confirm>");
});
};
runnable on_deny_final = [on_deny, menu, &engine]() {
if (on_deny) {
on_deny();
}
menu->back();
engine.postRunnable([menu]() {
menu->removePage("<confirm>");
});
};
subpanel->add(std::make_shared<Button>(yestext, glm::vec4(8.f), [=](GUI*){
on_confirm_final();
}));
subpanel->add(std::make_shared<Button>(notext, glm::vec4(8.f), [=](GUI*){
if (on_deny)
on_deny();
menu->back();
on_deny_final();
}));
panel->add(subpanel);
panel->keepAlive(Events::keyCallbacks[keycode::ENTER].add([=](){
on_confirm_final();
return true;
}));
panel->keepAlive(Events::keyCallbacks[keycode::ESCAPE].add([=](){
on_deny_final();
return true;
}));
panel->refresh();
menu->addPage("<confirm>", panel);

View File

@ -7,6 +7,8 @@
#include <memory>
#include <string>
class Engine;
namespace guiutil {
/// @brief Create element from XML
/// @param source XML
@ -19,7 +21,7 @@ namespace guiutil {
);
void confirm(
const std::shared_ptr<gui::Menu>& menu,
Engine& engine,
const std::wstring& text,
const runnable& on_confirm=nullptr,
const runnable& on_deny=nullptr,

View File

@ -44,7 +44,7 @@ void EngineController::deleteWorld(const std::string& name) {
return;
}
guiutil::confirm(
engine.getGUI()->getMenu(),
engine,
langs::get(L"delete-confirm", L"world") + L" (" +
util::str2wstr_utf8(folder.u8string()) + L")",
deletion
@ -119,7 +119,7 @@ static void show_convert_request(
return;
}
guiutil::confirm(
engine.getGUI()->getMenu(),
engine,
langs::get(message),
on_confirm,
nullptr,
@ -358,7 +358,7 @@ void EngineController::reconfigPacks(
if (hasIndices && !engine.isHeadless()) {
guiutil::confirm(
engine.getGUI()->getMenu(),
engine,
langs::get(L"remove-confirm", L"pack") + L" (" +
util::str2wstr_utf8(ss.str()) + L")",
[=]() { removeFunc(); }

View File

@ -1,6 +1,6 @@
#include "libgui.hpp"
#include "assets/Assets.hpp"
#include "engine/Engine.hpp"
#include "frontend/UiDocument.hpp"
#include "frontend/locale.hpp"
#include "graphics/ui/elements/Button.hpp"
#include "graphics/ui/elements/CheckBox.hpp"
@ -10,24 +10,17 @@
#include "graphics/ui/elements/Panel.hpp"
#include "graphics/ui/elements/TextBox.hpp"
#include "graphics/ui/elements/TrackBar.hpp"
#include "graphics/ui/elements/UINode.hpp"
#include "graphics/ui/gui_util.hpp"
#include "graphics/ui/markdown.hpp"
#include "graphics/core/Font.hpp"
#include "items/Inventories.hpp"
#include "util/stringutil.hpp"
#include "world/Level.hpp"
#include "api_lua.hpp"
using namespace gui;
using namespace scripting;
struct DocumentNode {
UiDocument* document;
std::shared_ptr<UINode> node;
};
static DocumentNode getDocumentNode(
static DocumentNode get_document_node_impl(
lua::State*, const std::string& name, const std::string& nodeName
) {
auto doc = engine->getAssets()->get<UiDocument>(name);
@ -43,18 +36,18 @@ static DocumentNode getDocumentNode(
return {doc, node};
}
static DocumentNode getDocumentNode(lua::State* L, int idx = 1) {
DocumentNode get_document_node(lua::State* L, int idx) {
lua::getfield(L, "docname", idx);
lua::getfield(L, "name", idx);
auto docname = lua::require_string(L, -2);
auto name = lua::require_string(L, -1);
auto node = getDocumentNode(L, docname, name);
auto node = get_document_node_impl(L, docname, name);
lua::pop(L, 2);
return node;
}
static int l_menu_back(lua::State* L) {
auto node = getDocumentNode(L);
auto node = get_document_node(L);
if (auto menu = dynamic_cast<Menu*>(node.node.get())) {
menu->back();
}
@ -62,7 +55,7 @@ static int l_menu_back(lua::State* L) {
}
static int l_menu_reset(lua::State* L) {
auto node = getDocumentNode(L);
auto node = get_document_node(L);
if (auto menu = dynamic_cast<Menu*>(node.node.get())) {
menu->reset();
}
@ -70,7 +63,7 @@ static int l_menu_reset(lua::State* L) {
}
static int l_textbox_paste(lua::State* L) {
auto node = getDocumentNode(L);
auto node = get_document_node(L);
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
auto text = lua::require_string(L, 2);
box->paste(util::str2wstr_utf8(text));
@ -79,7 +72,7 @@ static int l_textbox_paste(lua::State* L) {
}
static int l_container_add(lua::State* L) {
auto docnode = getDocumentNode(L);
auto docnode = get_document_node(L);
auto node = dynamic_cast<Container*>(docnode.node.get());
if (node == nullptr) {
return 0;
@ -97,7 +90,7 @@ static int l_container_add(lua::State* L) {
}
static int l_node_destruct(lua::State* L) {
auto docnode = getDocumentNode(L);
auto docnode = get_document_node(L);
auto node = docnode.node;
engine->getGUI()->postRunnable([node]() {
auto parent = node->getParent();
@ -109,7 +102,7 @@ static int l_node_destruct(lua::State* L) {
}
static int l_container_clear(lua::State* L) {
auto node = getDocumentNode(L, 1);
auto node = get_document_node(L, 1);
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
container->clear();
}
@ -117,7 +110,7 @@ static int l_container_clear(lua::State* L) {
}
static int l_container_set_interval(lua::State* L) {
auto node = getDocumentNode(L, 1);
auto node = get_document_node(L, 1);
auto interval = lua::tointeger(L, 2) / 1000.0f;
if (auto container = std::dynamic_pointer_cast<Container>(node.node)) {
lua::pushvalue(L, 3);
@ -128,8 +121,8 @@ static int l_container_set_interval(lua::State* L) {
}
static int l_move_into(lua::State* L) {
auto node = getDocumentNode(L, 1);
auto dest = getDocumentNode(L, 2);
auto node = get_document_node(L, 1);
auto dest = get_document_node(L, 2);
UINode::moveInto(
node.node, std::dynamic_pointer_cast<Container>(dest.node)
);
@ -137,7 +130,7 @@ static int l_move_into(lua::State* L) {
}
static int l_get_line_at(lua::State* L) {
auto node = getDocumentNode(L, 1);
auto node = get_document_node(L, 1);
auto position = lua::tointeger(L, 2);
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
return lua::pushinteger(L, box->getLineAt(position));
@ -146,7 +139,7 @@ static int l_get_line_at(lua::State* L) {
}
static int l_get_line_pos(lua::State* L) {
auto node = getDocumentNode(L, 1);
auto node = get_document_node(L, 1);
auto line = lua::tointeger(L, 2);
if (auto box = dynamic_cast<TextBox*>(node.node.get())) {
return lua::pushinteger(L, box->getLinePos(line));
@ -463,7 +456,7 @@ static int l_gui_getattr(lua::State* L) {
};
auto func = getters.find(attr);
if (func != getters.end()) {
auto docnode = getDocumentNode(L, docname, element);
auto docnode = get_document_node_impl(L, docname, element);
auto node = docnode.node;
return func->second(node.get(), L);
}
@ -632,7 +625,7 @@ static int l_gui_setattr(lua::State* L) {
auto element = lua::require_string(L, 2);
auto attr = lua::require_string(L, 3);
auto docnode = getDocumentNode(L, docname, element);
auto docnode = get_document_node_impl(L, docname, element);
auto node = docnode.node;
static const std::unordered_map<
@ -777,7 +770,7 @@ static int l_gui_confirm(lua::State* L) {
notext = lua::require_wstring(L, 5);
}
guiutil::confirm(
engine->getGUI()->getMenu(),
*engine,
question,
onconfirm,
ondeny,

View File

@ -0,0 +1,14 @@
#pragma once
#include <memory>
#include "api_lua.hpp"
#include "frontend/UiDocument.hpp"
#include "graphics/ui/elements/UINode.hpp"
struct DocumentNode {
UiDocument* document;
std::shared_ptr<gui::UINode> node;
};
DocumentNode get_document_node(lua::State* L, int idx = 1);

View File

@ -5,10 +5,11 @@
#include "frontend/hud.hpp"
#include "frontend/screens/Screen.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Container.hpp"
#include "util/stringutil.hpp"
#include "window/Events.hpp"
#include "window/input.hpp"
#include "api_lua.hpp"
#include "libgui.hpp"
namespace scripting {
extern Hud* hud;
@ -39,7 +40,7 @@ static int l_add_callback(lua::State* L) {
throw std::runtime_error("on_hud_open is not called yet");
}
auto key = input_util::keycode_from(bindname.substr(pos + 1));
auto callback = lua::create_runnable(L);
auto callback = lua::create_simple_handler(L);
hud->keepAlive(Events::keyCallbacks[key].add(callback));
return 0;
}
@ -50,18 +51,25 @@ static int l_add_callback(lua::State* L) {
throw std::runtime_error("unknown binding " + util::quote(bindname));
}
lua::pushvalue(L, 2);
runnable actual_callback = lua::create_runnable(L);
runnable callback = [=]() {
auto actual_callback = lua::create_simple_handler(L);
auto callback = [=]() -> bool {
if (!scripting::engine->getGUI()->isFocusCaught()) {
actual_callback();
return actual_callback();
}
return false;
};
if (hud) {
hud->keepAlive(bind->second.onactived.add(callback));
} else {
throw std::runtime_error("on_hud_open is not called yet");
return 0;
} else if (lua::gettop(L) >= 3) {
auto node = get_document_node(L, 3);
if (auto container = std::dynamic_pointer_cast<gui::Container>(node.node)) {
container->keepAlive(bind->second.onactived.add(callback));
return 0;
}
throw std::runtime_error("owner expected to be a container");
}
return 0;
throw std::runtime_error("on_hud_open is not called yet");
}
static int l_get_mouse_pos(lua::State* L) {

View File

@ -246,6 +246,22 @@ runnable lua::create_runnable(State* L) {
};
}
supplier<bool> lua::create_simple_handler(State* L) {
auto funcptr = create_lambda_handler(L);
return [=]() -> bool {
getglobal(L, LAMBDAS_TABLE);
getfield(L, *funcptr);
if (call_nothrow(L, 0)) {
bool result = toboolean(L, -1);
pop(L, 2);
return result;
} else {
pop(L);
return false;
}
};
}
scripting::common_func lua::create_lambda(State* L) {
auto funcptr = create_lambda_handler(L);
return [=](const std::vector<dv::value>& args) -> dv::value {

View File

@ -574,6 +574,7 @@ namespace lua {
}
runnable create_runnable(lua::State*);
supplier<bool> create_simple_handler(lua::State*);
scripting::common_func create_lambda(lua::State*);
scripting::common_func create_lambda_nothrow(lua::State*);

55
src/util/HandlersList.hpp Normal file
View File

@ -0,0 +1,55 @@
#pragma once
#include <mutex>
#include <iostream>
#include "typedefs.hpp"
#include "delegates.hpp"
namespace util {
template<class...Types>
class HandlersList {
int nextid = 1;
std::unordered_map<int, std::function<bool(Types...)>> handlers;
std::vector<int> order;
std::mutex mutex;
public:
HandlersList() = default;
HandlersList(HandlersList&& o) {
handlers = std::move(o.handlers);
order = std::move(o.order);
nextid = o.nextid;
}
void operator=(HandlersList&& o) {
handlers = std::move(o.handlers);
order = std::move(o.order);
nextid = o.nextid;
}
observer_handler add(std::function<bool(Types...)> handler) {
std::lock_guard lock(mutex);
int id = nextid++;
handlers[id] = std::move(handler);
order.push_back(id);
return observer_handler(new int(id), [this](int* id) { //-V508
std::lock_guard lock(mutex);
handlers.erase(*id);
order.erase(
std::remove(order.begin(), order.end(), *id), order.end()
);
delete id;
});
}
void notify(Types...args) {
std::lock_guard lock(mutex);
for (auto it = order.rbegin(); it != order.rend(); ++it) {
if (handlers.at(*it)(args...)) {
break;
}
}
}
};
}

View File

@ -23,7 +23,7 @@ bool Events::_cursor_locked = false;
std::vector<uint> Events::codepoints;
std::vector<keycode> Events::pressedKeys;
std::unordered_map<std::string, Binding> Events::bindings;
std::unordered_map<keycode, util::RunnablesList> Events::keyCallbacks;
std::unordered_map<keycode, util::HandlersList<>> Events::keyCallbacks;
bool Events::pressed(keycode keycode) {
return pressed(static_cast<int>(keycode));
@ -180,7 +180,7 @@ void Events::setPosition(float xpos, float ypos) {
Events::cursor.y = ypos;
}
observer_handler Events::addKeyCallback(keycode key, runnable callback) {
observer_handler Events::addKeyCallback(keycode key, KeyCallback callback) {
return keyCallbacks[key].add(std::move(callback));
}

View File

@ -20,6 +20,8 @@ class Events {
static uint currentFrame;
static bool cursor_drag;
public:
using KeyCallback = std::function<bool()>;
static int scroll;
static glm::vec2 delta;
static glm::vec2 cursor;
@ -27,7 +29,7 @@ public:
static std::vector<uint> codepoints;
static std::vector<keycode> pressedKeys;
static std::unordered_map<std::string, Binding> bindings;
static std::unordered_map<keycode, util::RunnablesList> keyCallbacks;
static std::unordered_map<keycode, util::HandlersList<>> keyCallbacks;
static void pollEvents();
@ -51,7 +53,7 @@ public:
static bool active(const std::string& name);
static bool jactive(const std::string& name);
static observer_handler addKeyCallback(keycode key, runnable callback);
static observer_handler addKeyCallback(keycode key, KeyCallback callback);
static void setKey(int key, bool b);
static void setButton(int button, bool b);

View File

@ -2,7 +2,7 @@
#include <string>
#include "util/RunnablesList.hpp"
#include "util/HandlersList.hpp"
/// @brief Represents glfw3 keycode values.
enum class keycode : int {
@ -131,7 +131,7 @@ enum class inputtype {
};
struct Binding {
util::RunnablesList onactived;
util::HandlersList<> onactived;
inputtype type;
int code;