diff --git a/source/code/MenuSystem.cpp b/source/code/MenuSystem.cpp index c0361b2..191df06 100644 --- a/source/code/MenuSystem.cpp +++ b/source/code/MenuSystem.cpp @@ -118,6 +118,58 @@ static void drawToScreen(const Button& b) { sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center, &globalData.logicalResize); } +void Button::drawToScreen(SDL_Renderer* target) const { + ::drawToScreen(*this); +} + +void ButtonWithAdjustment::drawToScreen(SDL_Renderer* target) const { + // Draw the main button + if (marked) { + globalData.spriteHolder->GetSprite(standardButton.menu_marked).Draw(globalData.screen, SDL_GetTicks(), x, y, &globalData.logicalResize); + } + else { + globalData.spriteHolder->GetSprite(standardButton.menu_unmarked).Draw(globalData.screen, SDL_GetTicks(), x, y, &globalData.logicalResize); + } + + standardButton.getLabel(getLabel(), marked)->Draw(globalData.screen, x+standardButton.xsize/2, y+standardButton.ysize/2, + sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center, &globalData.logicalResize); + + // Draw left adjustment button (down arrow) + int adjustSize = getAdjustButtonSize(); + int leftX = x - adjustSize - 10; + int leftY = y + (standardButton.ysize - adjustSize) / 2; // Vertically center the small button + globalData.spriteHolder->GetSprite("menu_unmarked").DrawScaled(globalData.screen, SDL_GetTicks(), leftX, leftY, adjustSize, adjustSize, &globalData.logicalResize); + standardButton.getLabel("\u25c0", false)->Draw(globalData.screen, leftX+adjustSize/2, leftY+adjustSize/2, + sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center, &globalData.logicalResize); + + // Draw right adjustment button (up arrow) + int rightX = x + standardButton.xsize + 10; + int rightY = y + (standardButton.ysize - adjustSize) / 2; // Vertically center the small button + globalData.spriteHolder->GetSprite("menu_unmarked").DrawScaled(globalData.screen, SDL_GetTicks(), rightX, rightY, adjustSize, adjustSize, &globalData.logicalResize); + standardButton.getLabel("\u25b6", false)->Draw(globalData.screen, rightX+adjustSize/2, rightY+adjustSize/2, + sago::SagoTextField::Alignment::center, sago::SagoTextField::VerticalAlignment::center, &globalData.logicalResize); +} + +bool ButtonWithAdjustment::isClickedAdjustLeft(int mx, int my) const { + int adjustSize = getAdjustButtonSize(); + int leftX = x - adjustSize - 10; + int leftY = y + (standardButton.ysize - adjustSize) / 2; + if (mx >= leftX && my >= leftY && mx <= leftX + adjustSize && my <= leftY + adjustSize) { + return true; + } + return false; +} + +bool ButtonWithAdjustment::isClickedAdjustRight(int mx, int my) const { + int adjustSize = getAdjustButtonSize(); + int rightX = x + standardButton.xsize + 10; + int rightY = y + (standardButton.ysize - adjustSize) / 2; + if (mx >= rightX && my >= rightY && mx <= rightX + adjustSize && my <= rightY + adjustSize) { + return true; + } + return false; +} + static bool isClicked(const Button& b, int x,int y) { if ( x >= b.x && y >= b.y && x<= b.x+b.standardButton.xsize && y <= b.y + b.standardButton.ysize) { @@ -129,9 +181,9 @@ static bool isClicked(const Button& b, int x,int y) { void Menu::drawSelf(SDL_Renderer* target) { DrawBackground(target); for (const Button* b : buttons) { - drawToScreen(*b); + b->drawToScreen(target); } - drawToScreen(exit); + exit.drawToScreen(target); exit.standardButton.getLabel(title, false)->Draw(target, 50, 50, sago::SagoTextField::Alignment::left, sago::SagoTextField::VerticalAlignment::top, &globalData.logicalResize); } @@ -140,10 +192,12 @@ void Menu::placeButtons() { int nextY = 100; int X = 50; for (Button* it : buttons) { - X = (globalData.xsize - it->standardButton.xsize)/2; - it->x = X; + // For all buttons, center based on the main button width to keep them aligned + int centerX = (globalData.xsize - it->standardButton.xsize)/2; + it->x = centerX; it->y = nextY; nextY += it->standardButton.ysize+10; + X = it->x; } exit.x = X; exit.y = nextY; @@ -300,6 +354,20 @@ void Menu::ProcessInput(const SDL_Event& event, bool& processed) { processed = true; } + if (isLeftEvent(event)) { + if (marked < (int)buttons.size()) { + buttons.at(marked)->doLeft(); + } + processed = true; + } + + if (isRightEvent(event)) { + if (marked < (int)buttons.size()) { + buttons.at(marked)->doRight(); + } + processed = true; + } + if (isEscapeEvent(event) && isSubmenu) { running = false; processed = true; @@ -351,6 +419,16 @@ void Menu::Update() { if ( (buttonState&SDL_BUTTON(1) )==SDL_BUTTON(1) && bMouseUp) { bMouseUp = false; for (int i=0; i< (int)buttons.size(); ++i) { + // Check for adjustment button clicks first + if (buttons.at(i)->isClickedAdjustLeft(mousex, mousey)) { + buttons.at(i)->doLeft(); + return; + } + if (buttons.at(i)->isClickedAdjustRight(mousex, mousey)) { + buttons.at(i)->doRight(); + return; + } + // Then check for main button click if (isClicked(*buttons.at(i), mousex, mousey)) { buttons.at(i)->doAction(); if (buttons.at(i)->isPopOnRun()) { diff --git a/source/code/MenuSystem.h b/source/code/MenuSystem.h index 8be082d..5defe0c 100644 --- a/source/code/MenuSystem.h +++ b/source/code/MenuSystem.h @@ -81,14 +81,40 @@ public: void setAction(void (*action2run)(void)); virtual void doAction(); //Run the callback function + virtual void doLeft() {}; //Handle left input (for adjustment buttons) + virtual void doRight() {}; //Handle right input (for adjustment buttons) void setPopOnRun(bool popOnRun); bool isPopOnRun() const; virtual const std::string& getLabel() const {return this->label; }; + virtual bool hasAdjustmentButtons() const { return false; }; + virtual void drawToScreen(SDL_Renderer* target) const; + virtual bool isClickedAdjustLeft(int /*x*/, int /*y*/) const { return false; }; + virtual bool isClickedAdjustRight(int /*x*/, int /*y*/) const { return false; }; + virtual int getTotalWidth() const { return standardButton.xsize; }; //May hold any other information the callback might need int iGeneric1 = 0; }; +//A button with left/right adjustment controls +class ButtonWithAdjustment : public Button +{ +public: + ButtonWithAdjustment() = default; + virtual ~ButtonWithAdjustment() = default; + + virtual bool hasAdjustmentButtons() const override { return true; }; + virtual void drawToScreen(SDL_Renderer* target) const override; + virtual bool isClickedAdjustLeft(int x, int y) const override; + virtual bool isClickedAdjustRight(int x, int y) const override; + virtual int getTotalWidth() const override { + int adjustSize = getAdjustButtonSize(); + return adjustSize + 10 + standardButton.xsize + 10 + adjustSize; + }; + + int getAdjustButtonSize() const { return standardButton.ysize / 2; }; +}; + class Menu : public sago::GameStateInterface { diff --git a/source/code/menudef.cpp b/source/code/menudef.cpp index eeaf85e..58eb7bf 100644 --- a/source/code/menudef.cpp +++ b/source/code/menudef.cpp @@ -218,6 +218,102 @@ void Button_testMusic::doAction() { Mix_PlayMusic(bgMusic.get(), -1); } +// Sound volume button with adjustment controls +class Button_soundVolume : public ButtonWithAdjustment { +private: + mutable std::string volumeLabel; + int incrementValue = 5; +public: + Button_soundVolume(); + virtual void doAction() override; + virtual void doLeft() override; + virtual void doRight() override; + virtual const std::string& getLabel() const override; +}; + +Button_soundVolume::Button_soundVolume() { + standardButton.setSurfaces(); +} + +void Button_soundVolume::doAction() { + // Test sound + sago::SoundHandler testSound = globalData.spriteHolder->GetDataHolder().getSoundHandler("pop"); + Mix_VolumeChunk(testSound.get(), Config::getInstance()->getInt("volume_sound")); + Mix_PlayChannel(1, testSound.get(), 0); +} + +void Button_soundVolume::doLeft() { + // Decrease volume + int newValue = Config::getInstance()->getInt("volume_sound") - incrementValue; + if (newValue < 0) { + newValue = 0; + } + Config::getInstance()->setInt("volume_sound", newValue); +} + +void Button_soundVolume::doRight() { + // Increase volume + int newValue = Config::getInstance()->getInt("volume_sound") + incrementValue; + if (newValue > MIX_MAX_VOLUME) { + newValue = MIX_MAX_VOLUME; + } + Config::getInstance()->setInt("volume_sound", newValue); +} + +const std::string& Button_soundVolume::getLabel() const { + int volumePct = Config::getInstance()->getInt("volume_sound")*100.0/MIX_MAX_VOLUME; + volumeLabel = fmt::format(_("Sound: {}"), volumePct)+"%"; + return volumeLabel; +} + +// Music volume button with adjustment controls +class Button_musicVolume : public ButtonWithAdjustment { +private: + mutable std::string volumeLabel; + int incrementValue = 5; +public: + Button_musicVolume(); + virtual void doAction() override; + virtual void doLeft() override; + virtual void doRight() override; + virtual const std::string& getLabel() const override; +}; + +Button_musicVolume::Button_musicVolume() { + standardButton.setSurfaces(); +} + +void Button_musicVolume::doAction() { + // Test music + Mix_VolumeMusic(Config::getInstance()->getInt("volume_music")); + sago::MusicHandler bgMusic = globalData.spriteHolder->GetDataHolder().getMusicHandler("bgmusic"); + Mix_PlayMusic(bgMusic.get(), -1); +} + +void Button_musicVolume::doLeft() { + // Decrease volume + int newValue = Config::getInstance()->getInt("volume_music") - incrementValue; + if (newValue < 0) { + newValue = 0; + } + Config::getInstance()->setInt("volume_music", newValue); +} + +void Button_musicVolume::doRight() { + // Increase volume + int newValue = Config::getInstance()->getInt("volume_music") + incrementValue; + if (newValue > MIX_MAX_VOLUME) { + newValue = MIX_MAX_VOLUME; + } + Config::getInstance()->setInt("volume_music", newValue); +} + +const std::string& Button_musicVolume::getLabel() const { + int volumePct = Config::getInstance()->getInt("volume_music")*100.0/MIX_MAX_VOLUME; + volumeLabel = fmt::format(_("Music: {}"), volumePct)+"%"; + return volumeLabel; +} + static void runSinglePlayerEndless0() { runGame(Gametype::SinglePlayerEndless, 0); @@ -453,8 +549,8 @@ static void PlayerConfigMenu() { static void ConfigureMenu() { Menu cm(globalData.screen,_("Configuration"),true); AlwaysSoftwareRenderButton bSoftware; - MusicButton bMusic; - SoundButton bSound; + Button_musicVolume bMusic; + Button_soundVolume bSound; FullscreenButton buttonFullscreen; BlockVariantButton bBlockVariant; Button bPlayerConfig; @@ -464,8 +560,6 @@ static void ConfigureMenu() { bThemes.setLabel(_("Themes")); bThemes.setAction(OpenThemesMenu); SetAlwaysSoftwareLabel(&bSoftware); - SetMusicLabel(&bMusic); - SetSoundLabel(&bSound); SetFullscreenLabel(&buttonFullscreen); SetBlockVariationLabel(&bBlockVariant); cm.addButton(&bMusic);