Working on ui
This commit is contained in:
parent
2728ca9646
commit
ffbecbbcde
@ -10,30 +10,17 @@
|
|||||||
"type": "LinearLayout",
|
"type": "LinearLayout",
|
||||||
"name": "settingsButtons",
|
"name": "settingsButtons",
|
||||||
"orientation": "vertical",
|
"orientation": "vertical",
|
||||||
|
"vertical_align" : "center",
|
||||||
|
"horizontal_align" : "center",
|
||||||
"spacing": 10,
|
"spacing": 10,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"width": 300,
|
"width": 1280,
|
||||||
"height": 300,
|
"height": 720,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
|
||||||
"type": "Button",
|
|
||||||
"name": "langButton",
|
|
||||||
"x": 1100,
|
|
||||||
"y": 580,
|
|
||||||
"width": 142,
|
|
||||||
"height": 96,
|
|
||||||
"textures": {
|
|
||||||
"normal": "resources/main_menu/lang.png",
|
|
||||||
"hover": "resources/main_menu/lang.png",
|
|
||||||
"pressed": "resources/main_menu/lang.png"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "titleBtn",
|
"name": "titleBtn",
|
||||||
"x": 473,
|
|
||||||
"y": 500,
|
|
||||||
"width": 254,
|
"width": 254,
|
||||||
"height": 35,
|
"height": 35,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -45,8 +32,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "underlineBtn",
|
"name": "underlineBtn",
|
||||||
"x": 516,
|
|
||||||
"y": 465,
|
|
||||||
"width": 168,
|
"width": 168,
|
||||||
"height": 44,
|
"height": 44,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -58,8 +43,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "subtitleBtn",
|
"name": "subtitleBtn",
|
||||||
"x": 528,
|
|
||||||
"y": 455,
|
|
||||||
"width": 144,
|
"width": 144,
|
||||||
"height": 11,
|
"height": 11,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -71,8 +54,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "singleButton",
|
"name": "singleButton",
|
||||||
"x": 409,
|
|
||||||
"y": 360,
|
|
||||||
"width": 382,
|
"width": 382,
|
||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -84,8 +65,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "multiplayerButton",
|
"name": "multiplayerButton",
|
||||||
"x": 409,
|
|
||||||
"y": 289,
|
|
||||||
"width": 382,
|
"width": 382,
|
||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -97,8 +76,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "multiplayerButton2",
|
"name": "multiplayerButton2",
|
||||||
"x": 409,
|
|
||||||
"y": 218,
|
|
||||||
"width": 382,
|
"width": 382,
|
||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -110,8 +87,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "exitButton",
|
"name": "exitButton",
|
||||||
"x": 409,
|
|
||||||
"y": 147,
|
|
||||||
"width": 382,
|
"width": 382,
|
||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
@ -123,8 +98,6 @@
|
|||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "versionLabel",
|
"name": "versionLabel",
|
||||||
"x": 559.5,
|
|
||||||
"y": 99,
|
|
||||||
"width": 81,
|
"width": 81,
|
||||||
"height": 9,
|
"height": 9,
|
||||||
"textures": {
|
"textures": {
|
||||||
|
|||||||
@ -184,21 +184,54 @@ namespace ZL {
|
|||||||
|
|
||||||
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
|
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
|
||||||
auto node = std::make_shared<UiNode>();
|
auto node = std::make_shared<UiNode>();
|
||||||
if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get<std::string>();
|
|
||||||
if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get<std::string>();
|
|
||||||
|
|
||||||
if (j.contains("x")) node->rect.x = j["x"].get<float>();
|
// 1. Определяем тип контейнера и ориентацию
|
||||||
if (j.contains("y")) node->rect.y = j["y"].get<float>();
|
std::string typeStr = j.value("type", "FrameLayout"); // По умолчанию FrameLayout
|
||||||
if (j.contains("width")) node->rect.w = j["width"].get<float>();
|
if (typeStr == "LinearLayout") {
|
||||||
if (j.contains("height")) node->rect.h = j["height"].get<float>();
|
node->layoutType = LayoutType::Linear;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node->layoutType = LayoutType::Frame;
|
||||||
|
}
|
||||||
|
|
||||||
if (j.contains("orientation") && j["orientation"].is_string()) node->orientation = j["orientation"].get<std::string>();
|
if (j.contains("name")) node->name = j["name"].get<std::string>();
|
||||||
if (j.contains("spacing")) node->spacing = j["spacing"].get<float>();
|
|
||||||
|
|
||||||
if (node->type == "Button") {
|
// 2. Читаем размеры во временные "локальные" поля
|
||||||
|
// Это критически важно: мы не пишем сразу в screenRect,
|
||||||
|
// так как LinearLayout их пересчитает.
|
||||||
|
node->localX = j.value("x", 0.0f);
|
||||||
|
node->localY = j.value("y", 0.0f);
|
||||||
|
node->width = j.value("width", 0.0f);
|
||||||
|
node->height = j.value("height", 0.0f);
|
||||||
|
|
||||||
|
// 3. Параметры компоновки
|
||||||
|
if (j.contains("orientation")) {
|
||||||
|
std::string orient = j["orientation"].get<std::string>();
|
||||||
|
node->orientation = (orient == "horizontal") ? Orientation::Horizontal : Orientation::Vertical;
|
||||||
|
}
|
||||||
|
node->spacing = j.value("spacing", 0.0f);
|
||||||
|
|
||||||
|
if (j.contains("horizontal_align")) {
|
||||||
|
std::string halign = j["horizontal_align"];
|
||||||
|
if (halign == "center") node->layoutSettings.hAlign = HorizontalAlign::Center;
|
||||||
|
else if (halign == "right") node->layoutSettings.hAlign = HorizontalAlign::Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j.contains("vertical_align")) {
|
||||||
|
std::string valign = j["vertical_align"];
|
||||||
|
if (valign == "center") node->layoutSettings.vAlign = VerticalAlign::Center;
|
||||||
|
else if (valign == "top") node->layoutSettings.vAlign = VerticalAlign::Top;
|
||||||
|
}
|
||||||
|
// Подготавливаем базовый rect для компонентов (кнопок и т.д.)
|
||||||
|
// На этапе парсинга мы даем им "желаемый" размер
|
||||||
|
UiRect initialRect = { node->localX, node->localY, node->width, node->height };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (typeStr == "Button") {
|
||||||
auto btn = std::make_shared<UiButton>();
|
auto btn = std::make_shared<UiButton>();
|
||||||
btn->name = node->name;
|
btn->name = node->name;
|
||||||
btn->rect = node->rect;
|
btn->rect = initialRect;
|
||||||
|
|
||||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||||
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
|
std::cerr << "UiManager: Button '" << btn->name << "' missing textures" << std::endl;
|
||||||
@ -225,10 +258,10 @@ namespace ZL {
|
|||||||
|
|
||||||
node->button = btn;
|
node->button = btn;
|
||||||
}
|
}
|
||||||
else if (node->type == "Slider") {
|
else if (typeStr == "Slider") {
|
||||||
auto s = std::make_shared<UiSlider>();
|
auto s = std::make_shared<UiSlider>();
|
||||||
s->name = node->name;
|
s->name = node->name;
|
||||||
s->rect = node->rect;
|
s->rect = initialRect;
|
||||||
|
|
||||||
if (!j.contains("textures") || !j["textures"].is_object()) {
|
if (!j.contains("textures") || !j["textures"].is_object()) {
|
||||||
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
|
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
|
||||||
@ -261,10 +294,10 @@ namespace ZL {
|
|||||||
|
|
||||||
node->slider = s;
|
node->slider = s;
|
||||||
}
|
}
|
||||||
else if (node->type == "TextField") {
|
else if (typeStr == "TextField") {
|
||||||
auto tf = std::make_shared<UiTextField>();
|
auto tf = std::make_shared<UiTextField>();
|
||||||
tf->name = node->name;
|
tf->name = node->name;
|
||||||
tf->rect = node->rect;
|
tf->rect = initialRect;
|
||||||
|
|
||||||
if (j.contains("placeholder")) tf->placeholder = j["placeholder"].get<std::string>();
|
if (j.contains("placeholder")) tf->placeholder = j["placeholder"].get<std::string>();
|
||||||
if (j.contains("fontPath")) tf->fontPath = j["fontPath"].get<std::string>();
|
if (j.contains("fontPath")) tf->fontPath = j["fontPath"].get<std::string>();
|
||||||
@ -331,11 +364,11 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->type == "TextView") {
|
if (typeStr == "TextView") {
|
||||||
auto tv = std::make_shared<UiTextView>();
|
auto tv = std::make_shared<UiTextView>();
|
||||||
tv->name = node->name;
|
|
||||||
tv->rect = node->rect;
|
|
||||||
|
|
||||||
|
tv->name = node->name;
|
||||||
|
tv->rect = initialRect;
|
||||||
if (j.contains("text")) tv->text = j["text"].get<std::string>();
|
if (j.contains("text")) tv->text = j["text"].get<std::string>();
|
||||||
if (j.contains("fontPath")) tv->fontPath = j["fontPath"].get<std::string>();
|
if (j.contains("fontPath")) tv->fontPath = j["fontPath"].get<std::string>();
|
||||||
if (j.contains("fontSize")) tv->fontSize = j["fontSize"].get<int>();
|
if (j.contains("fontSize")) tv->fontSize = j["fontSize"].get<int>();
|
||||||
@ -400,6 +433,7 @@ namespace ZL {
|
|||||||
throw std::runtime_error("Failed to load UI file: " + path);
|
throw std::runtime_error("Failed to load UI file: " + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
root = parseNode(j["root"], renderer, zipFile);
|
root = parseNode(j["root"], renderer, zipFile);
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
@ -407,7 +441,7 @@ namespace ZL {
|
|||||||
|
|
||||||
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
||||||
root = newRoot;
|
root = newRoot;
|
||||||
layoutNode(root);
|
layoutNode(root, 0, 0);
|
||||||
buttons.clear();
|
buttons.clear();
|
||||||
sliders.clear();
|
sliders.clear();
|
||||||
textViews.clear();
|
textViews.clear();
|
||||||
@ -433,37 +467,167 @@ namespace ZL {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node) {
|
void UiManager::updateLayout(const std::shared_ptr<UiNode>& node, float parentX, float parentY) {
|
||||||
|
// 1. Сначала определяем позицию самого контейнера
|
||||||
|
// Если это корень, он обычно занимает весь экран или заданные координаты
|
||||||
|
node->screenRect.x = parentX + node->localX;
|
||||||
|
node->screenRect.y = parentY + node->localY;
|
||||||
|
node->screenRect.w = node->width;
|
||||||
|
node->screenRect.h = node->height;
|
||||||
|
|
||||||
|
float currentX = node->screenRect.x;
|
||||||
|
// В UI обычно Y растет сверху вниз, но в нашем OpenGL (Renderer.cpp)
|
||||||
|
// используется орто-матрица с Y-вверх. Учтем это.
|
||||||
|
float currentY = node->screenRect.y + node->screenRect.h;
|
||||||
|
|
||||||
for (auto& child : node->children) {
|
for (auto& child : node->children) {
|
||||||
child->rect.x += node->rect.x;
|
if (node->layoutType == LayoutType::Linear) {
|
||||||
child->rect.y += node->rect.y;
|
if (node->orientation == Orientation::Vertical) {
|
||||||
}
|
// Игнорируем child->localX/Y для LinearVertical
|
||||||
|
currentY -= child->height; // Сдвигаемся вниз на высоту элемента
|
||||||
if (node->type == "LinearLayout") {
|
updateLayout(child, node->screenRect.x, currentY - node->screenRect.y);
|
||||||
std::string orient = node->orientation;
|
currentY -= node->spacing; // Добавляем отступ
|
||||||
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
|
|
||||||
|
|
||||||
float cursorX = node->rect.x;
|
|
||||||
float cursorY = node->rect.y;
|
|
||||||
for (auto& child : node->children) {
|
|
||||||
if (orient == "horizontal") {
|
|
||||||
child->rect.x = cursorX;
|
|
||||||
child->rect.y = node->rect.y;
|
|
||||||
cursorX += child->rect.w + node->spacing;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
child->rect.x = node->rect.x;
|
// Логика для Horizontal...
|
||||||
child->rect.y = cursorY;
|
updateLayout(child, currentX, 0); // упрощенно
|
||||||
cursorY += child->rect.h + node->spacing;
|
currentX += child->width + node->spacing;
|
||||||
}
|
}
|
||||||
layoutNode(child);
|
}
|
||||||
|
else {
|
||||||
|
// FrameLayout: используем честные localX и localY
|
||||||
|
updateLayout(child, node->screenRect.x, node->screenRect.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// После вычисления позиций, нужно обновить меши кнопок (buildMesh)
|
||||||
|
// на основе вычисленных screenRect.
|
||||||
|
if (node->button) {
|
||||||
|
node->button->rect = node->screenRect;
|
||||||
|
node->button->buildMesh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY) {
|
||||||
|
// 1. Базовый расчет позиции текущего узла относительно родителя
|
||||||
|
node->screenRect.x = parentX + node->localX;
|
||||||
|
node->screenRect.y = parentY + node->localY;
|
||||||
|
node->screenRect.w = node->width;
|
||||||
|
node->screenRect.h = node->height;
|
||||||
|
|
||||||
|
if (node->layoutType == LayoutType::Linear) {
|
||||||
|
float totalContentWidth = 0;
|
||||||
|
float totalContentHeight = 0;
|
||||||
|
|
||||||
|
// Предварительный расчет занимаемого места всеми детьми
|
||||||
|
for (size_t i = 0; i < node->children.size(); ++i) {
|
||||||
|
if (node->orientation == Orientation::Vertical) {
|
||||||
|
totalContentHeight += node->children[i]->height;
|
||||||
|
if (i < node->children.size() - 1) totalContentHeight += node->spacing;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
totalContentWidth += node->children[i]->width;
|
||||||
|
if (i < node->children.size() - 1) totalContentWidth += node->spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вычисляем начальные смещения (Offsets)
|
||||||
|
float startX = 0;
|
||||||
|
float startY = node->height; // Начинаем сверху (Y растет вверх)
|
||||||
|
|
||||||
|
// Вертикальное выравнивание всего блока внутри контейнера
|
||||||
|
if (node->orientation == Orientation::Vertical) {
|
||||||
|
if (node->layoutSettings.vAlign == VerticalAlign::Center) {
|
||||||
|
startY = (node->height + totalContentHeight) / 2.0f;
|
||||||
|
}
|
||||||
|
else if (node->layoutSettings.vAlign == VerticalAlign::Bottom) {
|
||||||
|
startY = totalContentHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Горизонтальное выравнивание всего блока
|
||||||
|
if (node->orientation == Orientation::Horizontal) {
|
||||||
|
if (node->layoutSettings.hAlign == HorizontalAlign::Center) {
|
||||||
|
startX = (node->width - totalContentWidth) / 2.0f;
|
||||||
|
}
|
||||||
|
else if (node->layoutSettings.hAlign == HorizontalAlign::Right) {
|
||||||
|
startX = node->width - totalContentWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float cursorX = startX;
|
||||||
|
float cursorY = startY;
|
||||||
|
|
||||||
|
for (auto& child : node->children) {
|
||||||
|
if (node->orientation == Orientation::Vertical) {
|
||||||
|
cursorY -= child->height;
|
||||||
|
|
||||||
|
// Горизонтальное выравнивание внутри строки (Cross-axis alignment)
|
||||||
|
float childX = 0;
|
||||||
|
float freeSpaceX = node->width - child->width;
|
||||||
|
if (node->layoutSettings.hAlign == HorizontalAlign::Center) childX = freeSpaceX / 2.0f;
|
||||||
|
else if (node->layoutSettings.hAlign == HorizontalAlign::Right) childX = freeSpaceX;
|
||||||
|
|
||||||
|
child->localX = childX;
|
||||||
|
child->localY = cursorY;
|
||||||
|
cursorY -= node->spacing;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Вертикальное выравнивание внутри колонки
|
||||||
|
float childY = 0;
|
||||||
|
float freeSpaceY = node->height - child->height;
|
||||||
|
if (node->layoutSettings.vAlign == VerticalAlign::Center) childY = freeSpaceY / 2.0f;
|
||||||
|
else if (node->layoutSettings.vAlign == VerticalAlign::Bottom) childY = 0; // Внизу
|
||||||
|
else childY = freeSpaceY; // Вверху
|
||||||
|
|
||||||
|
child->localX = cursorX;
|
||||||
|
child->localY = childY;
|
||||||
|
cursorX += child->width + node->spacing;
|
||||||
|
}
|
||||||
|
layoutNode(child, node->screenRect.x, node->screenRect.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// Если Frame, просто идем по детям с их фиксированными координатами
|
||||||
for (auto& child : node->children) {
|
for (auto& child : node->children) {
|
||||||
layoutNode(child);
|
layoutNode(child, node->screenRect.x, node->screenRect.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем меши визуальных компонентов
|
||||||
|
syncComponentRects(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiManager::syncComponentRects(const std::shared_ptr<UiNode>& node) {
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
// 1. Обновляем кнопку
|
||||||
|
if (node->button) {
|
||||||
|
node->button->rect = node->screenRect;
|
||||||
|
// Если у кнопки есть анимационные смещения, они учитываются внутри buildMesh
|
||||||
|
// или при рендеринге через Uniform-переменные матрицы модели.
|
||||||
|
node->button->buildMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Обновляем слайдер
|
||||||
|
if (node->slider) {
|
||||||
|
node->slider->rect = node->screenRect;
|
||||||
|
node->slider->buildTrackMesh();
|
||||||
|
node->slider->buildKnobMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Обновляем текстовое поле (TextView)
|
||||||
|
if (node->textView) {
|
||||||
|
node->textView->rect = node->screenRect;
|
||||||
|
// Если в TextView реализован кэш меша для текста, его нужно обновить здесь
|
||||||
|
// node->textView->rebuildText();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Обновляем поле ввода (TextField)
|
||||||
|
if (node->textField) {
|
||||||
|
node->textField->rect = node->screenRect;
|
||||||
|
// Аналогично для курсора и фонового меша
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
|
void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
|
||||||
|
|||||||
@ -31,6 +31,34 @@ namespace ZL {
|
|||||||
Pressed
|
Pressed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class LayoutType {
|
||||||
|
Frame, // Позиционирование по X, Y
|
||||||
|
Linear // Автоматическое позиционирование
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Orientation {
|
||||||
|
Vertical,
|
||||||
|
Horizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class HorizontalAlign {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class VerticalAlign {
|
||||||
|
Top,
|
||||||
|
Center,
|
||||||
|
Bottom
|
||||||
|
};
|
||||||
|
|
||||||
|
// В структуру или класс, отвечающий за LinearLayout (вероятно, это свойства UiNode)
|
||||||
|
struct LayoutSettings {
|
||||||
|
HorizontalAlign hAlign = HorizontalAlign::Left;
|
||||||
|
VerticalAlign vAlign = VerticalAlign::Top;
|
||||||
|
};
|
||||||
|
|
||||||
struct UiButton {
|
struct UiButton {
|
||||||
std::string name;
|
std::string name;
|
||||||
UiRect rect;
|
UiRect rect;
|
||||||
@ -111,21 +139,38 @@ namespace ZL {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct UiNode {
|
struct UiNode {
|
||||||
std::string type;
|
|
||||||
UiRect rect;
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
LayoutType layoutType = LayoutType::Frame;
|
||||||
|
Orientation orientation = Orientation::Vertical;
|
||||||
|
float spacing = 0.0f;
|
||||||
|
|
||||||
|
LayoutSettings layoutSettings;
|
||||||
|
|
||||||
|
// Внутренние вычисленные координаты для OpenGL
|
||||||
|
// Именно их мы передаем в Vertex Buffer при buildMesh()
|
||||||
|
UiRect screenRect;
|
||||||
|
|
||||||
|
// Данные из JSON (желаемые размеры и смещения)
|
||||||
|
float localX = 0;
|
||||||
|
float localY = 0;
|
||||||
|
float width = 0;
|
||||||
|
float height = 0;
|
||||||
|
|
||||||
|
// Иерархия
|
||||||
std::vector<std::shared_ptr<UiNode>> children;
|
std::vector<std::shared_ptr<UiNode>> children;
|
||||||
|
|
||||||
|
// Компоненты (только один из них обычно активен для ноды)
|
||||||
std::shared_ptr<UiButton> button;
|
std::shared_ptr<UiButton> button;
|
||||||
std::shared_ptr<UiSlider> slider;
|
std::shared_ptr<UiSlider> slider;
|
||||||
std::shared_ptr<UiTextView> textView;
|
std::shared_ptr<UiTextView> textView;
|
||||||
std::shared_ptr<UiTextField> textField;
|
std::shared_ptr<UiTextField> textField;
|
||||||
std::string orientation = "vertical";
|
|
||||||
float spacing = 0.0f;
|
|
||||||
|
|
||||||
|
// Анимации
|
||||||
struct AnimStep {
|
struct AnimStep {
|
||||||
std::string type;
|
std::string type;
|
||||||
float toX = 0.0f;
|
float toX = 0.0f;
|
||||||
float toY = 0.0f;
|
float toY = 0.0f;
|
||||||
|
float toScale = 1.0f; // Полезно добавить для UI
|
||||||
float durationMs = 0.0f;
|
float durationMs = 0.0f;
|
||||||
std::string easing = "linear";
|
std::string easing = "linear";
|
||||||
};
|
};
|
||||||
@ -202,7 +247,9 @@ namespace ZL {
|
|||||||
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
|
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void layoutNode(const std::shared_ptr<UiNode>& node);
|
void updateLayout(const std::shared_ptr<UiNode>& node, float parentX, float parentY);
|
||||||
|
void layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY);
|
||||||
|
void syncComponentRects(const std::shared_ptr<UiNode>& node);
|
||||||
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
|
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
|
||||||
|
|
||||||
struct ActiveAnim {
|
struct ActiveAnim {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user