#include "const.h" #include "menu.h" // Constructor Menu::Menu(SDL_Renderer *renderer, Text *text, Input *input) { this->renderer = renderer; this->text = text; this->input = input; soundMove = nullptr; soundAccept = nullptr; soundCancel = nullptr; } Menu::~Menu() { renderer = nullptr; text = nullptr; input = nullptr; if (soundMove) { JA_DeleteSound(soundMove); } if (soundAccept) { JA_DeleteSound(soundAccept); } if (soundCancel) { JA_DeleteSound(soundCancel); } } // Inicializador void Menu::init(std::string name, int x, int y, int backgroundType) { // Inicia variables this->name = name; selector.index = 0; itemSelected = MENU_NO_OPTION; this->x = x; this->y = y; rectBG.rect = {0, 0, 0, 0}; rectBG.color = {0, 0, 0}; rectBG.a = 0; this->backgroundType = backgroundType; isCenteredOnX = false; isCenteredOnY = false; areElementsCenteredOnX = false; centerX = 0; centerY = 0; widestItem = 0; colorGreyed = {128, 128, 128}; // Selector selector.originY = 0; selector.targetY = 0; selector.despY = 0; selector.originH = 0; selector.targetH = 0; selector.incH = 0; selector.y = 0.0f; selector.h = 0.0f; selector.numJumps = 8; selector.moving = false; selector.resizing = false; selector.rect = {0, 0, 0, 0}; selector.color = {0, 0, 0}; selector.itemColor = {0, 0, 0}; selector.a = 255; } // Carga los ficheros de audio void Menu::loadAudioFile(std::string file, int sound) { switch (sound) { case SOUND_ACCEPT: soundAccept = JA_LoadSound(file.c_str()); break; case SOUND_CANCEL: soundCancel = JA_LoadSound(file.c_str()); break; case SOUND_MOVE: soundMove = JA_LoadSound(file.c_str()); break; default: break; } } // Obtiene el nombre del menu std::string Menu::getName() { return name; } // Obtiene el valor de la variable int Menu::getItemSelected() { // Al llamar a esta funcion, se obtiene el valor y se borra const int temp = itemSelected; itemSelected = MENU_NO_OPTION; return temp; } // Actualiza la posicion y el estado del selector void Menu::updateSelector() { if (selector.moving) { // Calcula el desplazamiento en Y selector.y += selector.despY; if (selector.despY > 0) // Va hacia abajo { if (selector.y > selector.targetY) // Ha llegado al destino { selector.originY = selector.y = selector.targetY; selector.moving = false; } } if (selector.despY < 0) // Va hacia arriba { if (selector.y < selector.targetY) // Ha llegado al destino { selector.originY = selector.y = selector.targetY; selector.moving = false; } } selector.rect.y = int(selector.y); } else { selector.rect.y = int(selector.y); } if (selector.resizing) { // Calcula el incremento en H selector.h += selector.incH; if (selector.incH > 0) // Crece { if (selector.h > selector.targetH) // Ha llegado al destino { // selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index); selector.originH = selector.h = selector.targetH; selector.resizing = false; } } if (selector.incH < 0) // Decrece { if (selector.h < selector.targetH) // Ha llegado al destino { // selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index); selector.originH = selector.h = selector.targetH; selector.resizing = false; } } selector.rect.h = int(selector.h); } else { selector.rect.h = getSelectorHeight(selector.index); } } // Coloca el selector en una posición específica void Menu::setSelectorPos(int index) { if (index < item.size()) { selector.index = index; selector.rect.y = selector.y = selector.originY = selector.targetY = item[selector.index].rect.y; selector.rect.w = rectBG.rect.w; selector.rect.x = rectBG.rect.x; selector.originH = selector.targetH = selector.rect.h = getSelectorHeight(selector.index); selector.moving = false; selector.resizing = false; } } // Obtiene la anchura del elemento más ancho del menu int Menu::getWidestItem() { int result = 0; // Obtenemos la anchura del item mas ancho for (auto &i : item) { result = std::max(result, i.rect.w); } return result; } // Deja el menu apuntando al primer elemento void Menu::reset() { itemSelected = MENU_NO_OPTION; selector.index = 0; selector.originY = selector.targetY = selector.y = item[0].rect.y; selector.originH = selector.targetH = item[0].rect.h; selector.moving = false; selector.resizing = false; } // Actualiza el menu para recolocarlo correctamente y establecer el tamaño void Menu::reorganize() { setRectSize(); if (isCenteredOnX) { centerMenuOnX(centerX); } if (isCenteredOnY) { centerMenuOnY(centerY); } if (areElementsCenteredOnX) { centerMenuElementsOnX(); } } // Deja el menu apuntando al siguiente elemento bool Menu::increaseSelectorIndex() { // Obten las coordenadas del elemento actual selector.y = selector.originY = item[selector.index].rect.y; selector.h = selector.originH = getSelectorHeight(selector.index); // Calcula cual es el siguiente elemento ++selector.index %= item.size(); while (!item[selector.index].selectable) { ++selector.index %= item.size(); } // Establece las coordenadas y altura de destino selector.targetY = item[selector.index].rect.y; selector.despY = (selector.targetY - selector.originY) / selector.numJumps; selector.targetH = getSelectorHeight(selector.index); selector.incH = (selector.targetH - selector.originH) / selector.numJumps; selector.moving = true; if (selector.incH != 0) { selector.resizing = true; } return true; } // Deja el menu apuntando al elemento anterior bool Menu::decreaseSelectorIndex() { // Obten las coordenadas del elemento actual selector.y = selector.originY = item[selector.index].rect.y; selector.h = selector.originH = getSelectorHeight(selector.index); // Calcula cual es el siguiente elemento if (selector.index == 0) { selector.index = item.size(); } else { selector.index--; } while (!item[selector.index].selectable) { if (selector.index == 0) { selector.index = item.size(); } else { selector.index--; } } // Establece las coordenadas y altura de destino selector.targetY = item[selector.index].rect.y; selector.despY = (selector.targetY - selector.originY) / selector.numJumps; selector.targetH = getSelectorHeight(selector.index); selector.incH = (selector.targetH - selector.originH) / selector.numJumps; selector.moving = true; if (selector.incH != 0) { selector.resizing = true; } return true; } // Actualiza la logica del menu void Menu::update() { checkInput(); updateSelector(); } // Pinta el menu en pantalla void Menu::render() { // Rendereritza el fondo del menu if (backgroundType == MENU_BACKGROUND_SOLID) { SDL_SetRenderDrawColor(renderer, rectBG.color.r, rectBG.color.g, rectBG.color.b, rectBG.a); SDL_RenderFillRect(renderer, &rectBG.rect); } // Renderiza el rectangulo del selector const SDL_Rect temp = {selector.rect.x, selector.rect.y - 1, selector.rect.w, selector.rect.h + 1}; // temp.y--; // temp.h++; SDL_SetRenderDrawColor(renderer, selector.color.r, selector.color.g, selector.color.b, selector.a); SDL_RenderFillRect(renderer, &temp); // Renderiza el borde del fondo if (backgroundType == MENU_BACKGROUND_SOLID) { SDL_SetRenderDrawColor(renderer, rectBG.color.r, rectBG.color.g, rectBG.color.b, 255); SDL_RenderDrawRect(renderer, &rectBG.rect); } // Renderitza el texto for (int i = 0; i < item.size(); i++) { if (i == selector.index) { const color_t color = {selector.itemColor.r, selector.itemColor.g, selector.itemColor.b}; text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color); } else if (item[i].selectable) { text->write(item[i].rect.x, item[i].rect.y, item[i].label); } else if (item[i].greyed) { text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, colorGreyed); } else // No seleccionable { if ((item[i].linkedUp) && (i == selector.index + 1)) { const color_t color = {selector.itemColor.r, selector.itemColor.g, selector.itemColor.b}; text->writeColored(item[i].rect.x, item[i].rect.y, item[i].label, color); } else // No enlazado con el de arriba { text->write(item[i].rect.x, item[i].rect.y, item[i].label); } } } } // Establece el rectangulo de fondo del menu y el selector void Menu::setRectSize() { rectBG.rect.w = findWidth() + text->getCharacterWidth(); rectBG.rect.h = findHeight() + text->getCharacterWidth(); // La posición X es la del menú menos medio caracter rectBG.rect.x = x - (text->getCharacterWidth() / 2); // La posición Y es la del menu menos la altura de medio caracter rectBG.rect.y = y - (text->getCharacterWidth() / 2); // Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo setSelectorPos(selector.index); } // Establece el color del rectangulo de fondo void Menu::setBackgroundColor(color_t color, int alpha) { rectBG.color = color; rectBG.a = alpha; } // Establece el color del rectangulo del selector void Menu::setSelectorColor(color_t color, int alpha) { selector.color = color; selector.a = alpha; } // Establece el color del texto del selector void Menu::setSelectorTextColor(color_t color) { selector.itemColor = color; } // Centra el menu respecto un punto en el eje X void Menu::centerMenuOnX(int value) { isCenteredOnX = true; centerX = value; // Establece la nueva posición centrada en funcion del elemento más ancho x = (value) - (findWidth() / 2); // Reposiciona los elementos del menu for (auto &i : item) { i.rect.x = x; } // Recalcula el rectangulo de fondo setRectSize(); } // Centra el menu respecto un punto en el eje Y void Menu::centerMenuOnY(int value) { isCenteredOnY = true; centerY = value; // Establece la nueva posición centrada en funcion del elemento más ancho y = (value) - (findHeight() / 2); // Reposiciona los elementos del menu replaceElementsOnY(); // Recalcula el rectangulo de fondo setRectSize(); } // Centra los elementos del menu en el eje X void Menu::centerMenuElementsOnX() { areElementsCenteredOnX = true; for (auto &i : item) { i.rect.x = (centerX - (i.rect.w / 2)); } } // Añade un item al menu void Menu::addItem(std::string text, int hPaddingDown, bool selectable, bool greyed, bool linkedDown) { item_t temp; // Si es el primer item coge la posición en el eje Y del propio menu if (item.size() == 0) { temp.rect.y = y; } else // En caso contrario, coge la posición en el eje Y a partir del elemento anterior { temp.rect.y = item.back().rect.y + item.back().rect.h + item.back().hPaddingDown; } temp.rect.x = x; temp.hPaddingDown = hPaddingDown; temp.selectable = selectable; temp.greyed = greyed; temp.linkedDown = linkedDown; item.push_back(temp); setItemCaption(item.size() - 1, text); if (item.size() > 0) { if (item[item.size() - 1].linkedDown) { item[item.size()].linkedUp = true; } } centerX = x + (findWidth() / 2); reorganize(); } // Cambia el texto de un item void Menu::setItemCaption(int index, std::string text) { item[index].label = text; item[index].rect.w = this->text->lenght(item[index].label); item[index].rect.h = this->text->getCharacterWidth(); reorganize(); const std::string texto = item[index].label + ":" + std::to_string(item[index].rect.w); printf("Adding menu item -> %s\n", texto.c_str()); } // Establece el indice del itemm que se usará por defecto al cancelar el menu void Menu::setDefaultActionWhenCancel(int item) { defaultActionWhenCancel = item; } // Gestiona la entrada de teclado y mando durante el menu void Menu::checkInput() { if (input->checkInput(INPUT_UP, REPEAT_FALSE)) { if (decreaseSelectorIndex()) { if (soundMove) { JA_PlaySound(soundMove); } } } if (input->checkInput(INPUT_DOWN, REPEAT_FALSE)) { if (increaseSelectorIndex()) { if (soundMove) { JA_PlaySound(soundMove); } } } if (input->checkInput(INPUT_ACCEPT, REPEAT_FALSE)) { itemSelected = selector.index; if (soundAccept) { JA_PlaySound(soundAccept); } } if (input->checkInput(INPUT_CANCEL, REPEAT_FALSE)) { itemSelected = defaultActionWhenCancel; if (soundCancel) { JA_PlaySound(soundCancel); } } } // Calcula el ancho del menu int Menu::findWidth() { return getWidestItem(); } // Calcula el alto del menu int Menu::findHeight() { int height = 0; // Obtenemos la altura de la suma de alturas de los items for (auto &i : item) { height += i.rect.h + i.hPaddingDown; } return height - item.back().hPaddingDown; } // Recoloca los elementos del menu en el eje Y void Menu::replaceElementsOnY() { item[0].rect.y = y; for (int i = 1; i < item.size(); i++) { item[i].rect.y = item[i - 1].rect.y + item[i - 1].rect.h + item[i - 1].hPaddingDown; } } // Establece el estado seleccionable de un item void Menu::setSelectable(int index, bool value) { item[index].selectable = value; } // Establece el estado agrisado de un item void Menu::setGreyed(int index, bool value) { item[index].greyed = value; } // Establece el estado de enlace de un item void Menu::setLinkedDown(int index, bool value) { item[index].linkedDown = value; } // Calcula la altura del selector int Menu::getSelectorHeight(int value) { if (item[value].linkedDown) { return item[value].rect.h + item[value].hPaddingDown + item[value + 1].rect.h; } else { return item[value].rect.h; } }