add UiSlider and some fix

This commit is contained in:
Vlad 2025-12-23 13:37:54 +06:00
parent bee2fff538
commit d57112be35
5 changed files with 398 additions and 112 deletions

187
Game.cpp
View File

@ -167,31 +167,37 @@ namespace ZL
uiManager.setButtonCallback("playButton", [this](const std::string& name) { uiManager.setButtonCallback("playButton", [this](const std::string& name) {
std::cerr << "Play button pressed: " << name << std::endl; std::cerr << "Play button pressed: " << name << std::endl;
}); });
uiManager.setButtonCallback("exitButton", [](const std::string& name) { uiManager.setButtonCallback("exitButton", [](const std::string& name) {
Environment::exitGameLoop = true; Environment::exitGameLoop = true;
}); });
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) { uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
if (uiManager.pushMenuFromFile("../config/settings.json", this->renderer, CONST_ZIP_FILE)) { if (uiManager.pushMenuFromFile("../config/settings.json", this->renderer, CONST_ZIP_FILE)) {
uiManager.setButtonCallback("Opt1", [this](const std::string& n) { uiManager.setButtonCallback("Opt1", [this](const std::string& n) {
std::cerr << "Opt1 pressed: " << n << std::endl; std::cerr << "Opt1 pressed: " << n << std::endl;
}); });
uiManager.setButtonCallback("Opt2", [this](const std::string& n) { uiManager.setButtonCallback("Opt2", [this](const std::string& n) {
std::cerr << "Opt2 pressed: " << n << std::endl; std::cerr << "Opt2 pressed: " << n << std::endl;
}); });
uiManager.setButtonCallback("backButton", [this](const std::string& n) { uiManager.setButtonCallback("backButton", [this](const std::string& n) {
uiManager.popMenu(); uiManager.popMenu();
}); });
} }
else { else {
std::cerr << "Failed to open settings menu" << std::endl; std::cerr << "Failed to open settings menu" << std::endl;
} }
}); });
uiManager.setSliderCallback("musicVolumeSlider", [this](const std::string& name, float value) {
std::cerr << "Music volume slider changed to: " << value << std::endl;
musicVolume = value;
Environment::shipVelocity = musicVolume * 20.0f;
});
cubemapTexture = std::make_shared<Texture>( cubemapTexture = std::make_shared<Texture>(
std::array<TextureDataStruct, 6>{ std::array<TextureDataStruct, 6>{
@ -237,62 +243,62 @@ namespace ZL
} }
/* buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE)); /* buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
button.data.PositionData.push_back({ 100, 100, 0 }); button.data.PositionData.push_back({ 100, 100, 0 });
button.data.PositionData.push_back({ 100, 150, 0 }); button.data.PositionData.push_back({ 100, 150, 0 });
button.data.PositionData.push_back({ 300, 150, 0 }); button.data.PositionData.push_back({ 300, 150, 0 });
button.data.PositionData.push_back({ 100, 100, 0 }); button.data.PositionData.push_back({ 100, 100, 0 });
button.data.PositionData.push_back({ 300, 150, 0 }); button.data.PositionData.push_back({ 300, 150, 0 });
button.data.PositionData.push_back({ 300, 100, 0 }); button.data.PositionData.push_back({ 300, 100, 0 });
button.data.TexCoordData.push_back({ 0,0 }); button.data.TexCoordData.push_back({ 0,0 });
button.data.TexCoordData.push_back({ 0,1 }); button.data.TexCoordData.push_back({ 0,1 });
button.data.TexCoordData.push_back({ 1,1 }); button.data.TexCoordData.push_back({ 1,1 });
button.data.TexCoordData.push_back({ 0,0 }); button.data.TexCoordData.push_back({ 0,0 });
button.data.TexCoordData.push_back({ 1,1 }); button.data.TexCoordData.push_back({ 1,1 });
button.data.TexCoordData.push_back({ 1,0 }); button.data.TexCoordData.push_back({ 1,0 });
button.RefreshVBO();*/ button.RefreshVBO();*/
/*
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE));
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE)); musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 }); musicVolumeBar.data.TexCoordData.push_back({ 0,1 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 }); musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 }); musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 }); musicVolumeBar.data.TexCoordData.push_back({ 1,0 });
musicVolumeBar.data.TexCoordData.push_back({ 0,0 }); musicVolumeBar.RefreshVBO();
musicVolumeBar.data.TexCoordData.push_back({ 0,1 });
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.TexCoordData.push_back({ 1,0 });
musicVolumeBar.RefreshVBO();
musicVolumeBarButtonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE)); musicVolumeBarButtonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE));
float musicVolumeBarButtonButtonCenterY = 350.0f; float musicVolumeBarButtonButtonCenterY = 350.0f;
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 }); musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 }); musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 }); musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 }); musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 }); musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 });
musicVolumeBarButton.RefreshVBO(); musicVolumeBarButton.RefreshVBO();*/
renderer.InitOpenGL(); renderer.InitOpenGL();
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -412,7 +418,7 @@ namespace ZL
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(); CheckGlError();
} }
void Game::UpdateVolumeKnob() { /*void Game::UpdateVolumeKnob() {
float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY); float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY);
auto& pos = musicVolumeBarButton.data.PositionData; auto& pos = musicVolumeBarButton.data.PositionData;
@ -427,7 +433,6 @@ namespace ZL
musicVolumeBarButton.RefreshVBO(); musicVolumeBarButton.RefreshVBO();
} }
void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) { void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) {
int uiX = mouseX; int uiX = mouseX;
@ -443,7 +448,8 @@ namespace ZL
if (t > 1.0f) t = 1.0f; if (t > 1.0f) t = 1.0f;
musicVolume = t; musicVolume = t;
UpdateVolumeKnob(); UpdateVolumeKnob();
} }*/
void Game::drawUI() void Game::drawUI()
{ {
static const std::string defaultShaderName = "default"; static const std::string defaultShaderName = "default";
@ -452,7 +458,6 @@ namespace ZL
static const std::string vTexCoordName = "vTexCoord"; static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture"; static const std::string textureUniformName = "Texture";
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
renderer.shaderManager.PushShader(defaultShaderName); renderer.shaderManager.PushShader(defaultShaderName);
@ -460,22 +465,24 @@ namespace ZL
renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName); renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); //renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
renderer.PushMatrix(); //renderer.PushMatrix();
renderer.LoadIdentity(); //renderer.LoadIdentity();
//glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID()); //glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
//renderer.DrawVertexRenderStruct(button); //renderer.DrawVertexRenderStruct(button);
glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
renderer.DrawVertexRenderStruct(musicVolumeBar);
glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID()); //glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
renderer.DrawVertexRenderStruct(musicVolumeBarButton); //renderer.DrawVertexRenderStruct(musicVolumeBar);
//glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID());
//renderer.DrawVertexRenderStruct(musicVolumeBarButton);
//renderer.PopMatrix();
//renderer.PopProjectionMatrix();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName); renderer.DisableVertexAttribArray(vTexCoordName);
uiManager.draw(renderer); uiManager.draw(renderer);
@ -602,36 +609,39 @@ namespace ZL
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) { if (event.type == SDL_QUIT) {
Environment::exitGameLoop = true; Environment::exitGameLoop = true;
} }
else if (event.type == SDL_MOUSEBUTTONDOWN) { else if (event.type == SDL_MOUSEBUTTONDOWN) {
// 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè // 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè
int mx = event.button.x; int mx = event.button.x;
int my = event.button.y; int my = event.button.y;
std::cout << mx << " " << my << '\n';
int uiX = mx; int uiX = mx;
int uiY = Environment::height - my; int uiY = Environment::height - my;
uiManager.onMouseMove(uiX, uiY);
uiManager.onMouseDown(uiX, uiY); uiManager.onMouseDown(uiX, uiY);
if (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 && bool uiHandled = false;
uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) {
isDraggingVolume = true; for (const auto& button : uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
UpdateVolumeFromMouse(mx, my); (void)button;
}
else {
Environment::tapDownHold = true;
// Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
Environment::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y;
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y;
} }
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
for (const auto& slider : uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<std::shared_ptr<UiSlider>>{}) {
(void)slider;
}
return nullptr;
}();
if (!uiManager.isUiInteraction()) {
Environment::tapDownHold = true;
// Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
Environment::tapDownStartPos.v[0] = mx;
Environment::tapDownStartPos.v[1] = my;
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
Environment::tapDownCurrentPos.v[0] = mx;
Environment::tapDownCurrentPos.v[1] = my;
}
} }
else if (event.type == SDL_MOUSEBUTTONUP) { else if (event.type == SDL_MOUSEBUTTONUP) {
// 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè // 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
@ -642,27 +652,22 @@ namespace ZL
uiManager.onMouseUp(uiX, uiY); uiManager.onMouseUp(uiX, uiY);
isDraggingVolume = false; if (!uiManager.isUiInteraction()) {
Environment::tapDownHold = false; Environment::tapDownHold = false;
}
} }
else if (event.type == SDL_MOUSEMOTION) { else if (event.type == SDL_MOUSEMOTION) {
// 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè // 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
int mx = event.motion.x; int mx = event.motion.x;
int my = event.motion.y; int my = event.motion.y;
int uiX = mx; int uiX = mx;
int uiY = Environment::height - my; int uiY = Environment::height - my;
uiManager.onMouseMove(uiX, uiY); uiManager.onMouseMove(uiX, uiY);
if (isDraggingVolume) { if (Environment::tapDownHold && !uiManager.isUiInteraction()) {
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà Environment::tapDownCurrentPos.v[0] = mx;
UpdateVolumeFromMouse(mx, my); Environment::tapDownCurrentPos.v[1] = my;
}
if (Environment::tapDownHold) {
// Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ
Environment::tapDownCurrentPos.v[0] = event.motion.x;
Environment::tapDownCurrentPos.v[1] = event.motion.y;
} }
} }
else if (event.type == SDL_MOUSEWHEEL) { else if (event.type == SDL_MOUSEWHEEL) {

24
Game.h
View File

@ -50,26 +50,28 @@ namespace ZL {
std::vector<VertexRenderStruct> boxRenderArr; std::vector<VertexRenderStruct> boxRenderArr;
std::shared_ptr<Texture> buttonTexture; //std::shared_ptr<Texture> buttonTexture;
VertexRenderStruct button; //VertexRenderStruct button;
std::shared_ptr<Texture> musicVolumeBarTexture; //std::shared_ptr<Texture> musicVolumeBarTexture;
VertexRenderStruct musicVolumeBar; //VertexRenderStruct musicVolumeBar;
std::shared_ptr<Texture> musicVolumeBarButtonTexture; //std::shared_ptr<Texture> musicVolumeBarButtonTexture;
VertexRenderStruct musicVolumeBarButton; //VertexRenderStruct musicVolumeBarButton;
bool isDraggingVolume = false; //bool isDraggingVolume = false;
float musicVolume = 0.0f; float musicVolume = 0.0f;
float volumeBarMinX = 1190.0f; float volumeBarMinX = 1190.0f;
float volumeBarMaxX = 1200.0f; float volumeBarMaxX = 1200.0f;
float volumeBarMinY = 100.0f; float volumeBarMinY = 100.0f;
float volumeBarMaxY = 600.0f; float volumeBarMaxY = 600.0f;
float musicVolumeBarButtonButtonCenterX = 1195.0f; //float musicVolumeBarButtonButtonCenterX = 1195.0f;
float musicVolumeBarButtonButtonRadius = 25.0f; //float musicVolumeBarButtonButtonRadius = 25.0f;
void UpdateVolumeFromMouse(int mouseX, int mouseY); //void UpdateVolumeFromMouse(int mouseX, int mouseY);
void UpdateVolumeKnob(); //void UpdateVolumeKnob();
static const size_t CONST_TIMER_INTERVAL = 10; static const size_t CONST_TIMER_INTERVAL = 10;
static const size_t CONST_MAX_TIME_INTERVAL = 1000; static const size_t CONST_MAX_TIME_INTERVAL = 1000;

View File

@ -63,7 +63,94 @@ namespace ZL {
renderer.DisableVertexAttribArray(vTexCoordName); renderer.DisableVertexAttribArray(vTexCoordName);
} }
// UiManager implementation void UiSlider::buildTrackMesh() {
trackMesh.data.PositionData.clear();
trackMesh.data.TexCoordData.clear();
float x0 = rect.x;
float y0 = rect.y;
float x1 = rect.x + rect.w;
float y1 = rect.y + rect.h;
trackMesh.data.PositionData.push_back({ x0, y0, 0 });
trackMesh.data.TexCoordData.push_back({ 0, 0 });
trackMesh.data.PositionData.push_back({ x0, y1, 0 });
trackMesh.data.TexCoordData.push_back({ 0, 1 });
trackMesh.data.PositionData.push_back({ x1, y1, 0 });
trackMesh.data.TexCoordData.push_back({ 1, 1 });
trackMesh.data.PositionData.push_back({ x0, y0, 0 });
trackMesh.data.TexCoordData.push_back({ 0, 0 });
trackMesh.data.PositionData.push_back({ x1, y1, 0 });
trackMesh.data.TexCoordData.push_back({ 1, 1 });
trackMesh.data.PositionData.push_back({ x1, y0, 0 });
trackMesh.data.TexCoordData.push_back({ 1, 0 });
trackMesh.RefreshVBO();
}
void UiSlider::buildKnobMesh() {
knobMesh.data.PositionData.clear();
knobMesh.data.TexCoordData.clear();
float kw = vertical ? rect.w * 4.0f : rect.w * 0.5f;
float kh = vertical ? rect.w * 4.0f : rect.h * 0.5f;
float cx = rect.x + rect.w * 0.5f;
float cy = rect.y + (vertical ? (value * rect.h) : (rect.h * 0.5f));
float x0 = cx - kw * 0.5f;
float y0 = cy - kh * 0.5f;
float x1 = cx + kw * 0.5f;
float y1 = cy + kh * 0.5f;
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
knobMesh.data.TexCoordData.push_back({ 0, 0 });
knobMesh.data.PositionData.push_back({ x0, y1, 0 });
knobMesh.data.TexCoordData.push_back({ 0, 1 });
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
knobMesh.data.TexCoordData.push_back({ 1, 1 });
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
knobMesh.data.TexCoordData.push_back({ 0, 0 });
knobMesh.data.PositionData.push_back({ x1, y1, 0 });
knobMesh.data.TexCoordData.push_back({ 1, 1 });
knobMesh.data.PositionData.push_back({ x1, y0, 0 });
knobMesh.data.TexCoordData.push_back({ 1, 0 });
knobMesh.RefreshVBO();
}
void UiSlider::draw(Renderer& renderer) const {
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
if (texTrack) {
glBindTexture(GL_TEXTURE_2D, texTrack->getTexID());
renderer.DrawVertexRenderStruct(trackMesh);
}
if (texKnob) {
glBindTexture(GL_TEXTURE_2D, texKnob->getTexID());
renderer.DrawVertexRenderStruct(knobMesh);
}
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
}
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
std::ifstream in(path); std::ifstream in(path);
if (!in.is_open()) { if (!in.is_open()) {
@ -88,11 +175,16 @@ namespace ZL {
root = parseNode(j["root"], renderer, zipFile); root = parseNode(j["root"], renderer, zipFile);
layoutNode(root); layoutNode(root);
buttons.clear(); buttons.clear();
collectButtons(root); sliders.clear();
collectButtonsAndSliders(root);
for (auto& b : buttons) { for (auto& b : buttons) {
b->buildMesh(); b->buildMesh();
} }
for (auto& s : sliders) {
s->buildTrackMesh();
s->buildKnobMesh();
}
} }
std::shared_ptr<UiNode> UiManager::parseNode(const json& j, Renderer& renderer, const std::string& zipFile) { std::shared_ptr<UiNode> UiManager::parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
@ -137,8 +229,42 @@ namespace ZL {
node->button = btn; node->button = btn;
} }
else if (node->type == "Slider") {
auto s = std::make_shared<UiSlider>();
s->name = node->name;
s->rect = node->rect;
if (!j.contains("textures") || !j["textures"].is_object()) {
std::cerr << "UiManager: Slider '" << s->name << "' missing textures" << std::endl;
throw std::runtime_error("UI slider textures missing");
}
auto t = j["textures"];
auto loadTex = [&](const std::string& key)->std::shared_ptr<Texture> {
if (!t.contains(key) || !t[key].is_string()) return nullptr;
std::string path = t[key].get<std::string>();
try {
auto data = CreateTextureDataFromPng(path.c_str(), zipFile.c_str());
return std::make_shared<Texture>(data);
}
catch (const std::exception& e) {
std::cerr << "UiManager: failed load texture " << path << " : " << e.what() << std::endl;
throw std::runtime_error("UI texture load failed: " + path);
}
};
s->texTrack = loadTex("track");
s->texKnob = loadTex("knob");
if (j.contains("value")) s->value = j["value"].get<float>();
if (j.contains("orientation")) {
std::string orient = j["orientation"].get<std::string>();
std::transform(orient.begin(), orient.end(), orient.begin(), ::tolower);
s->vertical = (orient != "horizontal");
}
node->slider = s;
}
// parse children
if (j.contains("children") && j["children"].is_array()) { if (j.contains("children") && j["children"].is_array()) {
for (const auto& ch : j["children"]) { for (const auto& ch : j["children"]) {
node->children.push_back(parseNode(ch, renderer, zipFile)); node->children.push_back(parseNode(ch, renderer, zipFile));
@ -181,11 +307,14 @@ namespace ZL {
} }
} }
void UiManager::collectButtons(const std::shared_ptr<UiNode>& node) { void UiManager::collectButtonsAndSliders(const std::shared_ptr<UiNode>& node) {
if (node->button) { if (node->button) {
buttons.push_back(node->button); buttons.push_back(node->button);
} }
for (auto& c : node->children) collectButtons(c); if (node->slider) {
sliders.push_back(node->slider);
}
for (auto& c : node->children) collectButtonsAndSliders(c);
} }
bool UiManager::setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb) { bool UiManager::setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb) {
@ -198,11 +327,70 @@ namespace ZL {
return true; return true;
} }
bool UiManager::addSlider(const std::string& name, const UiRect& rect, Renderer& renderer, const std::string& zipFile,
const std::string& trackPath, const std::string& knobPath, float initialValue, bool vertical) {
auto s = std::make_shared<UiSlider>();
s->name = name;
s->rect = rect;
s->value = std::clamp(initialValue, 0.0f, 1.0f);
s->vertical = vertical;
try {
if (!trackPath.empty()) {
auto data = CreateTextureDataFromPng(trackPath.c_str(), zipFile.c_str());
s->texTrack = std::make_shared<Texture>(data);
}
if (!knobPath.empty()) {
auto data = CreateTextureDataFromPng(knobPath.c_str(), zipFile.c_str());
s->texKnob = std::make_shared<Texture>(data);
}
}
catch (const std::exception& e) {
std::cerr << "UiManager: addSlider failed to load textures: " << e.what() << std::endl;
return false;
}
s->buildTrackMesh();
s->buildKnobMesh();
sliders.push_back(s);
return true;
}
std::shared_ptr<UiSlider> UiManager::findSlider(const std::string& name) {
for (auto& s : sliders) if (s->name == name) return s;
return nullptr;
}
bool UiManager::setSliderCallback(const std::string& name, std::function<void(const std::string&, float)> cb) {
auto s = findSlider(name);
if (!s) {
std::cerr << "UiManager: setSliderCallback failed, slider not found: " << name << std::endl;
return false;
}
s->onValueChanged = std::move(cb);
return true;
}
bool UiManager::setSliderValue(const std::string& name, float value) {
auto s = findSlider(name);
if (!s) return false;
value = std::clamp(value, 0.0f, 1.0f);
if (fabs(s->value - value) < 1e-6f) return true;
s->value = value;
s->buildKnobMesh();
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
return true;
}
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
MenuState prev; MenuState prev;
prev.root = root; prev.root = root;
prev.buttons = buttons; prev.buttons = buttons;
prev.sliders = sliders;
prev.pressedButton = pressedButton; prev.pressedButton = pressedButton;
prev.pressedSlider = pressedSlider;
prev.path = ""; prev.path = "";
try { try {
@ -226,11 +414,16 @@ namespace ZL {
root = s.root; root = s.root;
buttons = s.buttons; buttons = s.buttons;
sliders = s.sliders;
pressedButton = s.pressedButton; pressedButton = s.pressedButton;
pressedSlider = s.pressedSlider;
for (auto& b : buttons) { for (auto& b : buttons) {
if (b) b->buildMesh(); if (b) b->buildMesh();
} }
for (auto& sl : sliders) {
if (sl) { sl->buildTrackMesh(); sl->buildKnobMesh(); }
}
return true; return true;
} }
@ -240,8 +433,6 @@ namespace ZL {
} }
void UiManager::draw(Renderer& renderer) { void UiManager::draw(Renderer& renderer) {
if (!root) return;
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1); renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
renderer.PushMatrix(); renderer.PushMatrix();
renderer.LoadIdentity(); renderer.LoadIdentity();
@ -249,6 +440,9 @@ namespace ZL {
for (const auto& b : buttons) { for (const auto& b : buttons) {
b->draw(renderer); b->draw(renderer);
} }
for (const auto& s : sliders) {
s->draw(renderer);
}
renderer.PopMatrix(); renderer.PopMatrix();
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
@ -263,6 +457,22 @@ namespace ZL {
if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal; if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal;
} }
} }
if (pressedSlider) {
auto s = pressedSlider;
float t;
if (s->vertical) {
t = (y - s->rect.y) / s->rect.h;
}
else {
t = (x - s->rect.x) / s->rect.w;
}
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;
s->value = t;
s->buildKnobMesh();
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
}
} }
void UiManager::onMouseDown(int x, int y) { void UiManager::onMouseDown(int x, int y) {
@ -271,8 +481,24 @@ namespace ZL {
b->state = ButtonState::Pressed; b->state = ButtonState::Pressed;
pressedButton = b; pressedButton = b;
} }
else { }
// leave others
for (auto& s : sliders) {
if (s->rect.contains((float)x, (float)y)) {
pressedSlider = s;
float t;
if (s->vertical) {
t = (y - s->rect.y) / s->rect.h;
}
else {
t = (x - s->rect.x) / s->rect.w;
}
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;
s->value = t;
s->buildKnobMesh();
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
break;
} }
} }
} }
@ -290,6 +516,10 @@ namespace ZL {
} }
} }
pressedButton.reset(); pressedButton.reset();
if (pressedSlider) {
pressedSlider.reset();
}
} }
std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) { std::shared_ptr<UiButton> UiManager::findButton(const std::string& name) {

View File

@ -45,12 +45,32 @@ namespace ZL {
void draw(Renderer& renderer) const; 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 UiNode { struct UiNode {
std::string type; std::string type;
UiRect rect; UiRect rect;
std::string name; std::string name;
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::string orientation = "vertical"; std::string orientation = "vertical";
float spacing = 0.0f; float spacing = 0.0f;
}; };
@ -67,10 +87,21 @@ namespace ZL {
void onMouseDown(int x, int y); void onMouseDown(int x, int y);
void onMouseUp(int x, int y); void onMouseUp(int x, int y);
bool isUiInteraction() const {
return pressedButton != nullptr || pressedSlider != nullptr;
}
std::shared_ptr<UiButton> findButton(const std::string& name); std::shared_ptr<UiButton> findButton(const std::string& name);
bool setButtonCallback(const std::string& name, std::function<void(const std::string&)> cb); 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)
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
bool popMenu(); bool popMenu();
void clearMenuStack(); void clearMenuStack();
@ -78,17 +109,21 @@ namespace ZL {
private: private:
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);
void layoutNode(const std::shared_ptr<UiNode>& node); void layoutNode(const std::shared_ptr<UiNode>& node);
void collectButtons(const std::shared_ptr<UiNode>& node); void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);
std::shared_ptr<UiNode> root; std::shared_ptr<UiNode> root;
std::vector<std::shared_ptr<UiButton>> buttons; std::vector<std::shared_ptr<UiButton>> buttons;
std::vector<std::shared_ptr<UiSlider>> sliders;
std::shared_ptr<UiButton> pressedButton; std::shared_ptr<UiButton> pressedButton;
std::shared_ptr<UiSlider> pressedSlider;
struct MenuState { struct MenuState {
std::shared_ptr<UiNode> root; std::shared_ptr<UiNode> root;
std::vector<std::shared_ptr<UiButton>> buttons; std::vector<std::shared_ptr<UiButton>> buttons;
std::vector<std::shared_ptr<UiSlider>> sliders;
std::shared_ptr<UiButton> pressedButton; std::shared_ptr<UiButton> pressedButton;
std::shared_ptr<UiSlider> pressedSlider;
std::string path; std::string path;
}; };

View File

@ -66,6 +66,20 @@
] ]
} }
] ]
},
{
"type": "Slider",
"name": "musicVolumeSlider",
"x": 1140,
"y": 100,
"width": 10,
"height": 500,
"value": 0.5,
"orientation": "vertical",
"textures": {
"track": "./resources/musicVolumeBarTexture.png",
"knob": "./resources/musicVolumeBarButton.png"
}
} }
] ]
} }