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 | Общие методы элементов: diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua index 294a7c79..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 @@ -102,6 +113,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/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))) diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index 2df7f322..7c891c88 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -341,6 +341,43 @@ 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 docname = lua::require_string(L, 1); + auto element = lua::require_string(L, 2); + auto docnode = get_document_node_impl(L, docname, element); + + const auto& id = request_node_id(docnode); + + return push_document_node(L, id); +} + static int p_get_add(UINode* node, lua::State* L) { if (dynamic_cast(node)) { return lua::pushcfunction(L, lua::wrap); @@ -436,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< @@ -492,6 +544,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()) {