Merge branch 'main' into render-update

This commit is contained in:
MihailRis 2025-04-27 14:28:11 +03:00
commit ad07ec61cd
19 changed files with 151 additions and 34 deletions

View File

@ -164,9 +164,10 @@ Properties:
Properties:
| Name | Type | Read | Write | Description |
| ----- | ------ | ---- | ----- | ------------ |
| src | string | yes | yes | texture name |
| Name | Type | Read | Write | Description |
| ------ | ------ | ---- | ----- | ---------------- |
| src | string | yes | yes | texture name |
| region | vec4 | yes | yes | image sub-region |
## Canvas

View File

@ -40,6 +40,8 @@ Examples:
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
- `onclick` - lua function called when an element is clicked.
- `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.
- `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*.
@ -112,6 +114,7 @@ Inner text is a button text.
- `src` - name of an image stored in textures folder. Extension is not specified. Type: string.
Example: *gui/error*
- `region` - image region x1, y1, x2, y2 from 0.0, 0.0 (upper left corner), 1.0, 1.0 (lower right corner)
## *canvas*

View File

@ -164,9 +164,10 @@ document["worlds-panel"]:clear()
Свойства:
| Название | Тип | Чтение | Запись | Описание |
| -------- | ------ | ------ | ------ | --------------------- |
| src | string | да | да | отображаемая текстура |
| Название | Тип | Чтение | Запись | Описание |
| -------- | ------ | ------ | ------ | ---------------------- |
| src | string | да | да | отображаемая текстура |
| region | vec4 | да | да | под-регион изображения |
## Холст (canvas)

View File

@ -44,6 +44,8 @@
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
- `onclick` - lua функция вызываемая при нажатии на элемент.
- `ondoubleclick` - lua функция вызываемая при двойном нажатии на элемент.
- `onfocus` - lua функция вызываемая при фокусировке на элемент.
- `ondefocus` - lua функция вызываемая при потере фокуса элеметом.
- `tooltip` - текст всплывающей подсказки
- `tooltip-delay` - задержка появления всплывающей подсказки
- `gravity` - автоматическое позиционирование элемента в контейнере. (Не работает в автоматических контейнерах, как panel). Значения: *top-left, top-center, top-right, center-left, center-center, center-right, bottom-left, bottom-center, bottom-right*.
@ -113,6 +115,7 @@
## Изображение - *image*
- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error`
- `region` - под-регион изображения x1, y1, x2, y2 от 0.0, 0.0 (левый верхний угол), 1.0, 1.0 (правый нижний угол)
## Холст - *canvas*

View File

@ -226,15 +226,20 @@ end
function gui.template(name, params)
local text = file.read(file.find("layouts/templates/"..name..".xml"))
for k,v in pairs(params) do
local arg = tostring(v):gsub("'", "\\'"):gsub('"', '\\"')
text = text:gsub("(%%{"..k.."})", arg)
end
text = text:gsub("if%s*=%s*'%%{%w+}'", "if=''")
text = text:gsub("if%s*=%s*\"%%{%w+}\"", "if=\"\"")
text = text:gsub("%%{([^}]+)}", function(n)
local s = params[n]
if type(s) ~= "string" then
return tostring(s)
end
if #s == 0 then
return
end
local e = string.escape(s)
return e:sub(2, #e-1)
end)
text = text:gsub('if%s*=%s*[\'"]%%{%w+}[\'"]', "if=\"\"")
-- remove unsolved properties: attr='%{var}'
text = text:gsub("%s*%S+='%%{[^}]+}'%s*", " ")
text = text:gsub('%s*%S+="%%{[^}]+}"%s*', " ")
text = text:gsub('%s*%S+=[\'"]%%{[^}]+}[\'"]%s*', " ")
return text
end

View File

@ -53,11 +53,11 @@ namespace xml {
/// @brief Get element tag
const std::string& getTag() const;
inline bool isText() const {
bool isText() const {
return getTag() == "#";
}
inline const std::string& text() const {
const std::string& getInnerText() const {
return attr("#").getText();
}

View File

@ -156,7 +156,7 @@ template<> void ContentUnitLoader<Block>::loadUnit(
if (auto found = root.at("emission")) {
const auto& emissionarr = *found;
for (size_t i = 0; i < 3; i++) {
def.emission[i] = std::clamp(emissionarr[i].asInteger(), static_cast<integer_t>(0), static_cast<integer_t>(15));
def.emission[i] = glm::clamp(emissionarr[i].asInteger(), static_cast<integer_t>(0), static_cast<integer_t>(15));
}
}

View File

@ -67,7 +67,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
? scripting::create_doc_environment(scripting::get_root_environment(), name)
: scripting::create_doc_environment(penv, name);
gui::UiXmlReader reader(gui, env);
gui::UiXmlReader reader(gui, scriptenv(env));
auto view = reader.readXML(file.string(), *xmldoc->getRoot());
view->setId("root");
uidocscript script {};

View File

@ -71,6 +71,7 @@ void Container::mouseRelease(int x, int y) {
}
void Container::act(float delta) {
UINode::act(delta);
for (const auto& node : nodes) {
if (node->isVisible()) {
node->act(delta);
@ -162,7 +163,13 @@ void Container::add(const std::shared_ptr<UINode>& node) {
nodes.push_back(node);
node->setParent(this);
node->reposition();
refresh();
mustRefresh = true;
auto parent = getParent();
while (parent) {
parent->setMustRefresh();
parent = parent->getParent();
}
}
void Container::add(const std::shared_ptr<UINode>& node, glm::vec2 pos) {
@ -202,7 +209,6 @@ void Container::listenInterval(float interval, ontimeout callback, int repeat) {
void Container::setSize(glm::vec2 size) {
if (size == getSize()) {
refresh();
return;
}
UINode::setSize(size);

View File

@ -55,7 +55,7 @@ void Image::draw(const DrawContext& pctx, const Assets& assets) {
0,
0,
0,
UVRegion(),
region,
false,
true,
calcColor()
@ -76,3 +76,11 @@ const std::string& Image::getTexture() const {
void Image::setTexture(const std::string& name) {
texture = name;
}
void Image::setRegion(const UVRegion& region) {
this->region = region;
}
const UVRegion& Image::getRegion() const {
return region;
}

View File

@ -1,11 +1,13 @@
#pragma once
#include "UINode.hpp"
#include "maths/UVRegion.hpp"
namespace gui {
class Image : public UINode {
protected:
std::string texture;
UVRegion region {};
bool autoresize = false;
public:
Image(GUI& gui, std::string texture, glm::vec2 size=glm::vec2(32,32));
@ -16,5 +18,7 @@ namespace gui {
virtual bool isAutoResize() const;
virtual const std::string& getTexture() const;
virtual void setTexture(const std::string& name);
void setRegion(const UVRegion& region);
const UVRegion& getRegion() const;
};
}

View File

@ -74,6 +74,16 @@ UINode* UINode::listenDoubleClick(const onaction& action) {
return this;
}
UINode* UINode::listenFocus(const onaction& action) {
focusCallbacks.listen(action);
return this;
}
UINode* UINode::listenDefocus(const onaction& action) {
defocusCallbacks.listen(action);
return this;
}
void UINode::click(int, int) {
pressed = true;
}
@ -96,8 +106,14 @@ bool UINode::isPressed() const {
return pressed;
}
void UINode::onFocus() {
focused = true;
focusCallbacks.notify(gui);
}
void UINode::defocus() {
focused = false;
defocusCallbacks.notify(gui);
}
bool UINode::isFocused() const {

View File

@ -66,6 +66,7 @@ namespace gui {
class UINode : public std::enable_shared_from_this<UINode> {
protected:
GUI& gui;
bool mustRefresh = true;
private:
/// @brief element identifier used for direct access in UiDocument
std::string id = "";
@ -114,6 +115,10 @@ namespace gui {
ActionsSet actions;
/// @brief 'ondoubleclick' callbacks
ActionsSet doubleClickCallbacks;
/// @brief 'onfocus' callbacks
ActionsSet focusCallbacks;
/// @brief 'ondefocus' callbacks
ActionsSet defocusCallbacks;
/// @brief element tooltip text
std::wstring tooltip;
/// @brief element tooltip delay
@ -127,7 +132,12 @@ namespace gui {
/// @brief Called every frame for all visible elements
/// @param delta delta timУ
virtual void act(float delta) {};
virtual void act(float delta) {
if (mustRefresh) {
mustRefresh = false;
refresh();
}
};
virtual void draw(const DrawContext& pctx, const Assets& assets) = 0;
virtual void setVisible(bool flag);
@ -169,10 +179,12 @@ namespace gui {
/// @brief Get element z-index
int getZIndex() const;
virtual UINode* listenAction(const onaction &action);
virtual UINode* listenDoubleClick(const onaction &action);
virtual UINode* listenAction(const onaction& action);
virtual UINode* listenDoubleClick(const onaction& action);
virtual UINode* listenFocus(const onaction& action);
virtual UINode* listenDefocus(const onaction& action);
virtual void onFocus() {focused = true;}
virtual void onFocus();
virtual void doubleClick(int x, int y);
virtual void click(int x, int y);
virtual void clicked(Mousecode button) {}
@ -269,5 +281,9 @@ namespace gui {
const std::shared_ptr<UINode>& node,
const std::string& id
);
void setMustRefresh() {
mustRefresh = true;
}
};
}

View File

@ -21,7 +21,7 @@ std::shared_ptr<gui::UINode> guiutil::create(
if (env == nullptr) {
env = scripting::get_root_environment();
}
UiXmlReader reader(gui, env);
UiXmlReader reader(gui, std::move(env));
return reader.readXML("[string]", source);
}

View File

@ -63,7 +63,7 @@ static runnable create_runnable(
const std::string& name
) {
if (element.has(name)) {
std::string text = element.attr(name).getText();
const std::string& text = element.attr(name).getText();
if (!text.empty()) {
return scripting::create_runnable(
reader.getEnvironment(), text, reader.getFilename()
@ -180,6 +180,14 @@ static void read_uinode(
node.listenAction(onclick);
}
if (auto onfocus = create_action(reader, element, "onfocus")) {
node.listenFocus(onfocus);
}
if (auto ondefocus = create_action(reader, element, "ondefocus")) {
node.listenDefocus(ondefocus);
}
if (auto ondoubleclick = create_action(reader, element, "ondoubleclick")) {
node.listenDoubleClick(ondoubleclick);
}
@ -279,7 +287,7 @@ static std::wstring parse_inner_text(
) {
std::wstring text = L"";
if (element.size() == 1) {
std::string source = element.sub(0).attr("#").getText();
std::string source = element.sub(0).getInnerText();
util::trim(source);
text = util::str2wstr_utf8(source);
if (text[0] == '@') {
@ -379,7 +387,7 @@ static std::shared_ptr<UINode> read_button(
std::shared_ptr<Button> button;
auto& elements = element.getElements();
if (!elements.empty() && elements[0]->getTag() != "#") {
if (!elements.empty() && !elements[0]->isText()) {
auto inner = reader.readUINode(*elements.at(0));
if (inner != nullptr) {
button = std::make_shared<Button>(gui, inner, padding);
@ -536,6 +544,11 @@ static std::shared_ptr<UINode> read_image(
std::string src = element.attr("src", "").getText();
auto image = std::make_shared<Image>(reader.getGUI(), src);
read_uinode(reader, element, *image);
if (element.has("region")) {
auto vec = element.attr("region").asVec4();
image->setRegion(UVRegion(vec.x, vec.y, vec.z, vec.w));
}
return image;
}
@ -757,7 +770,7 @@ static std::shared_ptr<UINode> read_iframe(
return iframe;
}
UiXmlReader::UiXmlReader(gui::GUI& gui, const scriptenv& env) : gui(gui), env(env) {
UiXmlReader::UiXmlReader(gui::GUI& gui, scriptenv&& env) : gui(gui), env(std::move(env)) {
contextStack.emplace("");
add("image", read_image);
add("canvas", read_canvas);

View File

@ -20,9 +20,9 @@ namespace gui {
std::unordered_set<std::string> ignored;
std::stack<std::string> contextStack;
std::string filename;
const scriptenv& env;
scriptenv env;
public:
UiXmlReader(gui::GUI& gui, const scriptenv& env);
UiXmlReader(gui::GUI& gui, scriptenv&& env);
void add(const std::string& tag, uinode_reader reader);
bool hasReader(const std::string& tag) const;

View File

@ -83,11 +83,19 @@ static int l_container_add(lua::State* L) {
}
auto xmlsrc = lua::require_string(L, 2);
try {
auto env = docnode.document->getEnvironment();
if (lua::istable(L, 3)) {
env = create_environment(std::move(env));
lua::pushenv(L, *env);
lua::pushvalue(L, 3);
lua::setfield(L, "DATA");
lua::pop(L);
}
auto subnode = guiutil::create(
engine->getGUI(), xmlsrc, docnode.document->getEnvironment()
engine->getGUI(), xmlsrc, std::move(env)
);
node->add(subnode);
UINode::getIndices(subnode, docnode.document->getMapWriteable());
node->add(std::move(subnode));
} catch (const std::exception& err) {
throw std::runtime_error(err.what());
}
@ -337,6 +345,14 @@ static int p_get_src(UINode* node, lua::State* L) {
return 0;
}
static int p_get_region(UINode* node, lua::State* L) {
if (auto image = dynamic_cast<Image*>(node)) {
const auto& region = image->getRegion();
return lua::pushvec4(L, {region.u1, region.v1, region.u2, region.v2});
}
return 0;
}
static int p_get_data(UINode* node, lua::State* L) {
if (auto canvas = dynamic_cast<Canvas*>(node)) {
return lua::newuserdata<lua::LuaCanvas>(L, canvas->texture(), canvas->data());
@ -548,6 +564,7 @@ static int l_gui_getattr(lua::State* L) {
{"cursor", p_get_cursor},
{"data", p_get_data},
{"parent", p_get_parent},
{"region", p_get_region},
};
auto func = getters.find(attr);
if (func != getters.end()) {
@ -651,6 +668,12 @@ static void p_set_src(UINode* node, lua::State* L, int idx) {
iframe->setSrc(lua::require_string(L, idx));
}
}
static void p_set_region(UINode* node, lua::State* L, int idx) {
if (auto image = dynamic_cast<Image*>(node)) {
auto vec = lua::tovec4(L, idx);
image->setRegion(UVRegion(vec.x, vec.y, vec.z, vec.w));
}
}
static void p_set_value(UINode* node, lua::State* L, int idx) {
if (auto bar = dynamic_cast<TrackBar*>(node)) {
bar->setValue(lua::tonumber(L, idx));
@ -777,6 +800,7 @@ static int l_gui_setattr(lua::State* L) {
{"inventory", p_set_inventory},
{"cursor", p_set_cursor},
{"focused", p_set_focused},
{"region", p_set_region},
};
auto func = setters.find(attr);
if (func != setters.end()) {

View File

@ -154,6 +154,22 @@ std::unique_ptr<Process> scripting::start_coroutine(
});
}
[[nodiscard]] scriptenv scripting::create_environment(
const scriptenv& parent
) {
auto L = lua::get_main_state();
int id = lua::create_environment(L, (parent ? *parent : 0));
lua::pushenv(L, id);
lua::pushvalue(L, -1);
lua::setfield(L, "CUR_ENV");
lua::pop(L);
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
lua::remove_environment(L, *id);
delete id;
});
}
[[nodiscard]] scriptenv scripting::create_doc_environment(
const scriptenv& parent, const std::string& name
) {

View File

@ -58,6 +58,7 @@ namespace scripting {
scriptenv get_root_environment();
scriptenv create_pack_environment(const ContentPack& pack);
scriptenv create_environment(const scriptenv& parent);
scriptenv create_doc_environment(
const scriptenv& parent, const std::string& name
);