diff --git a/src/audio_settings_page.cpp b/src/audio_settings_page.cpp index 244ad34..0a9e748 100644 --- a/src/audio_settings_page.cpp +++ b/src/audio_settings_page.cpp @@ -7,23 +7,6 @@ namespace { -std::string buildVolumeBar(int volume_0_to_100) { - int totalBlocks = 10; - int filled = (volume_0_to_100 * totalBlocks + 50) / 100; - filled = std::clamp(filled, 0, totalBlocks); - - std::string bar = "["; - for (int i = 0; i < totalBlocks; ++i) { - if (i < filled) { - bar += "\u2588"; // Solid block - } else { - bar += "\u2591"; // Light shade block - } - } - bar += "] " + std::to_string(volume_0_to_100) + "%"; - return bar; -} - // Case-insensitive lexicographic comparison for alphabetical list ordering. int ciCompare(const std::string& a, const std::string& b) { size_t i = 0; @@ -241,6 +224,8 @@ void AudioSettingsPage::destroyTextures() { deviceText_.destroy(); statusMsgText_.destroy(); footerText_.destroy(); + volumeLabelText_.destroy(); + volumePercentText_.destroy(); for (auto& row : menuRows_) row.destroy(); menuRows_.clear(); @@ -561,8 +546,58 @@ void AudioSettingsPage::render(SDL_Renderer* renderer) { : ((currentTrackIndex_ >= 0 && currentTrackIndex_ < static_cast(tracks_.size())) ? tracks_[currentTrackIndex_].display_name : "Original Audio"); + // Volume row is drawn specially: a fixed percentage label to the left + // of a full-width outlined bar whose solid fill tracks the volume. + int volume = state_->audio.getVolume(); + SDL_Color volumeColor = (selectedItem_ == 0) ? yellow : white; + volumeLabelText_.ensure(renderer, fontBody_, "Volume:", volumeColor); + volumePercentText_.ensure(renderer, fontBody_, std::to_string(volume) + "%", volumeColor); + + float labelX = 100.0f; + float textY = listY + (rowHeight - volumeLabelText_.height) / 2.0f; + if (volumeLabelText_.texture) { + SDL_FRect dst{labelX, textY, volumeLabelText_.width, volumeLabelText_.height}; + SDL_RenderTexture(renderer, volumeLabelText_.texture, nullptr, &dst); + } + + float pctX = labelX + volumeLabelText_.width + 12.0f; + if (volumePercentText_.texture) { + SDL_FRect dst{pctX, textY, volumePercentText_.width, volumePercentText_.height}; + SDL_RenderTexture(renderer, volumePercentText_.texture, nullptr, &dst); + } + + // The bar outline spans the remaining full width (right edge matching + // the menu's 100px right margin) and never moves: its left edge is + // reserved from the widest percentage text ("100%"). Only the fill + // grows and shrinks with the volume. + int widestPctW = 0; + TTF_GetStringSize(fontBody_, "100%", 0, &widestPctW, nullptr); + float barLeft = pctX + static_cast(widestPctW) + 20.0f; + float barRight = fw - 100.0f; + float barTop = listY + (rowHeight - 24.0f) / 2.0f; + float barH = 24.0f; + float barW = barRight - barLeft; + + SDL_SetRenderDrawColor(renderer, 150, 160, 180, 255); + SDL_FRect barOutline{barLeft, barTop, barW, barH}; + SDL_RenderRect(renderer, &barOutline); + + float fillW = barW * static_cast(volume) / 100.0f; + if (fillW > 0.0f) { + SDL_SetRenderDrawColor(renderer, 0, 200, 120, 255); + SDL_FRect fill{barLeft + 1.0f, barTop + 1.0f, std::max(0.0f, fillW - 2.0f), barH - 2.0f}; + SDL_RenderFillRect(renderer, &fill); + } + + if (selectedItem_ == 0) { + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_FRect selLine{labelX, listY + volumeLabelText_.height + 2.0f, barRight - labelX, 6.0f}; + SDL_RenderFillRect(renderer, &selLine); + } + + listY += rowHeight; + std::vector rowTexts = { - "Volume: " + buildVolumeBar(state_->audio.getVolume()), "Output Device: [" + devName + "]", "Audio Track: [" + trackName + "]", std::string("Audio Status: [") + (state_->audio.isAudioEnabled() ? "ON" : "OFF") + "]", @@ -571,13 +606,14 @@ void AudioSettingsPage::render(SDL_Renderer* renderer) { if (menuRows_.size() != rowTexts.size()) menuRows_.resize(rowTexts.size()); for (int i = 0; i < static_cast(rowTexts.size()); ++i) { - SDL_Color color = (selectedItem_ == i) ? yellow : white; + int rowIndex = i + 1; // row 0 is the volume slider + SDL_Color color = (selectedItem_ == rowIndex) ? yellow : white; menuRows_[i].ensure(renderer, fontBody_, rowTexts[i], color); if (!menuRows_[i].texture) { listY += rowHeight; continue; } SDL_FRect dst{100.0f, listY, menuRows_[i].width, menuRows_[i].height}; SDL_RenderTexture(renderer, menuRows_[i].texture, nullptr, &dst); - if (selectedItem_ == i) { + if (selectedItem_ == rowIndex) { SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_FRect selLine{100.0f, listY + menuRows_[i].height + 2.0f, menuRows_[i].width, 6.0f}; SDL_RenderFillRect(renderer, &selLine); diff --git a/src/audio_settings_page.h b/src/audio_settings_page.h index 954000b..7a2d4be 100644 --- a/src/audio_settings_page.h +++ b/src/audio_settings_page.h @@ -66,6 +66,8 @@ private: CachedText deviceText_; CachedText statusMsgText_; CachedText footerText_; + CachedText volumeLabelText_; + CachedText volumePercentText_; std::vector menuRows_; CachedText modalTitleText_;