Files
MARTe-Integrated-Components/Client/streamhub/SourcePanel.cpp
2026-06-25 00:45:45 +02:00

118 lines
4.3 KiB
C++

/**
* @file SourcePanel.cpp
* @brief Source browser sidebar implementation.
*/
#include "SourcePanel.h"
#include "App.h"
#include "Protocol.h"
#include "imgui.h"
#include "implot.h"
#include "Icons.h"
#include <cstdio>
#include <algorithm>
namespace StreamHubClient {
void RenderSourcePanel(App& app) {
auto& sources = app.sources();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.f, 2.f));
if (ImGui::CollapsingHeader("Sources", ImGuiTreeNodeFlags_DefaultOpen)) {
for (int srcIdx = 0; srcIdx < static_cast<int>(sources.size()); srcIdx++) {
Source& src = sources[srcIdx];
/* State color indicator */
ImVec4 stateColor = (src.state == "connected")
? ImVec4(0.1f, 0.9f, 0.3f, 1.f)
: ImVec4(0.8f, 0.3f, 0.3f, 1.f);
ImGui::PushID(srcIdx);
ImGui::TextColored(stateColor, ICON_FA_CIRCLE);
ImGui::SameLine();
std::string treeLabel = src.label.empty() ? src.id : src.label;
bool nodeOpen = ImGui::TreeNodeEx(treeLabel.c_str(),
ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth);
/* Per-source context menu: remove */
if (ImGui::BeginPopupContextItem("##srccmenu")) {
if (ImGui::MenuItem(ICON_FA_TRASH_CAN " Remove source")) {
app.ws().sendText(BuildRemoveSource(src.id));
}
ImGui::EndPopup();
}
if (nodeOpen) {
for (int sigIdx = 0; sigIdx < static_cast<int>(src.signals.size()); sigIdx++) {
Signal& sig = src.signals[sigIdx];
ImGui::PushID(sigIdx);
/* Color swatch */
ImVec4 col = sig.color;
if (ImGui::ColorButton("##col", col,
ImGuiColorEditFlags_NoTooltip |
ImGuiColorEditFlags_NoBorder,
ImVec2(12.f, 12.f))) {
ImGui::OpenPopup("##sigcolor");
}
if (ImGui::BeginPopup("##sigcolor")) {
ImGui::ColorPicker4("Signal Color", reinterpret_cast<float*>(&sig.color),
ImGuiColorEditFlags_NoAlpha);
ImGui::EndPopup();
}
ImGui::SameLine();
/* Unit / type label */
char label[128];
if (sig.meta.numElements > 1) {
snprintf(label, sizeof(label), "%s [%u]",
sig.meta.name.c_str(), sig.meta.numElements);
} else {
snprintf(label, sizeof(label), "%s", sig.meta.name.c_str());
}
if (!sig.meta.unit.empty()) {
strncat(label, " (", sizeof(label) - strlen(label) - 1);
strncat(label, sig.meta.unit.c_str(), sizeof(label) - strlen(label) - 1);
strncat(label, ")", sizeof(label) - strlen(label) - 1);
}
ImGui::Selectable(label, false, 0, ImVec2(0, 0));
/* Drag-and-drop source: payload = (srcIdx, sigIdx) */
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
struct DragPayload { int srcIdx; int sigIdx; };
DragPayload payload{srcIdx, sigIdx};
ImGui::SetDragDropPayload("SIGNAL_REF", &payload, sizeof(payload));
ImGui::TextUnformatted(sig.meta.name.c_str());
ImGui::EndDragDropSource();
}
ImGui::PopID();
}
ImGui::TreePop();
}
ImGui::PopID();
}
}
ImGui::PopStyleVar();
/* ── Add / Save sources (inline at the bottom of the sidebar) ──────── */
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(ICON_FA_PLUS " Add Source", ImVec2(-1.f, 0.f))) {
app.showAddSrc() = true;
}
if (ImGui::Button(ICON_FA_FLOPPY_DISK " Save Sources", ImVec2(-1.f, 0.f))) {
app.ws().sendText(BuildSaveSources());
}
}
} /* namespace StreamHubClient */