LCOV - code coverage report
Current view: top level - src/gui/pedalboard - pedal_board.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 78.1 % 137 107
Test Date: 2026-06-07 15:51:50 Functions: 100.0 % 6 6

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

Generated by: LCOV version 2.0-1