Working on UI for web
This commit is contained in:
parent
ffbecbbcde
commit
6b4b549b3c
@ -1,114 +1,103 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"children": [
|
||||
{
|
||||
"type": "LinearLayout",
|
||||
"name": "settingsButtons",
|
||||
"orientation": "vertical",
|
||||
"vertical_align" : "center",
|
||||
"horizontal_align" : "center",
|
||||
"spacing": 10,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"children": [
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "titleBtn",
|
||||
"width": 254,
|
||||
"height": 35,
|
||||
"textures": {
|
||||
"type": "LinearLayout",
|
||||
"orientation": "vertical",
|
||||
"vertical_align": "center",
|
||||
"horizontal_align": "center",
|
||||
"spacing": 10,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": "match_parent",
|
||||
"height": "match_parent",
|
||||
"children": [
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "titleBtn",
|
||||
"width": 254,
|
||||
"height": 35,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/title.png",
|
||||
"hover": "resources/main_menu/title.png",
|
||||
"pressed": "resources/main_menu/title.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "underlineBtn",
|
||||
"width": 168,
|
||||
"height": 44,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "underlineBtn",
|
||||
"width": 168,
|
||||
"height": 44,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/line.png",
|
||||
"hover": "resources/main_menu/line.png",
|
||||
"pressed": "resources/main_menu/line.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "subtitleBtn",
|
||||
"width": 144,
|
||||
"height": 11,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "subtitleBtn",
|
||||
"width": 144,
|
||||
"height": 11,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/subtitle.png",
|
||||
"hover": "resources/main_menu/subtitle.png",
|
||||
"pressed": "resources/main_menu/subtitle.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "singleButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "singleButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/single.png",
|
||||
"hover": "resources/main_menu/single.png",
|
||||
"pressed": "resources/main_menu/single.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "multiplayerButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "multiplayerButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/multi.png",
|
||||
"hover": "resources/main_menu/multi.png",
|
||||
"pressed": "resources/main_menu/multi.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "multiplayerButton2",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "multiplayerButton2",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/multi.png",
|
||||
"hover": "resources/main_menu/multi.png",
|
||||
"pressed": "resources/main_menu/multi.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "exitButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "exitButton",
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/exit.png",
|
||||
"hover": "resources/main_menu/exit.png",
|
||||
"pressed": "resources/main_menu/exit.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "versionLabel",
|
||||
"width": 81,
|
||||
"height": 9,
|
||||
"textures": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "versionLabel",
|
||||
"width": 81,
|
||||
"height": 9,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/version.png",
|
||||
"hover": "resources/main_menu/version.png",
|
||||
"pressed": "resources/main_menu/version.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@ -342,6 +342,7 @@ namespace ZL
|
||||
Environment::width = event.window.data1;
|
||||
Environment::height = event.window.data2;
|
||||
Environment::computeProjectionDimensions();
|
||||
menuManager.uiManager.updateAllLayouts();
|
||||
std::cout << "Window resized: " << Environment::width << "x" << Environment::height << std::endl;
|
||||
|
||||
space.clearTextRendererCache();
|
||||
|
||||
@ -362,7 +362,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
crosshairCfgLoaded = loadCrosshairConfig("resources/config/crosshair_config.json");
|
||||
std::cerr << "[Crosshair] loaded=" << crosshairCfgLoaded
|
||||
std::cout << "[Crosshair] loaded=" << crosshairCfgLoaded
|
||||
<< " enabled=" << crosshairCfg.enabled
|
||||
<< " w=" << Environment::width << " h=" << Environment::height
|
||||
<< " alpha=" << crosshairCfg.alpha
|
||||
|
||||
@ -201,9 +201,31 @@ namespace ZL {
|
||||
// так как 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);
|
||||
|
||||
if (j.contains("width")) {
|
||||
if (j["width"].is_string() && j["width"] == "match_parent") {
|
||||
node->width = -1.0f; // Наш маркер для match_parent
|
||||
}
|
||||
else {
|
||||
node->width = j["width"].get<float>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node->width = 0.0f;
|
||||
}
|
||||
if (j.contains("height")) {
|
||||
if (j["height"].is_string() && j["height"] == "match_parent") {
|
||||
node->height = -1.0f; // Наш маркер для match_parent
|
||||
}
|
||||
else {
|
||||
node->height = j["height"].get<float>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node->height = 0.0f;
|
||||
}
|
||||
|
||||
// 3. Параметры компоновки
|
||||
if (j.contains("orientation")) {
|
||||
std::string orient = j["orientation"].get<std::string>();
|
||||
@ -220,14 +242,13 @@ namespace ZL {
|
||||
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;
|
||||
else if (valign == "bottom") node->layoutSettings.vAlign = VerticalAlign::Bottom;
|
||||
}
|
||||
// Подготавливаем базовый rect для компонентов (кнопок и т.д.)
|
||||
// На этапе парсинга мы даем им "желаемый" размер
|
||||
UiRect initialRect = { node->localX, node->localY, node->width, node->height };
|
||||
|
||||
|
||||
|
||||
if (typeStr == "Button") {
|
||||
auto btn = std::make_shared<UiButton>();
|
||||
btn->name = node->name;
|
||||
@ -441,7 +462,7 @@ namespace ZL {
|
||||
|
||||
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
|
||||
root = newRoot;
|
||||
layoutNode(root, 0, 0);
|
||||
layoutNode(root, 0, 0, Environment::projectionWidth, Environment::projectionHeight);
|
||||
buttons.clear();
|
||||
sliders.clear();
|
||||
textViews.clear();
|
||||
@ -465,55 +486,18 @@ namespace ZL {
|
||||
replaceRoot(newRoot);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UiManager::updateLayout(const std::shared_ptr<UiNode>& node, float parentX, float parentY) {
|
||||
// 1. Сначала определяем позицию самого контейнера
|
||||
// Если это корень, он обычно занимает весь экран или заданные координаты
|
||||
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY, float parentW, float parentH) {
|
||||
node->screenRect.w = (node->width < 0) ? parentW : node->width;
|
||||
node->screenRect.h = (node->height < 0) ? parentH : node->height;
|
||||
|
||||
// 2. Позиция относительно родителя
|
||||
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) {
|
||||
if (node->layoutType == LayoutType::Linear) {
|
||||
if (node->orientation == Orientation::Vertical) {
|
||||
// Игнорируем child->localX/Y для LinearVertical
|
||||
currentY -= child->height; // Сдвигаемся вниз на высоту элемента
|
||||
updateLayout(child, node->screenRect.x, currentY - node->screenRect.y);
|
||||
currentY -= node->spacing; // Добавляем отступ
|
||||
}
|
||||
else {
|
||||
// Логика для Horizontal...
|
||||
updateLayout(child, currentX, 0); // упрощенно
|
||||
currentX += child->width + node->spacing;
|
||||
}
|
||||
}
|
||||
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;
|
||||
// Используем удобные локальные переменные для расчетов детей
|
||||
float currentW = node->screenRect.w;
|
||||
float currentH = node->screenRect.h;
|
||||
|
||||
if (node->layoutType == LayoutType::Linear) {
|
||||
float totalContentWidth = 0;
|
||||
@ -531,14 +515,12 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
// Вычисляем начальные смещения (Offsets)
|
||||
float startX = 0;
|
||||
float startY = node->height; // Начинаем сверху (Y растет вверх)
|
||||
float startY = currentH;
|
||||
|
||||
// Вертикальное выравнивание всего блока внутри контейнера
|
||||
if (node->orientation == Orientation::Vertical) {
|
||||
if (node->layoutSettings.vAlign == VerticalAlign::Center) {
|
||||
startY = (node->height + totalContentHeight) / 2.0f;
|
||||
startY = (currentH + totalContentHeight) / 2.0f;
|
||||
}
|
||||
else if (node->layoutSettings.vAlign == VerticalAlign::Bottom) {
|
||||
startY = totalContentHeight;
|
||||
@ -548,10 +530,10 @@ namespace ZL {
|
||||
// Горизонтальное выравнивание всего блока
|
||||
if (node->orientation == Orientation::Horizontal) {
|
||||
if (node->layoutSettings.hAlign == HorizontalAlign::Center) {
|
||||
startX = (node->width - totalContentWidth) / 2.0f;
|
||||
startX = (currentW - totalContentWidth) / 2.0f;
|
||||
}
|
||||
else if (node->layoutSettings.hAlign == HorizontalAlign::Right) {
|
||||
startX = node->width - totalContentWidth;
|
||||
startX = currentW - totalContentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,12 +541,15 @@ namespace ZL {
|
||||
float cursorY = startY;
|
||||
|
||||
for (auto& child : node->children) {
|
||||
if (node->orientation == Orientation::Vertical) {
|
||||
cursorY -= child->height;
|
||||
|
||||
// Горизонтальное выравнивание внутри строки (Cross-axis alignment)
|
||||
float childW = (child->width < 0) ? currentW : child->width;
|
||||
float childH = (child->height < 0) ? currentH : child->height;
|
||||
|
||||
if (node->orientation == Orientation::Vertical) {
|
||||
cursorY -= childH; // используем вычисленный childH
|
||||
|
||||
float childX = 0;
|
||||
float freeSpaceX = node->width - child->width;
|
||||
float freeSpaceX = currentW - childW;
|
||||
if (node->layoutSettings.hAlign == HorizontalAlign::Center) childX = freeSpaceX / 2.0f;
|
||||
else if (node->layoutSettings.hAlign == HorizontalAlign::Right) childX = freeSpaceX;
|
||||
|
||||
@ -573,24 +558,34 @@ namespace ZL {
|
||||
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;
|
||||
|
||||
// Вертикальное выравнивание внутри "строки" (Cross-axis alignment)
|
||||
float childY = 0;
|
||||
float freeSpaceY = currentH - childH;
|
||||
|
||||
if (node->layoutSettings.vAlign == VerticalAlign::Center) {
|
||||
childY = freeSpaceY / 2.0f;
|
||||
}
|
||||
else if (node->layoutSettings.vAlign == VerticalAlign::Top) {
|
||||
childY = freeSpaceY; // Прижимаем к верхнему краю (т.к. Y растет вверх)
|
||||
}
|
||||
else if (node->layoutSettings.vAlign == VerticalAlign::Bottom) {
|
||||
childY = 0; // Прижимаем к нижнему краю
|
||||
}
|
||||
|
||||
child->localY = childY;
|
||||
cursorX += child->width + node->spacing;
|
||||
|
||||
// Сдвигаем курсор вправо для следующего элемента
|
||||
cursorX += childW + node->spacing;
|
||||
}
|
||||
layoutNode(child, node->screenRect.x, node->screenRect.y);
|
||||
layoutNode(child, node->screenRect.x, node->screenRect.y, currentW, currentH);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Если Frame, просто идем по детям с их фиксированными координатами
|
||||
for (auto& child : node->children) {
|
||||
layoutNode(child, node->screenRect.x, node->screenRect.y);
|
||||
layoutNode(child, node->screenRect.x, node->screenRect.y, node->width, node->height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,6 +625,13 @@ namespace ZL {
|
||||
}
|
||||
}
|
||||
|
||||
void UiManager::updateAllLayouts() {
|
||||
if (!root) return;
|
||||
|
||||
// Запускаем расчет от корня, передавая размеры экрана как "родительские"
|
||||
layoutNode(root, 0, 0, Environment::projectionWidth, Environment::projectionHeight);
|
||||
}
|
||||
|
||||
void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
|
||||
if (node->button) {
|
||||
buttons.push_back(node->button);
|
||||
|
||||
@ -245,10 +245,10 @@ namespace ZL {
|
||||
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||
bool stopAnimationOnNode(const std::string& nodeName, const std::string& animName);
|
||||
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
|
||||
void updateAllLayouts();
|
||||
|
||||
private:
|
||||
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 layoutNode(const std::shared_ptr<UiNode>& node, float parentX, float parentY, float parentW, float parentH);
|
||||
void syncComponentRects(const std::shared_ptr<UiNode>& node);
|
||||
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user