LCOV - code coverage report
Current view: top level - src/gui/pedalboard - pedal_board.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 77.1 % 131 101
Test Date: 2026-06-03 09:13:19 Functions: 100.0 % 6 6

            Line data    Source code
       1              : #include "gui/pedalboard/pedal_board.h"
       2              : #include "gui/pedalboard/pedal_widget.h"
       3              : #include "gui/theme/theme.h"
       4              : #include "gui/commands/command.h"
       5              : #include "audio/effects/amp_simulator.h"
       6              : #include "gui/views/gui_midi.h"
       7              : #include "midi/midi_manager.h"
       8              : 
       9              : #include <cstring>
      10              : #include <imgui.h>
      11              : #include <set>
      12              : #include <algorithm>
      13              : 
      14              : namespace Amplitron {
      15              : 
      16              : /** @brief Construct PedalBoard and build initial widget list from engine state. */
      17           95 : PedalBoard::PedalBoard(AudioEngine& engine, CommandHistory& history, GuiMidi* gui_midi)
      18           76 :     : engine_(engine), history_(history), gui_midi_(gui_midi) {
      19           57 :     rebuild_widgets();
      20           57 : }
      21              : 
      22              : /** @brief Default destructor. */
      23           76 : PedalBoard::~PedalBoard() = default;
      24              : 
      25              : /** @brief Recreate PedalWidget list to match the engine's current effect chain. */
      26          132 : void PedalBoard::rebuild_widgets() {
      27          132 :     std::set<Effect*> prev_visible;
      28          168 :     for (int idx : visible_indices_) {
      29           36 :         if (idx >= 0 && idx < static_cast<int>(widgets_.size())) {
      30           48 :             prev_visible.insert(widgets_[idx]->get_effect().get());
      31           12 :         }
      32              :     }
      33              : 
      34          132 :     widgets_.clear();
      35          132 :     visible_indices_.clear();
      36          132 :     auto& effects = engine_.effects();
      37              : 
      38          132 :     int amp_idx = find_amp_index();
      39              : 
      40          213 :     for (int i = 0; i < static_cast<int>(effects.size()); ++i) {
      41           81 :         auto w = std::make_unique<PedalWidget>(engine_, effects[i], i);
      42           81 :         w->set_history(&history_);
      43           81 :         w->set_gui_midi(gui_midi_);
      44           81 :         widgets_.push_back(std::move(w));
      45              : 
      46           81 :         Effect* ptr = effects[i].get();
      47           81 :         bool is_amp = (amp_idx >= 0 && i == amp_idx);
      48           63 :         bool is_post_amp = (amp_idx >= 0 && i > amp_idx);
      49              : 
      50           81 :         if (is_post_amp) continue;
      51              : 
      52           78 :         if (prev_visible.count(ptr)) {
      53           36 :             visible_indices_.insert(i);
      54           54 :         } else if (effects[i]->is_enabled() || is_amp) {
      55           42 :             visible_indices_.insert(i);
      56           14 :         }
      57           81 :     }
      58          132 : }
      59              : 
      60              : /** @brief Find the index of the current AmpSimulator in the effect chain (-1 if none). */
      61          156 : int PedalBoard::find_amp_index() const {
      62          156 :     auto& fx = engine_.effects();
      63          225 :     for (int i = 0; i < static_cast<int>(fx.size()); ++i) {
      64           99 :         if (std::strcmp(fx[i]->name(), "Amp Sim") == 0) return i;
      65           23 :     }
      66           84 :     return -1;
      67           52 : }
      68              : 
      69              : /** @brief Render the toolbar (add/reset) and the scrollable signal chain area. */
      70            6 : void PedalBoard::render() {
      71            6 :     float bar_height = ImGui::GetFrameHeight() + ImGui::GetStyle().WindowPadding.y * 2.0f + ImGui::GetStyle().WindowBorderSize * 2.0f;
      72            6 :     ImGui::BeginChild("PedalToolbar", ImVec2(0, bar_height), true,
      73              :                        ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
      74              : 
      75            2 :     {
      76            6 :         float avail = ImGui::GetContentRegionAvail().y;
      77            6 :         float row_h = ImGui::GetFrameHeight();
      78            6 :         float offset = std::max(0.0f, (avail - row_h) * 0.5f);
      79            6 :         ImGui::SetCursorPosY(ImGui::GetCursorPosY() + offset);
      80              :     }
      81              :     
      82            6 :     render_add_pedal_menu();
      83            6 :     ImGui::SameLine();
      84              : 
      85            6 :     if (ImGui::Button("Reset All")) {
      86            0 :         show_confirm_reset_ = true;
      87            0 :     }
      88            6 :     ImGui::SameLine();
      89              : 
      90            6 :     if (ImGui::Button("Clear All")) {
      91            0 :         show_confirm_clear_ = true;
      92            0 :     }
      93            6 :     ImGui::SameLine();
      94              : 
      95            6 :     render_midi_menu();
      96            6 :     ImGui::SameLine();
      97              : 
      98            6 :     if (show_confirm_reset_) {
      99            3 :         ImGui::OpenPopup("Confirm Reset##Modal");
     100            3 :         show_confirm_reset_ = false;
     101            1 :     }
     102            6 :     if (show_confirm_clear_) {
     103            3 :         ImGui::OpenPopup("Confirm Clear##Modal");
     104            3 :         show_confirm_clear_ = false;
     105            1 :     }
     106              : 
     107            6 :     if (ImGui::BeginPopupModal("Confirm Reset##Modal", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
     108            0 :         ImGui::Text("Are you sure you want to reset ALL parameters to their default values?\nThis will affect every pedal on the board.");
     109            0 :         ImGui::Separator();
     110            0 :         if (ImGui::Button("Reset", ImVec2(120, 0))) {
     111            0 :             history_.execute(std::make_unique<ResetAllCommand>(engine_));
     112            0 :             ImGui::CloseCurrentPopup();
     113            0 :         }
     114            0 :         ImGui::SetItemDefaultFocus();
     115            0 :         ImGui::SameLine();
     116            0 :         if (ImGui::Button("Cancel", ImVec2(120, 0))) {
     117            0 :             ImGui::CloseCurrentPopup();
     118            0 :         }
     119            0 :         ImGui::EndPopup();
     120            0 :     }
     121              : 
     122            6 :     if (ImGui::BeginPopupModal("Confirm Clear##Modal", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
     123            3 :         ImGui::Text("Are you sure you want to remove ALL pedals from the signal chain?\nThis cannot be undone easily if you have many complex settings.");
     124            3 :         ImGui::Separator();
     125            3 :         if (ImGui::Button("Clear All", ImVec2(120, 0))) {
     126            0 :             history_.execute(std::make_unique<ClearAllCommand>(engine_));
     127            0 :             rebuild_widgets();
     128            0 :             ImGui::CloseCurrentPopup();
     129            0 :         }
     130            3 :         ImGui::SetItemDefaultFocus();
     131            3 :         ImGui::SameLine();
     132            3 :         if (ImGui::Button("Cancel", ImVec2(120, 0))) {
     133            0 :             ImGui::CloseCurrentPopup();
     134            0 :         }
     135            3 :         ImGui::EndPopup();
     136            1 :     }
     137              : 
     138            6 :     if (show_confirm_midi_clear_) {
     139            3 :         ImGui::OpenPopup("Confirm MIDI Clear##Modal");
     140            3 :         show_confirm_midi_clear_ = false;
     141            1 :     }
     142              : 
     143            6 :     if (ImGui::BeginPopupModal("Confirm MIDI Clear##Modal", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
     144            3 :         ImGui::Text("Are you sure you want to clear ALL MIDI CC mappings?");
     145            3 :         ImGui::TextColored(Theme::Gold(), "This action cannot be undone.");
     146            3 :         ImGui::Spacing();
     147              : 
     148            3 :         if (ImGui::Button("Clear All", ImVec2(120, 0))) {
     149            0 :             if (gui_midi_) {
     150            0 :                 gui_midi_->manager().clear_mappings();
     151            0 :             }
     152            0 :             ImGui::CloseCurrentPopup();
     153            0 :         }
     154            3 :         ImGui::SetItemDefaultFocus();
     155            3 :         ImGui::SameLine();
     156            3 :         if (ImGui::Button("Cancel", ImVec2(120, 0))) {
     157            0 :             ImGui::CloseCurrentPopup();
     158            0 :         }
     159            3 :         ImGui::EndPopup();
     160            1 :     }
     161              : 
     162            6 :     ImGui::SameLine();
     163              : 
     164            6 :     render_amp_selector();
     165              : 
     166            6 :     ImGui::SameLine();
     167            6 :     int pedal_count = static_cast<int>(engine_.effects().size());
     168            8 :     ImGui::TextColored(Theme::TextSecondary(),
     169            2 :         "  %d effects | Drag headers to route", pedal_count);
     170              : 
     171            6 :     ImGui::EndChild();
     172              : 
     173              :     // Canvas Window Viewport Layout Setup
     174            6 :     ImGui::BeginChild("PedalArea", ImVec2(0, 0), true,
     175              :         ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
     176              : 
     177            6 :     render_signal_chain();
     178              : 
     179            6 :     ImGui::EndChild();
     180            6 : }
     181              : 
     182              : /** @brief Add an effect to the chain via undo system, rebuild widgets, and make it visible. */
     183            3 : void PedalBoard::add_effect_and_show(std::shared_ptr<Effect> effect) {
     184            3 :     history_.execute(std::make_unique<AddEffectCommand>(engine_, std::move(effect)));
     185            3 :     rebuild_widgets();
     186            3 : }
     187              : 
     188              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1