diff --git a/doc/en/scripting/ui.md b/doc/en/scripting/ui.md index 7ce304b8c..968a11929 100644 --- a/doc/en/scripting/ui.md +++ b/doc/en/scripting/ui.md @@ -34,25 +34,26 @@ Access to nested elements is performed by index (starting from one). Properties that apply to all elements: -| Name | Type | Read | Write | Description | -| ------------- | ------- | ---- | ----- | ------------------------------------------- | -| id | string | yes | *no* | element id | -| exists | bool | yes | *no* | checks if element exists | -| 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 | +| Name | Type | Read | Write | Description | +| ------------- | ------- | ---- | ----- | ------------------------------------------------ | +| id | string | yes | *no* | element id | +| exists | bool | yes | *no* | checks if element exists | +| 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 | +| zIndex | bool | yes | *no* | z-index value. In panel, it determines the order | Common element methods: @@ -236,3 +237,12 @@ Properties: | Name | Type | Read | Write | Description | | --------- | ---- | ---- | ----- | ------------------------------------------------- | | inventory | int | yes | yes | id of the inventory to which the element is bound | + +## Slot + +Properties: + +| Name | Type | Read | Write | Description | +| --------- | ---- | ---- | ----- | -------------------------------------------------- | +| inventory | int | yes | no | id of the inventory to which the element is bound | +| slotIndex | int | yes | no | index of the slot to which the element is attached | diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md index 6a739d168..c238d24f6 100644 --- a/doc/en/xml-ui-layouts.md +++ b/doc/en/xml-ui-layouts.md @@ -42,8 +42,11 @@ Examples: - `ondoubleclick` - lua function called when you double click on an element. - `onfocus` - lua function called when focusing on an element. - `ondefocus` - lua function called when the element loses focus. -- `onmouseover` - lua function called when the cursor enters an element. -- `onmouseover` - lua function called when the cursor exits an element. +- `onmouseenter` - lua function called when the cursor enters an element. +- `onmouseleave` - lua function called when the cursor leaves an element. +- `onmouseover` - lua function called when the cursor hovers over an element. +- `onmouseout` - lua function called when the cursor exits an element. + onmouseenter and onmouseleave do not respect the elements hierarchy. - `tooltip` - tooltip text - `tooltip-delay` - tooltip show-up delay - `gravity` - automatic positioning of the element in the container. (Does not work in automatic containers like panel). Values: *top-left, top-center, top-right, center-left, center-center, center-right, bottom-left, bottom-center, bottom-right*. diff --git a/doc/ru/scripting/ui.md b/doc/ru/scripting/ui.md index 963e8b004..33c4958f4 100644 --- a/doc/ru/scripting/ui.md +++ b/doc/ru/scripting/ui.md @@ -34,25 +34,26 @@ document["worlds-panel"]:clear() Свойства, относящиеся ко всем элементам: -| Название | Тип | Чтение | Запись | Описание | -| ------------- | ------- | ------ | ------ | ----------------------------------------- | -| id | string | да | *нет* | идентификатор элемента | -| exists | bool | да | *нет* | проверяет, существует ли элемент | -| 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 | +| Название | Тип | Чтение | Запись | Описание | +| ------------- | ------- | ------ | ------ | ---------------------------------------------- | +| id | string | да | *нет* | идентификатор элемента | +| exists | bool | да | *нет* | проверяет, существует ли элемент | +| 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 | +| zIndex | bool | да | *нет* | значение z-индекса. В panel определяет порядок | Общие методы элементов: @@ -237,3 +238,12 @@ Canvas.decode(data: Bytearray, format: str) -> Canvas |-----------|-----|--------|--------|-------------------------------------------| | inventory | int | да | да | id инвентаря, к которому привязан элемент | +## Слот (slot) + +Свойства: + +| Название | Тип | Чтение | Запись | Описание | +|-----------|-----|--------|--------|-------------------------------------------| +| inventory | int | да | нет | id инвентаря, к которому привязан элемент | +| slotIndex | int | да | нет | индекс слота, к которому примязан элемент | + diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md index 3c5d1acc1..d1255007c 100644 --- a/doc/ru/xml-ui-layouts.md +++ b/doc/ru/xml-ui-layouts.md @@ -46,8 +46,11 @@ - `ondoubleclick` - lua функция вызываемая при двойном нажатии на элемент. - `onfocus` - lua функция вызываемая при фокусировке на элемент. - `ondefocus` - lua функция вызываемая при потере фокуса элеметом. -- `onmouseover` - lua функция вызываемая при входе курсора в элемент. -- `onmouseover` - lua функция вызываемая при выходе курсора из элемента. +- `onmouseenter` - lua функция вызываемая при входе курсора в элемент. +- `onmouseleave` - lua функция вызываемая при выходе курсора из элемента. +- `onmouseover` - lua функция вызываемая при наведении курсора на элемент. +- `onmouseout` - lua функция вызываемая при выходе курсора из элемента. + onmouseenter и onmouseleave не учитывают иерархию элементов. - `tooltip` - текст всплывающей подсказки - `tooltip-delay` - задержка появления всплывающей подсказки - `gravity` - автоматическое позиционирование элемента в контейнере. (Не работает в автоматических контейнерах, как panel). Значения: *top-left, top-center, top-right, center-left, center-center, center-right, bottom-left, bottom-center, bottom-right*. diff --git a/res/modules/internal/gui_util.lua b/res/modules/internal/gui_util.lua index 793dc8bf1..f9b29c193 100644 --- a/res/modules/internal/gui_util.lua +++ b/res/modules/internal/gui_util.lua @@ -183,7 +183,7 @@ function gui.ask(text, on_yes, on_no) end gui.root.root:add(string.format([[ - diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp index 6ddc6744c..d40ab3b8e 100644 --- a/src/frontend/hud.cpp +++ b/src/frontend/hud.cpp @@ -470,7 +470,9 @@ void Hud::showExchangeSlot() { SlotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr) ); exchangeSlot->setId("hud.exchange-slot"); - exchangeSlot->bind(exchangeSlotInv->getId(), exchangeSlotInv->getSlot(0), &content); + exchangeSlot->bind( + exchangeSlotInv->getId(), exchangeSlotInv->getSlot(0), 0, &content + ); exchangeSlot->setColor(glm::vec4()); exchangeSlot->setInteractive(false); exchangeSlot->setZIndex(1); diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp index 22ae5b908..05c20604a 100644 --- a/src/graphics/ui/GUI.cpp +++ b/src/graphics/ui/GUI.cpp @@ -133,17 +133,40 @@ void GUI::actMouse(float delta, const CursorState& cursor) { auto hover = container->getAt(cursor.pos); if (this->hover && this->hover != hover) { - this->hover->setHover(false); + this->hover->setMouseEnter(false); } if (hover) { - hover->setHover(true); - + if (hover != this->hover) { + hover->setMouseEnter(true); + } int scroll = input.getScroll(); if (scroll) { hover->scrolled(scroll); } } this->hover = hover; + auto node = hover; + while (node) { + if (mouseOver.find(hover) != mouseOver.end()) { + break; + } + mouseOver.insert(node); + node->setMouseOver(true); + auto parent = node->getParent(); + if (parent) { + node = parent->shared_from_this(); + } + } + + for (auto it = mouseOver.begin(); it != mouseOver.end(); ) { + auto node = *it; + if (node->isInside(cursor.pos)) { + ++it; + continue; + } + node->setMouseOver(false); + it = mouseOver.erase(it); + } if (input.jclicked(Mousecode::BUTTON_1)) { if (pressed == nullptr && this->hover) { @@ -219,7 +242,7 @@ void GUI::act(float delta, const glm::uvec2& vp) { actMouse(delta, cursor); } else { if (hover) { - hover->setHover(false); + hover->setMouseEnter(false); hover = nullptr; } } @@ -247,7 +270,6 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) { auto& page = menu->getCurrent(); if (page.panel) { menu->setSize(page.panel->getSize()); - page.panel->refresh(); if (auto panel = std::dynamic_pointer_cast(page.panel)) { panel->cropToContent(); } diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp index 16d8e358c..8a12c3cd4 100644 --- a/src/graphics/ui/GUI.hpp +++ b/src/graphics/ui/GUI.hpp @@ -2,6 +2,7 @@ #include "delegates.hpp" +#include #include #include #include @@ -80,6 +81,7 @@ namespace gui { std::unique_ptr uicamera; std::shared_ptr menu; std::queue postRunnables; + std::set> mouseOver; PageLoaderFunc pagesLoader; diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp index d3a747225..2d610c7ca 100644 --- a/src/graphics/ui/elements/InventoryView.cpp +++ b/src/graphics/ui/elements/InventoryView.cpp @@ -382,7 +382,7 @@ void SlotView::performLeftClick(ItemStack& stack, ItemStack& grabbed) { void SlotView::performRightClick(ItemStack& stack, ItemStack& grabbed) { if (layout.rightClick) { - layout.rightClick(inventoryid, stack); + layout.rightClick(inventoryId, stack); if (layout.updateFunc) { layout.updateFunc(layout.index, stack); } @@ -452,10 +452,11 @@ const std::wstring& SlotView::getTooltip() const { } void SlotView::bind( - int64_t inventoryid, ItemStack& stack, const Content* content + int64_t inventoryid, ItemStack& stack, size_t index, const Content* content ) { - this->inventoryid = inventoryid; + this->inventoryId = inventoryid; bound = &stack; + this->index = index; this->content = content; } @@ -467,6 +468,14 @@ ItemStack& SlotView::getStack() { return *bound; } +int64_t SlotView::getInventoryId() const { + return inventoryId; +} + +size_t SlotView::getIndex() const { + return index; +} + InventoryView::InventoryView(GUI& gui) : Container(gui, glm::vec2()) { setColor(glm::vec4(0, 0, 0, 0.0f)); } @@ -510,9 +519,11 @@ void InventoryView::bind( this->inventory = inventory; this->content = content; for (auto slot : slots) { + const auto& layout = slot->getLayout(); slot->bind( inventory->getId(), - inventory->getSlot(slot->getLayout().index), + inventory->getSlot(layout.index), + layout.index, content ); } diff --git a/src/graphics/ui/elements/InventoryView.hpp b/src/graphics/ui/elements/InventoryView.hpp index b80f65e68..9275a2daa 100644 --- a/src/graphics/ui/elements/InventoryView.hpp +++ b/src/graphics/ui/elements/InventoryView.hpp @@ -61,8 +61,9 @@ namespace gui { SlotLayout layout; bool highlighted = false; - int64_t inventoryid = 0; + int64_t inventoryId = 0; ItemStack* bound = nullptr; + size_t index = 0; void performLeftClick(ItemStack& stack, ItemStack& grabbed); void performRightClick(ItemStack& stack, ItemStack& grabbed); @@ -100,11 +101,14 @@ namespace gui { void bind( int64_t inventoryid, ItemStack& stack, + size_t index, const Content* content ); ItemStack& getStack(); const SlotLayout& getLayout() const; + int64_t getInventoryId() const; + size_t getIndex() const; static inline std::string EXCHANGE_SLOT_NAME = "exchange-slot"; }; diff --git a/src/graphics/ui/elements/Panel.cpp b/src/graphics/ui/elements/Panel.cpp index ed9af66c5..885ae3d61 100644 --- a/src/graphics/ui/elements/Panel.cpp +++ b/src/graphics/ui/elements/Panel.cpp @@ -67,7 +67,7 @@ void Panel::remove(UINode* node) { } void Panel::refresh() { - UINode::refresh(); + Container::refresh(); float x = padding.x; float y = padding.y; diff --git a/src/graphics/ui/elements/UINode.cpp b/src/graphics/ui/elements/UINode.cpp index cbcb1c909..465ff26e4 100644 --- a/src/graphics/ui/elements/UINode.cpp +++ b/src/graphics/ui/elements/UINode.cpp @@ -47,10 +47,11 @@ void UINode::setAlign(Align align) { this->align = align; } -void UINode::setHover(bool flag) { - if (hover == flag) { - return; - } +void UINode::setMouseEnter(bool flag) { + actions.notify(flag ? UIAction::MOUSE_ENTER : UIAction::MOUSE_LEAVE, gui); +} + +void UINode::setMouseOver(bool flag) { hover = flag; actions.notify(flag ? UIAction::MOUSE_OVER : UIAction::MOUSE_OUT, gui); } diff --git a/src/graphics/ui/elements/UINode.hpp b/src/graphics/ui/elements/UINode.hpp index cbd43b380..fb699a737 100644 --- a/src/graphics/ui/elements/UINode.hpp +++ b/src/graphics/ui/elements/UINode.hpp @@ -59,7 +59,9 @@ namespace gui { DEFOCUS, RIGHT_CLICK, MOUSE_OVER, - MOUSE_OUT + MOUSE_OUT, + MOUSE_ENTER, + MOUSE_LEAVE, }; using ActionsSet = TaggedCallbacksSet; @@ -167,11 +169,14 @@ namespace gui { virtual void setAlign(Align align); Align getAlign() const; - virtual void setHover(bool flag); + virtual void setMouseEnter(bool flag); bool isHover() const; + void setMouseOver(bool flag); + virtual void setParent(UINode* node); UINode* getParent() const; + std::shared_ptr getParentShared() const; /// @brief Set element color (doesn't affect inner elements). /// Also replaces hover color to avoid adding extra properties diff --git a/src/graphics/ui/gui_xml.cpp b/src/graphics/ui/gui_xml.cpp index 6011373fb..967fd6bca 100644 --- a/src/graphics/ui/gui_xml.cpp +++ b/src/graphics/ui/gui_xml.cpp @@ -186,6 +186,8 @@ static void read_uinode( register_action(node, reader, element, "ondoubleclick", UIAction::DOUBLE_CLICK); register_action(node, reader, element, "onmouseover", UIAction::MOUSE_OVER); register_action(node, reader, element, "onmouseout", UIAction::MOUSE_OUT); + register_action(node, reader, element, "onmouseenter", UIAction::MOUSE_ENTER); + register_action(node, reader, element, "onmouseleave", UIAction::MOUSE_LEAVE); } static void read_container_impl( diff --git a/src/logic/scripting/lua/libs/libgui.cpp b/src/logic/scripting/lua/libs/libgui.cpp index b08d54455..4d1506e23 100644 --- a/src/logic/scripting/lua/libs/libgui.cpp +++ b/src/logic/scripting/lua/libs/libgui.cpp @@ -209,6 +209,15 @@ static int p_get_inventory(UINode* node, lua::State* L) { if (auto inventory = dynamic_cast(node)) { auto inv = inventory->getInventory(); return lua::pushinteger(L, inv ? inv->getId() : 0); + } else if (auto slot = dynamic_cast(node)) { + return lua::pushinteger(L, slot->getInventoryId()); + } + return 0; +} + +static int p_get_slot_index(UINode* node, lua::State* L) { + if (auto slot = dynamic_cast(node)) { + return lua::pushinteger(L, slot->getIndex()); } return 0; } @@ -568,6 +577,13 @@ static int p_get_options(UINode* node, lua::State* L) { return 0; } +static int p_get_zindex(UINode* node, lua::State* L) { + if (node == nullptr) { + return 0; + } + return lua::pushinteger(L, node->getZIndex()); +} + static int p_is_exists(UINode* node, lua::State* L) { return lua::pushboolean(L, node != nullptr); } @@ -661,12 +677,14 @@ static int l_gui_getattr(lua::State* L) { {"reset", p_get_reset}, {"paste", p_get_paste}, {"inventory", p_get_inventory}, + {"slotIndex", p_get_slot_index}, {"focused", p_get_focused}, {"cursor", p_get_cursor}, {"data", p_get_data}, {"parent", p_get_parent}, {"region", p_get_region}, {"options", p_get_options}, + {"zIndex", p_get_zindex}, }; auto func = getters.find(attr); if (func != getters.end()) { @@ -803,6 +821,15 @@ static void p_set_options(UINode* node, lua::State* L, int idx) { selectbox->setOptions(std::move(options)); } } +static void p_set_zindex(UINode* node, lua::State* L, int idx) { + if (node == nullptr) { + return; + } + node->setZIndex(lua::tointeger(L, idx)); + if (auto parent = node->getParent()) { + parent->setMustRefresh(); + } +} static void p_set_value(UINode* node, lua::State* L, int idx) { if (auto bar = dynamic_cast(node)) { bar->setValue(lua::tonumber(L, idx)); @@ -951,6 +978,7 @@ static int l_gui_setattr(lua::State* L) { {"focused", p_set_focused}, {"region", p_set_region}, {"options", p_set_options}, + {"zIndex", p_set_zindex}, }; auto func = setters.find(attr); if (func != setters.end()) {