diff --git a/doc/en/scripting/builtins/libfile.md b/doc/en/scripting/builtins/libfile.md index 9d9532d8..9f1754d7 100644 --- a/doc/en/scripting/builtins/libfile.md +++ b/doc/en/scripting/builtins/libfile.md @@ -125,10 +125,22 @@ Combines objects from JSON files of different packs. file.name(path: str) --> str ``` -Extracts the file name from the path. +Extracts the file name from the path. Example: `world:data/base/config.toml` -> `config.toml`. ``lua file.stem(path: str) --> str ``` -Extracts the file name from the path, removing the extension. +Extracts the file name from the path, removing the extension. Example: `world:data/base/config.toml` -> `config`. + +```lua +file.ext(path: str) --> str +``` + +Extracts the extension from the path. Example: `world:data/base/config.toml` -> `toml`. + +```lua +file.prefix(path: str) --> str +``` + +Extracts the entry point (prefix) from the path. Example: `world:data/base/config.toml` -> `world`. diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md index c49ff478..64b5005b 100644 --- a/doc/en/scripting/builtins/libgui.md +++ b/doc/en/scripting/builtins/libgui.md @@ -79,3 +79,16 @@ gui.confirm( ``` Requests confirmation from the user for an action. **Does not** stop code execution. + +```lua +gui.load_document( + -- Path to the xml file of the page. Example: `core:layouts/pages/main.xml` + path: str, + -- Name (id) of the document. Example: `core:pages/main` + name: str + -- Table of parameters passed to the on_open event + args: table +) --> str +``` + +Loads a UI document with its script, returns the name of the document if successfully loaded. diff --git a/doc/ru/scripting/builtins/libfile.md b/doc/ru/scripting/builtins/libfile.md index 3a93e20d..45ca462c 100644 --- a/doc/ru/scripting/builtins/libfile.md +++ b/doc/ru/scripting/builtins/libfile.md @@ -125,10 +125,22 @@ file.read_combined_object(путь: str) -> массив file.name(путь: str) --> str ``` -Извлекает имя файла из пути. +Извлекает имя файла из пути. Пример: `world:data/base/config.toml` -> `config.toml`. ```lua file.stem(путь: str) --> str ``` -Извлекает имя файла из пути, удаляя расширение. +Извлекает имя файла из пути, удаляя расширение. Пример: `world:data/base/config.toml` -> `config`. + +```lua +file.ext(путь: str) --> str +``` + +Извлекает расширение из пути. Пример: `world:data/base/config.toml` -> `toml`. + +```lua +file.prefix(путь: str) --> str +``` + +Извлекает точку входа (префикс) из пути. Пример: `world:data/base/config.toml` -> `world`. diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md index 22b22981..66e67809 100644 --- a/doc/ru/scripting/builtins/libgui.md +++ b/doc/ru/scripting/builtins/libgui.md @@ -76,3 +76,16 @@ gui.confirm( ``` Запрашивает у пользователя подтверждение действия. **Не** останавливает выполнение кода. + +```lua +gui.load_document( + -- Путь к xml файлу страницы. Пример: `core:layouts/pages/main.xml` + path: str, + -- Имя (id) документа. Пример: `core:pages/main` + name: str + -- Таблица параметров, передаваемых в событие on_open + args: table +) --> str +``` + +Загружает UI документ с его скриптом, возвращает имя документа, если успешно загружен. diff --git a/res/modules/gui_util.lua b/res/modules/gui_util.lua new file mode 100644 index 00000000..0d371eae --- /dev/null +++ b/res/modules/gui_util.lua @@ -0,0 +1,38 @@ +local gui_util = {} + +--- Parse `pagename?arg1=value1&arg2=value2` queries +--- @param query page query string +--- @return page_name, args_table +function gui_util.parse_query(query) + local args = {} + local name + + local index = string.find(query, '?') + if index then + local argstr = string.sub(query, index + 1) + name = string.sub(query, 1, index - 1) + + local map = {} + for key, value in string.gmatch(argstr, "([^=&]*)=([^&]*)") do + map[key] = value + end + table.insert(args, map) + else + name = query + end + return name, args +end + +--- @param query page query string +--- @return document_id +function gui_util.load_page(query) + local name, args = gui_util.parse_query(query) + local filename = file.find(string.format("layouts/pages/%s.xml", name)) + if filename then + name = file.prefix(filename)..":pages/"..name + gui.load_document(filename, name, args) + return name + end +end + +return gui_util diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index b0c614ad..1b0a8a42 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -192,6 +192,9 @@ _GUI_ROOT = Document.new("core:root") _MENU = _GUI_ROOT.menu menu = _MENU +local gui_util = require "core:gui_util" +__vc_page_loader = gui_util.load_page + --- Console library extension --- console.cheats = {} diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 769b74a3..5da1b347 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -374,3 +374,7 @@ end function file.ext(path) return path:match("%.([^:/\\]+)$") end + +function file.prefix(path) + return path:match("^([^:]+)") +end diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index fdcf2e0a..10c9f6df 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -29,6 +29,7 @@ #include "logic/EngineController.hpp" #include "logic/CommandsInterpreter.hpp" #include "logic/scripting/scripting.hpp" +#include "logic/scripting/scripting_hud.hpp" #include "network/Network.hpp" #include "util/listutil.hpp" #include "util/platform.hpp" @@ -123,11 +124,10 @@ Engine::Engine(CoreParameters coreParameters) paths.getResourcesFolder() )); } + scripting::initialize(this); keepAlive(settings.ui.language.observe([this](auto lang) { setLanguage(lang); }, true)); - - scripting::initialize(this); basePacks = files::read_list(resdir/fs::path("config/builtins.list")); } @@ -442,7 +442,7 @@ void Engine::setScreen(std::shared_ptr screen) { void Engine::setLanguage(std::string locale) { langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); if (gui) { - gui->getMenu()->setPageLoader(menus::create_page_loader(*this)); + gui->getMenu()->setPageLoader(scripting::create_page_loader()); } } diff --git a/src/frontend/menu.cpp b/src/frontend/menu.cpp index 12283bea..4a36410c 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -34,46 +34,6 @@ void menus::create_version_label(Engine& engine) { )); } -gui::page_loader_func menus::create_page_loader(Engine& engine) { - return [&](const std::string& query) { - std::vector args; - - std::string name; - size_t index = query.find('?'); - if (index != std::string::npos) { - auto argstr = query.substr(index+1); - name = query.substr(0, index); - - auto map = dv::object(); - auto filename = "query for "+name; - BasicParser parser(filename, argstr); - while (parser.hasNext()) { - auto key = std::string(parser.readUntil('=')); - parser.nextChar(); - auto value = std::string(parser.readUntil('&')); - map[key] = value; - } - args.emplace_back(map); - } else { - name = query; - } - - auto file = engine.getResPaths()->find("layouts/pages/"+name+".xml"); - auto fullname = "core:pages/"+name; - - auto documentPtr = UiDocument::read( - scripting::get_root_environment(), - fullname, - file, - "core:layouts/pages/" + name - ); - auto document = documentPtr.get(); - engine.getAssets()->store(std::move(documentPtr), fullname); - scripting::on_ui_open(document, std::move(args)); - return document->getRoot(); - }; -} - bool menus::call(Engine& engine, runnable func) { if (engine.isHeadless()) { throw std::runtime_error("menus::call(...) in headless mode"); diff --git a/src/frontend/menu.hpp b/src/frontend/menu.hpp index 2f7eceb2..3ae0cd49 100644 --- a/src/frontend/menu.hpp +++ b/src/frontend/menu.hpp @@ -16,8 +16,6 @@ namespace menus { /// @brief Create development version label at the top-right screen corner void create_version_label(Engine& engine); - gui::page_loader_func create_page_loader(Engine& engine); - UiDocument* show( Engine& engine, const std::string& name, diff --git a/src/graphics/ui/elements/Menu.cpp b/src/graphics/ui/elements/Menu.cpp index 898f8797..1756baec 100644 --- a/src/graphics/ui/elements/Menu.cpp +++ b/src/graphics/ui/elements/Menu.cpp @@ -79,10 +79,14 @@ void Menu::back() { setPage(page, false); } -void Menu::setPageLoader(page_loader_func loader) { +void Menu::setPageLoader(PageLoaderFunc loader) { pagesLoader = std::move(loader); } +PageLoaderFunc Menu::getPageLoader() { + return pagesLoader; +} + Page& Menu::getCurrent() { return current; } diff --git a/src/graphics/ui/elements/Menu.hpp b/src/graphics/ui/elements/Menu.hpp index b9cb213b..a6550907 100644 --- a/src/graphics/ui/elements/Menu.hpp +++ b/src/graphics/ui/elements/Menu.hpp @@ -11,7 +11,7 @@ namespace gui { bool temporal = false; }; - using page_loader_func = std::function(const std::string& name)>; + using PageLoaderFunc = std::function(const std::string&)>; class Menu : public Container { protected: @@ -19,7 +19,7 @@ namespace gui { std::stack pageStack; Page current; std::unordered_map>> pageSuppliers; - page_loader_func pagesLoader = nullptr; + PageLoaderFunc pagesLoader = nullptr; public: Menu(); @@ -49,7 +49,9 @@ namespace gui { ); /// @brief Page loader is called if accessed page is not found - void setPageLoader(page_loader_func loader); + void setPageLoader(PageLoaderFunc loader); + + PageLoaderFunc getPageLoader(); /// @brief Set page to previous saved in history void back(); diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 2d6daa96..c705d16d 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -9,6 +9,8 @@ #include "elements/TrackBar.hpp" #include "elements/InputBindBox.hpp" #include "elements/InventoryView.hpp" +#include "GUI.hpp" +#include "engine/Engine.hpp" #include "frontend/menu.hpp" #include "frontend/locale.hpp" @@ -616,8 +618,8 @@ static std::shared_ptr readInventory(UiXmlReader& reader, const xml::xml static std::shared_ptr readPageBox(UiXmlReader& reader, const xml::xmlelement& element) { auto menu = std::make_shared(); - // fixme - menu->setPageLoader(menus::create_page_loader(*scripting::engine)); + // FIXME + menu->setPageLoader(scripting::engine->getGUI()->getMenu()->getPageLoader()); _readContainer(reader, element, *menu); return menu; diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index bed74cf0..d2b566f5 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -787,6 +787,23 @@ static int l_gui_alert(lua::State* L) { return 0; } +static int l_gui_load_document(lua::State* L) { + auto filename = lua::require_string(L, 1); + auto alias = lua::require_string(L, 2); + auto args = lua::tovalue(L, 3); + + auto documentPtr = UiDocument::read( + scripting::get_root_environment(), + alias, + engine->getPaths().resolve(fs::u8path(filename)), + filename + ); + auto document = documentPtr.get(); + engine->getAssets()->store(std::move(documentPtr), alias); + scripting::on_ui_open(document, {args}); + return 0; +} + const luaL_Reg guilib[] = { {"get_viewport", lua::wrap}, {"getattr", lua::wrap}, @@ -798,6 +815,7 @@ const luaL_Reg guilib[] = { {"escape_markup", lua::wrap}, {"confirm", lua::wrap}, {"alert", lua::wrap}, + {"load_document", lua::wrap}, {"__reindex", lua::wrap}, {NULL, NULL} }; diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index 75c063a4..c13a0010 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -4,6 +4,7 @@ #include "engine/Engine.hpp" #include "files/files.hpp" #include "frontend/hud.hpp" +#include "frontend/UiDocument.hpp" #include "graphics/render/WorldRenderer.hpp" #include "objects/Player.hpp" #include "lua/libs/api_lua.hpp" @@ -93,3 +94,15 @@ void scripting::load_hud_script( register_event(env, "on_hud_render", packid + ":.hudrender"); register_event(env, "on_hud_close", packid + ":.hudclose"); } + +gui::PageLoaderFunc scripting::create_page_loader() { + auto L = lua::get_main_state(); + if (lua::getglobal(L, "__vc_page_loader")) { + auto func = lua::create_lambda(L); + return [func](const std::string& name) -> std::shared_ptr { + auto docname = func({name}).asString(); + return engine->getAssets()->require(docname).getRoot(); + }; + } + return nullptr; +} diff --git a/src/logic/scripting/scripting_hud.hpp b/src/logic/scripting/scripting_hud.hpp index 7eed738c..f6829976 100644 --- a/src/logic/scripting/scripting_hud.hpp +++ b/src/logic/scripting/scripting_hud.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include "typedefs.hpp" @@ -10,6 +12,11 @@ namespace fs = std::filesystem; class Hud; class WorldRenderer; +namespace gui { + class UINode; + using PageLoaderFunc = std::function(const std::string&)>; +} + namespace scripting { extern Hud *hud; extern WorldRenderer* renderer; @@ -29,4 +36,6 @@ namespace scripting { const fs::path& file, const std::string& fileName ); + + gui::PageLoaderFunc create_page_loader(); }