From 3e493a4a97e01244b3766e4ed5823cd576a60d85 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 16 Apr 2025 20:58:57 +0300 Subject: [PATCH 1/4] add uinode.parent read-only property --- res/modules/internal/gui_util.lua | 1 + res/scripts/stdlib.lua | 5 +++++ src/logic/scripting/lua/libs/libgui.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua index 294a7c79..c44e2fbe 100644 --- a/res/modules/internal/gui_util.lua +++ b/res/modules/internal/gui_util.lua @@ -102,6 +102,7 @@ end setmetatable(RadioGroup, RadioGroup) gui_util.Document = Document +gui_util.Element = Element gui_util.RadioGroup = RadioGroup return gui_util diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 5fa9ca29..a0f18e6d 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -186,9 +186,14 @@ end gui_util = require "core:internal/gui_util" Document = gui_util.Document +Element = gui_util.Element RadioGroup = gui_util.RadioGroup __vc_page_loader = gui_util.load_page +function __vc_get_document_node(docname, nodeid) + return Element.new(docname, nodeid) +end + _GUI_ROOT = Document.new("core:root") _MENU = _GUI_ROOT.menu menu = _MENU diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 2df7f322..a70e7136 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -341,6 +341,29 @@ static int p_get_data(UINode* node, lua::State* L) { return 0; } +static int p_get_parent(UINode* node, lua::State* L) { + auto parent = node->getParent(); + if (!parent) { + return 0; + } + auto id = parent->getId(); + if (id.empty()) { + id = "#" + std::to_string(reinterpret_cast(parent)); + } + parent->setId(id); + + auto docname = lua::require_string(L, 1); + auto element = lua::require_string(L, 2); + auto docnode = get_document_node_impl(L, docname, element); + UINode::getIndices( + parent->shared_from_this(), docnode.document->getMapWriteable() + ); + lua::requireglobal(L, "__vc_get_document_node"); + lua::pushvalue(L, 1); + lua::pushstring(L, id); + return lua::call(L, 2, 1); +} + static int p_get_add(UINode* node, lua::State* L) { if (dynamic_cast(node)) { return lua::pushcfunction(L, lua::wrap); @@ -492,6 +515,7 @@ static int l_gui_getattr(lua::State* L) { {"focused", p_get_focused}, {"cursor", p_get_cursor}, {"data", p_get_data}, + {"parent", p_get_parent}, }; auto func = getters.find(attr); if (func != getters.end()) { From 38e07533825aa24f9e066b37b1070b9e3a795732 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 17 Apr 2025 19:54:58 +0300 Subject: [PATCH 2/4] add __ipairs metamethod support if missing in LuaJIT --- res/scripts/stdmin.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index 06c86832..634d4c39 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -3,6 +3,23 @@ local canvas_ffi_buffer local canvas_ffi_buffer_size = 0 +local ipairs_mt_supported = false +for i, _ in ipairs(setmetatable({l={1}}, { + __ipairs=function(self) return ipairs(self.l) end})) do + ipairs_mt_supported = true +end + +if not ipairs_mt_supported then + local raw_ipairs = ipairs + ipairs = function(t) + local metatable = getmetatable(t) + if metatable and metatable.__ipairs then + return metatable.__ipairs(t) + end + return raw_ipairs(t) + end +end + function __vc_Canvas_set_data(self, data) if type(data) == "cdata" then self:_set_data(tostring(ffi.cast("uintptr_t", data))) From 559f946a662d3cb579b1c95937c3e73851f8f898 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 17 Apr 2025 19:55:43 +0300 Subject: [PATCH 3/4] feat: ui sub-nodes access --- res/modules/internal/gui_util.lua | 11 +++++ src/logic/scripting/lua/libs/libgui.cpp | 55 +++++++++++++++++++------ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua index c44e2fbe..15360521 100644 --- a/res/modules/internal/gui_util.lua +++ b/res/modules/internal/gui_util.lua @@ -60,6 +60,17 @@ function Element.new(docname, name) end, __newindex=function(self, k, v) gui.setattr(self.docname, self.name, k, v) + end, + __ipairs=function(self) + local i = 0 + return function() + i = i + 1 + local elem = gui.getattr(self.docname, self.name, i) + if elem == nil then + return + end + return i, elem + end end }) end diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index a70e7136..7c891c88 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -341,27 +341,41 @@ static int p_get_data(UINode* node, lua::State* L) { return 0; } +static const std::string& request_node_id(const DocumentNode& docnode) { + std::string id = docnode.node->getId(); + if (id.empty()) { + id = "#" + std::to_string( + reinterpret_cast(docnode.node.get())); + } + docnode.node->setId(std::move(id)); + UINode::getIndices( + docnode.node, docnode.document->getMapWriteable() + ); + return docnode.node->getId(); +} + +/// @brief Push UI-document node object to stack +/// using lua argument at 1 as document name +/// @param id UI-node id +static int push_document_node(lua::State* L, const std::string& id) { + lua::requireglobal(L, "__vc_get_document_node"); + lua::pushvalue(L, 1); + lua::pushstring(L, id); + return lua::call(L, 2, 1); +} + static int p_get_parent(UINode* node, lua::State* L) { auto parent = node->getParent(); if (!parent) { return 0; } - auto id = parent->getId(); - if (id.empty()) { - id = "#" + std::to_string(reinterpret_cast(parent)); - } - parent->setId(id); - auto docname = lua::require_string(L, 1); auto element = lua::require_string(L, 2); auto docnode = get_document_node_impl(L, docname, element); - UINode::getIndices( - parent->shared_from_this(), docnode.document->getMapWriteable() - ); - lua::requireglobal(L, "__vc_get_document_node"); - lua::pushvalue(L, 1); - lua::pushstring(L, id); - return lua::call(L, 2, 1); + + const auto& id = request_node_id(docnode); + + return push_document_node(L, id); } static int p_get_add(UINode* node, lua::State* L) { @@ -459,6 +473,21 @@ static int p_get_scroll(UINode* node, lua::State* L) { static int l_gui_getattr(lua::State* L) { auto docname = lua::require_string(L, 1); auto element = lua::require_string(L, 2); + if (lua::isnumber(L, 3)) { + auto docnode = get_document_node_impl(L, docname, element); + auto container = dynamic_cast(docnode.node.get()); + if (container == nullptr) { + return 0; + } + size_t index = lua::tointeger(L, 3) - 1; + const auto& nodes = container->getNodes(); + if (index >= nodes.size()) { + return 0; + } + const auto& node = nodes.at(index); + const auto& id = request_node_id(DocumentNode {docnode.document, node}); + return push_document_node(L, id); + } auto attr = lua::require_string(L, 3); static const std::unordered_map< From 41ec44b6ee7ffd86327aa3a2db3e9267c900d745 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 17 Apr 2025 20:05:09 +0300 Subject: [PATCH 4/4] update doc/*/scripting/ui.md --- doc/en/scripting/ui.md | 37 ++++++++++++++++++++----------------- doc/ru/scripting/ui.md | 37 ++++++++++++++++++++----------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 771049c5..c649fadc 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -28,27 +28,30 @@ For example: document["worlds-panel"]:clear() ``` +Access to nested elements is performed by index (starting from one). + ## General properties and methods Properties that apply to all elements: -| Name | Type | Read | Write | Description | -| ------------- | ------ | ---- | ----- | ------------------------------------------- | -| id | string | yes | *no* | element id | -| pos | vec2 | yes | yes | element position inside a container | -| wpos | vec2 | yes | yes | element position inside the window | -| size | vec2 | yes | yes | element size | -| interactive | bool | yes | yes | ability to interact with the element | -| enabled | bool | yes | yes | visually indicated version of *interactive* | -| visible | bool | yes | yes | element visibility | -| focused | bool | yes | yes | focus on element | -| color | rgba | yes | yes | element color | -| hoverColor | rgba | yes | yes | hover color | -| pressedColor | rgba | yes | yes | color when pressed | -| tooltip | string | yes | yes | tooltip text | -| tooltipDelay | float | yes | yes | tooltip delay | -| contentOffset | vec2 | yes | *no* | element content offset | -| cursor | string | yes | yes | cursor displayed on hover | +| Name | Type | Read | Write | Description | +| ------------- | ------- | ---- | ----- | ------------------------------------------- | +| id | string | yes | *no* | element id | +| pos | vec2 | yes | yes | element position inside a container | +| wpos | vec2 | yes | yes | element position inside the window | +| size | vec2 | yes | yes | element size | +| interactive | bool | yes | yes | ability to interact with the element | +| enabled | bool | yes | yes | visually indicated version of *interactive* | +| visible | bool | yes | yes | element visibility | +| focused | bool | yes | yes | focus on element | +| color | rgba | yes | yes | element color | +| hoverColor | rgba | yes | yes | hover color | +| pressedColor | rgba | yes | yes | color when pressed | +| tooltip | string | yes | yes | tooltip text | +| tooltipDelay | float | yes | yes | tooltip delay | +| contentOffset | vec2 | yes | *no* | element content offset | +| cursor | string | yes | yes | cursor displayed on hover | +| parent | Element | yes | *no* | parent element or nil | Common element methods: diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index f9d9ab00..918b77af 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -28,27 +28,30 @@ Id элемента не может быть изменен из скрипта. document["worlds-panel"]:clear() ``` +Доступ к вложенным элементам производится по индексу (с единицы). + ## Общие свойства и методы Свойства, относящиеся ко всем элементам: -| Название | Тип | Чтение | Запись | Описание | -| ------------- | ------ | ------ | ------ | ----------------------------------------- | -| id | string | да | *нет* | идентификатор элемента | -| pos | vec2 | да | да | позиция элемента внутри контейнера | -| wpos | vec2 | да | да | позиция элемента в окне | -| size | vec2 | да | да | размер элемента | -| interactive | bool | да | да | возможность взаимодействия с элементом | -| enabled | bool | да | да | визуально обозначаемая версия interactive | -| visible | bool | да | да | видимость элемента | -| focused | bool | да | да | фокус на элементе | -| color | rgba | да | да | цвет элемента | -| hoverColor | rgba | да | да | цвет при наведении | -| pressedColor | rgba | да | да | цвет при нажатии | -| tooltip | string | да | да | текст всплывающей подсказки | -| tooltipDelay | float | да | да | задержка всплывающей подсказки | -| contentOffset | vec2 | да | *нет* | смещение содержимого | -| cursor | string | да | да | курсор, отображаемый при наведении | +| Название | Тип | Чтение | Запись | Описание | +| ------------- | ------- | ------ | ------ | ----------------------------------------- | +| id | string | да | *нет* | идентификатор элемента | +| pos | vec2 | да | да | позиция элемента внутри контейнера | +| wpos | vec2 | да | да | позиция элемента в окне | +| size | vec2 | да | да | размер элемента | +| interactive | bool | да | да | возможность взаимодействия с элементом | +| enabled | bool | да | да | визуально обозначаемая версия interactive | +| visible | bool | да | да | видимость элемента | +| focused | bool | да | да | фокус на элементе | +| color | rgba | да | да | цвет элемента | +| hoverColor | rgba | да | да | цвет при наведении | +| pressedColor | rgba | да | да | цвет при нажатии | +| tooltip | string | да | да | текст всплывающей подсказки | +| tooltipDelay | float | да | да | задержка всплывающей подсказки | +| contentOffset | vec2 | да | *нет* | смещение содержимого | +| cursor | string | да | да | курсор, отображаемый при наведении | +| parent | Element | да | *нет* | родительский элемент или nil | Общие методы элементов: