space-game001/src/UiManager.h
Vladislav Khorev ffbecbbcde Working on ui
2026-02-28 22:21:08 +03:00

307 lines
8.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include "render/Renderer.h"
#include "render/TextureManager.h"
#include "render/TextRenderer.h"
#include "Environment.h"
#include "external/nlohmann/json.hpp"
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include <map>
namespace ZL {
using json = nlohmann::json;
struct UiRect {
float x = 0;
float y = 0;
float w = 0;
float h = 0;
bool contains(float px, float py) const {
return px >= x && px <= x + w && py >= y && py <= y + h;
}
};
enum class ButtonState {
Normal,
Hover,
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 {
std::string name;
UiRect rect;
std::shared_ptr<Texture> texNormal;
std::shared_ptr<Texture> texHover;
std::shared_ptr<Texture> texPressed;
ButtonState state = ButtonState::Normal;
VertexRenderStruct mesh;
std::function<void(const std::string&)> onClick;
// animation runtime
float animOffsetX = 0.0f;
float animOffsetY = 0.0f;
float animScaleX = 1.0f;
float animScaleY = 1.0f;
void buildMesh();
void draw(Renderer& renderer) const;
};
struct UiSlider {
std::string name;
UiRect rect;
std::shared_ptr<Texture> texTrack;
std::shared_ptr<Texture> texKnob;
VertexRenderStruct trackMesh;
VertexRenderStruct knobMesh;
float value = 0.0f;
bool vertical = true;
std::function<void(const std::string&, float)> onValueChanged;
void buildTrackMesh();
void buildKnobMesh();
void draw(Renderer& renderer) const;
};
struct UiTextView {
std::string name;
UiRect rect;
std::string text = "";
std::string fontPath = "resources/fonts/DroidSans.ttf";
int fontSize = 32;
std::array<float, 4> color = { 1.f, 1.f, 1.f, 1.f }; // rgba
bool centered = true;
std::unique_ptr<TextRenderer> textRenderer;
void draw(Renderer& renderer) const {
if (textRenderer) {
textRenderer->drawText(text, rect.x + rect.w / 2, rect.y + rect.h / 2, 1.0f, centered, color);
}
}
};
struct UiTextField {
std::string name;
UiRect rect;
std::string text = "";
std::string placeholder = "";
std::string fontPath = "resources/fonts/DroidSans.ttf";
int fontSize = 32;
std::array<float, 4> color = { 1.f, 1.f, 1.f, 1.f };
std::array<float, 4> placeholderColor = { 0.5f, 0.5f, 0.5f, 1.f };
std::array<float, 4> backgroundColor = { 0.2f, 0.2f, 0.2f, 1.f };
std::array<float, 4> borderColor = { 0.5f, 0.5f, 0.5f, 1.f };
int maxLength = 256;
bool focused = false;
std::unique_ptr<TextRenderer> textRenderer;
std::function<void(const std::string&, const std::string&)> onTextChanged;
void draw(Renderer& renderer) const;
};
struct UiNode {
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::shared_ptr<UiButton> button;
std::shared_ptr<UiSlider> slider;
std::shared_ptr<UiTextView> textView;
std::shared_ptr<UiTextField> textField;
// Анимации
struct AnimStep {
std::string type;
float toX = 0.0f;
float toY = 0.0f;
float toScale = 1.0f; // Полезно добавить для UI
float durationMs = 0.0f;
std::string easing = "linear";
};
struct AnimSequence {
std::vector<AnimStep> steps;
bool repeat = false;
};
std::map<std::string, AnimSequence> animations;
};
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
std::shared_ptr<UiNode> loadUiFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
class UiManager {
public:
UiManager() = default;
void replaceRoot(std::shared_ptr<UiNode> newRoot);
void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
void draw(Renderer& renderer);
void onMouseMove(int x, int y);
void onMouseDown(int x, int y);
void onMouseUp(int x, int y);
void onKeyPress(unsigned char key);
void onKeyBackspace();
bool isUiInteraction() const {
return pressedButton != nullptr || pressedSlider != nullptr || focusedTextField != nullptr;
}
void stopAllAnimations() {
nodeActiveAnims.clear();
for (auto& b : buttons) {
if (b) {
b->animOffsetX = 0.0f;
b->animOffsetY = 0.0f;
b->animScaleX = 1.0f;
b->animScaleY = 1.0f;
}
}
}
std::shared_ptr<UiButton> findButton(const std::string& name);
bool setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb);
bool addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
const std::string& trackPath, const std::string& knobPath, float initialValue = 0.0f, bool vertical = true);
std::shared_ptr<UiSlider> findSlider(const std::string& name);
bool setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb);
bool setSliderValue(const std::string& name, float value); // programmatic set (clamped 0..1)
std::shared_ptr<UiTextView> findTextView(const std::string& name);
bool setText(const std::string& name, const std::string& newText);
std::shared_ptr<UiTextField> findTextField(const std::string& name);
bool setTextFieldCallback(const std::string& name, std::function<void(const std::string&, const std::string&)> cb);
std::string getTextFieldValue(const std::string& name);
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
bool pushMenuFromSavedRoot(std::shared_ptr<UiNode> newRoot);
bool popMenu();
void clearMenuStack();
void update(float deltaMs);
void startAnimation(const std::string& animName);
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);
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 syncComponentRects(const std::shared_ptr<UiNode>& node);
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
struct ActiveAnim {
std::string name;
const UiNode::AnimSequence* seq = nullptr;
size_t stepIndex = 0;
float elapsedMs = 0.0f;
bool repeat = false;
float startOffsetX = 0.0f;
float startOffsetY = 0.0f;
float endOffsetX = 0.0f;
float endOffsetY = 0.0f;
float startScaleX = 1.0f;
float startScaleY = 1.0f;
float endScaleX = 1.0f;
float endScaleY = 1.0f;
std::function<void()> onComplete;
float origOffsetX = 0.0f;
float origOffsetY = 0.0f;
float origScaleX = 1.0f;
float origScaleY = 1.0f;
bool stepStarted = false;
};
std::shared_ptr<UiNode> root;
std::vector<std::shared_ptr<UiButton>> buttons;
std::vector<std::shared_ptr<UiSlider>> sliders;
std::vector<std::shared_ptr<UiTextView>> textViews;
std::vector<std::shared_ptr<UiTextField>> textFields;
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
std::shared_ptr<UiButton> pressedButton;
std::shared_ptr<UiSlider> pressedSlider;
std::shared_ptr<UiTextField> focusedTextField;
struct MenuState {
std::shared_ptr<UiNode> root;
std::vector<std::shared_ptr<UiButton>> buttons;
std::vector<std::shared_ptr<UiSlider>> sliders;
std::vector<std::shared_ptr<UiTextField>> textFields;
std::shared_ptr<UiButton> pressedButton;
std::shared_ptr<UiSlider> pressedSlider;
std::shared_ptr<UiTextField> focusedTextField;
std::string path;
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks;
};
std::vector<MenuState> menuStack;
};
} // namespace ZL