#include "const.h" #include "menu.h" // Constructor Menu::Menu(SDL_Renderer *renderer, Text *text, Input *input, std::string *fileList) { mRenderer = renderer; mText = text; mInput = input; mFileList = fileList; } Menu::~Menu() { mRenderer = nullptr; mText = nullptr; mInput = nullptr; mFileList = nullptr; } // Inicializador void Menu::init(std::string name, int x, int y, int backgroundType) { loadMedia(); // Inicia variables mName = name; mSelector.index = 0; mTotalItems = 0; mItemSelected = MENU_NO_OPTION; mVerticalPadding = 1; mPosX = x; mPosY = y; mRectBG.rect.x = 0; mRectBG.rect.y = 0; mRectBG.rect.w = 0; mRectBG.rect.h = 0; mRectBG.r = 0; mRectBG.g = 0; mRectBG.b = 0; mBackgroundType = backgroundType; mIsCenteredOnX = false; mIsCenteredOnY = false; mAreElementsCenteredOnX = false; mCenterX = x + ((SCREEN_WIDTH - x) / 2); mCenterY = y + ((SCREEN_HEIGHT - y) / 2); mWidestItem = 0; mColorGreyed = {128, 128, 128}; // Selector mSelector.originY = 0; mSelector.targetY = 0; mSelector.despY = 0; mSelector.originH = 0; mSelector.targetH = 0; mSelector.incH = 0; mSelector.y = 0; mSelector.numJumps = 4; mSelector.moving = false; mSelector.resizing = false; mSelector.rect.x = 0; mSelector.rect.y = 0; mSelector.rect.w = 0; mSelector.rect.h = 0; mSelector.r = 0; mSelector.g = 0; mSelector.b = 0; mSelector.a = 255; // Elementos del menu for (int i = 0; i < MENU_MAX_ITEMS; i++) { mItem[i].label = ""; mItem[i].w = 0; mItem[i].h = 0; mItem[i].x = 0; mItem[i].y = 0; mItem[i].hPaddingUp = 0; mItem[i].hPaddingDown = 0; } } // Carga los recursos necesarios para la sección 'Title' bool Menu::loadMedia() { // Indicador de éxito en la carga bool success = true; // Sonidos mSoundMove = JA_LoadSound(mFileList[17].c_str()); mSoundAccept = JA_LoadSound(mFileList[18].c_str()); mSoundCancel = JA_LoadSound(mFileList[16].c_str()); return success; } // Obtiene el nombre del menu std::string Menu::getName() { return mName; } // Obtiene el valor de la variable Uint8 Menu::getItemSelected() { const int temp = mItemSelected; mItemSelected = MENU_NO_OPTION; return temp; } // Actualiza la posicion y el estado del selector void Menu::updateSelector() { if (mSelector.moving) { // Calcula el desplazamiento en Y mSelector.y += mSelector.despY; if (mSelector.despY > 0) // Va hacia abajo { if (mSelector.y > mSelector.targetY) // Ha llegado al destino { mSelector.originY = mSelector.y = mSelector.targetY; mSelector.moving = false; } } if (mSelector.despY < 0) // Va hacia arriba { if (mSelector.y < mSelector.targetY) // Ha llegado al destino { mSelector.originY = mSelector.y = mSelector.targetY; mSelector.moving = false; } } mSelector.rect.y = int(mSelector.y) - 1; } else { mSelector.rect.y = int(mSelector.y) - 1; } if (mSelector.resizing) { // Calcula el incremento en H mSelector.rect.h += mSelector.incH; if (mSelector.incH > 0) // Crece { if (mSelector.rect.h > mSelector.targetH) // Ha llegado al destino { mSelector.originH = mSelector.rect.h = mSelector.targetH; mSelector.resizing = false; } } if (mSelector.incH < 0) // Decrece { if (mSelector.rect.h < mSelector.targetH) // Ha llegado al destino { mSelector.originH = mSelector.rect.h = mSelector.targetH; mSelector.resizing = false; } } //mSelector.rect.h = getSelectorHeight(mSelector.index); } else { mSelector.rect.h = getSelectorHeight(mSelector.index); } } // Establece el origen del selector void Menu::setSelectorOrigin(int value) { mSelector.originY = value; } // Establece el destino del selector void Menu::setSelectorTarget(int value) { mSelector.targetY = value; } // Coloca el selector en una posición específica void Menu::setSelectorPos(Uint8 index) { if (index < mTotalItems) { mSelector.index = index; mSelector.y = mSelector.originY = mSelector.targetY = mItem[mSelector.index].y; mSelector.moving = false; mSelector.resizing = false; } } // Obtiene la anchura del elemento más ancho del menu Uint16 Menu::getWidestItem() { Uint16 result = 0; // Obtenemos la anchura del item mas ancho for (int i = 0; i < mTotalItems; i++) { if (mItem[i].w > result) { result = mItem[i].w; } } return result; } // Deja el menu apuntando al primer elemento void Menu::reset() { mItemSelected = MENU_NO_OPTION; mSelector.index = 0; mSelector.originY = mSelector.targetY = mSelector.y = mItem[0].y; mSelector.originH = mSelector.targetH = mItem[0].h; mSelector.moving = false; mSelector.resizing = false; } // Deja el menu sin elemento seleccionado void Menu::deselectItem() { mItemSelected = MENU_NO_OPTION; } // Actualiza el menu para recolocarlo correctamente y establecer el tamaño void Menu::reorganize() { setRectSize(); if (mIsCenteredOnX) centerMenuOnX(mCenterX); if (mIsCenteredOnY) centerMenuOnY(mCenterY); if (mAreElementsCenteredOnX) centerMenuElementsOnX(); } // Deja el menu apuntando al siguiente elemento bool Menu::increaseSelectorIndex() { bool success = false; mSelector.y = mSelector.originY = mItem[mSelector.index].y; mSelector.rect.h = mSelector.originH = getSelectorHeight(mSelector.index); if (mSelector.index < (mTotalItems - 1)) { mSelector.index++; while ((!mItem[mSelector.index].selectable) && (mSelector.index < (mTotalItems - 1))) mSelector.index++; success = true; } mSelector.targetY = mItem[mSelector.index].y; mSelector.despY = (mSelector.targetY - mSelector.originY) / mSelector.numJumps; mSelector.targetH = getSelectorHeight(mSelector.index); mSelector.incH = (mSelector.targetH - mSelector.originH) / mSelector.numJumps; mSelector.moving = true; return success; } // Deja el menu apuntando al elemento anterior bool Menu::decreaseSelectorIndex() { bool success = false; mSelector.y = mSelector.originY = mItem[mSelector.index].y; mSelector.rect.h = mSelector.originH = getSelectorHeight(mSelector.index); if (mSelector.index > 0) { mSelector.index--; while ((!mItem[mSelector.index].selectable) && (mSelector.index > 0)) mSelector.index--; success = true; } mSelector.targetY = mItem[mSelector.index].y; mSelector.despY = (mSelector.targetY - mSelector.originY) / mSelector.numJumps; mSelector.targetH = getSelectorHeight(mSelector.index); mSelector.incH = (mSelector.targetH - mSelector.originH) / mSelector.numJumps; mSelector.moving = true; return success; } // Actualiza la logica del menu void Menu::update() { updateSelector(); } // Pinta el menu en pantalla void Menu::render() { // Rendereritza el fondo del menu if (mBackgroundType == MENU_BACKGROUND_SOLID) { SDL_SetRenderDrawColor(mRenderer, mRectBG.r, mRectBG.g, mRectBG.b, mRectBG.a); SDL_RenderFillRect(mRenderer, &mRectBG.rect); } // Renderiza el rectangulo del selector SDL_SetRenderDrawColor(mRenderer, mSelector.r, mSelector.g, mSelector.b, mSelector.a); SDL_RenderFillRect(mRenderer, &mSelector.rect); // Renderiza el borde del fondo if (mBackgroundType == MENU_BACKGROUND_SOLID) { SDL_SetRenderDrawColor(mRenderer, mRectBG.r, mRectBG.g, mRectBG.b, 255); SDL_RenderDrawRect(mRenderer, &mRectBG.rect); } // Renderitza el texto for (int i = 0; i < mTotalItems; i++) { if (i == mSelector.index) { const color_t color = {mSelector.itemR, mSelector.itemG, mSelector.itemB}; mText->writeColored(mItem[i].x, mItem[i].y, mItem[i].label, color); } else if (mItem[i].selectable) { mText->write(mItem[i].x, mItem[i].y, mItem[i].label); } else if (mItem[i].greyed) { mText->writeColored(mItem[i].x, mItem[i].y, mItem[i].label, mColorGreyed); } else // No seleccionable { if ((mItem[i].linkedUp) && (i == mSelector.index + 1)) { const color_t color = {mSelector.itemR, mSelector.itemG, mSelector.itemB}; mText->writeColored(mItem[i].x, mItem[i].y, mItem[i].label, color); } else // No enlazado con el de arriba { mText->write(mItem[i].x, mItem[i].y, mItem[i].label); } } } } // Establece el rectangulo de fondo del menu y el selector void Menu::setRectSize() { mRectBG.rect.w = findWidth(); mRectBG.rect.h = findHeight(); // La posición X es la del menú menos medio caracter mRectBG.rect.x = mPosX - (mText->getCharacterWidth() / 2); // La posición Y es la del menu menos la altura de medio caracter i el padding mRectBG.rect.y = mPosY - (mText->getCharacterWidth() / 2) - mVerticalPadding; // Establecemos los valores del rectangulo del selector a partir de los valores del rectangulo de fondo mSelector.rect.h = getSelectorHeight(mSelector.index); mSelector.rect.w = mRectBG.rect.w; mSelector.rect.x = mRectBG.rect.x; } // Establece el valor de la variable void Menu::setTotalItems(int num) { mTotalItems = num; if (mTotalItems > MENU_MAX_ITEMS) mTotalItems = MENU_MAX_ITEMS; } // Establece el color del rectangulo de fondo void Menu::setBackgroundColor(int r, int g, int b, int alpha) { mRectBG.r = r; mRectBG.g = g; mRectBG.b = b; mRectBG.a = alpha; } // Establece el color del rectangulo del selector void Menu::setSelectorColor(int r, int g, int b, int alpha) { mSelector.r = r; mSelector.g = g; mSelector.b = b; mSelector.a = alpha; } // Establece el color del texto del selector void Menu::setSelectorTextColor(int r, int g, int b) { mSelector.itemR = r; mSelector.itemG = g; mSelector.itemB = b; } // Centra el menu respecto un punto en el eje X void Menu::centerMenuOnX(int value) { mIsCenteredOnX = true; mCenterX = value; // Actualiza el rectangulo de fondo para recalcular las dimensiones setRectSize(); // Obten el acho del menu mWidestItem = getWidestItem(); // Establece la nueva posición centrada en funcion del elemento más ancho mPosX = (value) - (mWidestItem / 2); // Reposiciona los elementos del menu for (int i = 0; i < MENU_MAX_ITEMS; i++) { mItem[i].x = mPosX; } // Recalcula el rectangulo de fondo setRectSize(); // Recoloca el selector mSelector.originY = mSelector.targetY = mSelector.y = mItem[mSelector.index].y; mSelector.moving = false; mSelector.originH = mSelector.targetH = mSelector.rect.h = mItem[mSelector.index].h; mSelector.resizing = false; } // Centra el menu respecto un punto en el eje Y void Menu::centerMenuOnY(int value) { mIsCenteredOnY = true; mCenterY = value; // Actualiza el rectangulo de fondo para recalcular las dimensiones setRectSize(); // Obten el alto del menu mHeight = findHeight(); // Establece la nueva posición centrada en funcion del elemento más ancho mPosY = (value) - (mHeight / 2); // Reposiciona los elementos del menu replaceElementsOnY(); // Recalcula el rectangulo de fondo setRectSize(); // Recoloca el selector mSelector.originY = mSelector.targetY = mSelector.y = mItem[mSelector.index].y; mSelector.moving = false; mSelector.originH = mSelector.targetH = mSelector.rect.h = mItem[mSelector.index].h; mSelector.resizing = false; } // Centra los elementos del menu en el eje X void Menu::centerMenuElementsOnX() { mAreElementsCenteredOnX = true; for (int i = 0; i < mTotalItems; i++) mItem[i].x = (mCenterX - (mItem[i].w / 2)); } // Añade un item al menu void Menu::addItem(std::string text, const Uint8 hPaddingUp, const Uint8 hPaddingDown, bool selectable, bool greyed, bool linkedDown) { // Si es el primer item coge la posición en el eje Y del propio menu if (mTotalItems == 0) mItem[mTotalItems].y = mPosY; else // En caso contrario, coge la posición en el eje Y a partir del elemento anterior mItem[mTotalItems].y = mItem[mTotalItems - 1].y + mItem[mTotalItems - 1].h + mItem[mTotalItems - 1].hPaddingDown; mItem[mTotalItems].label = text; mItem[mTotalItems].w = mText->lenght(mItem[mTotalItems].label); mItem[mTotalItems].h = mText->getCharacterWidth() + (mVerticalPadding * 2); mItem[mTotalItems].x = mPosX; mItem[mTotalItems].hPaddingUp = hPaddingUp; mItem[mTotalItems].hPaddingDown = hPaddingDown; mItem[mTotalItems].selectable = selectable; mItem[mTotalItems].greyed = greyed; mItem[mTotalItems].linkedDown = linkedDown; mItem[mTotalItems + 1].linkedUp = linkedDown; setTotalItems(mTotalItems + 1); reorganize(); setSelectorPos(0); } // Cambia el texto de un item void Menu::setItemCaption(Uint8 index, std::string text) { mItem[index].label = text; mItem[index].w = mText->lenght(mItem[index].label); reorganize(); } // Establece el indice del itemm que se usará por defecto al cancelar el menu void Menu::setDefaultActionWhenCancel(Uint8 item) { mDefaultActionWhenCancel = item; } // Gestiona la entrada de teclado y mando durante el menu void Menu::checkInput() { if (mInput->checkInput(INPUT_UP, REPEAT_FALSE)) { if (decreaseSelectorIndex()) JA_PlaySound(mSoundMove); } if (mInput->checkInput(INPUT_DOWN, REPEAT_FALSE)) { if (increaseSelectorIndex()) JA_PlaySound(mSoundMove); } if (mInput->checkInput(INPUT_ACCEPT, REPEAT_FALSE)) { mItemSelected = mSelector.index; JA_PlaySound(mSoundAccept); } if (mInput->checkInput(INPUT_CANCEL, REPEAT_FALSE)) { mItemSelected = mDefaultActionWhenCancel; JA_PlaySound(mSoundCancel); } } // Calcula el ancho del menu Uint16 Menu::findWidth() { Uint16 width = 0; // Obtenemos la anchura del item mas ancho for (int i = 0; i < mTotalItems; i++) if (mItem[i].w > width) width = mItem[i].w; // La anchura de la cadena más larga, mas un caracter width += (mText->getCharacterWidth() * 1); return width; } // Calcula el alto del menu Uint16 Menu::findHeight() { Uint16 height = 0; // Obtenemos la altura de la suma de alturas de los items for (int i = 0; i < mTotalItems; i++) height += mItem[i].h + mItem[i].hPaddingDown; // La altura de la suma de los items mas un caracter y menos un pixel (porque el texto en realidad es de 7 pixeles) height += (mText->getCharacterWidth() * 1) - 1; return height; } // Recoloca los elementos del menu en el eje Y void Menu::replaceElementsOnY() { mItem[0].y = mPosY; for (int i = 1; i < mTotalItems; i++) mItem[i].y = mItem[i - 1].y + mItem[i - 1].h + mItem[i - 1].hPaddingDown; } // Establece el estado seleccionable de un item void Menu::setSelectable(Uint8 index, bool value) { mItem[index].selectable = value; } // Establece el estado agrisado de un item void Menu::setGreyed(Uint8 index, bool value) { mItem[index].greyed = value; } // Establece el estado de enlace de un item void Menu::setLinkedDown(Uint8 index, bool value) { mItem[index].linkedDown = value; } // Calcula la altura del selector int Menu::getSelectorHeight(int value) { if (mItem[value].linkedDown) return mItem[value + 1].y + mItem[value + 1].h - mItem[value].y - 1; else return mItem[value].h - 1; }