Files
IQ.Pilot/iqpilot/tools/cabana/ui/chart/signalselector.cc
2026-09-02 16:46:45 -05:00

156 lines
5.6 KiB
C++

#include "tools/cabana/ui/chart/signalselector.h"
#include <algorithm>
#include <cfloat>
#include "imgui.h"
#include "tools/cabana/streams/abstractstream.h"
#include "tools/cabana/ui/chart/chart.h"
#include "tools/cabana/ui/icons.h"
#include "tools/cabana/ui/util.h"
#include "tools/cabana/utils/strings.h"
SignalSelector::SignalSelector(std::string title) : title_(std::move(title)) {
for (const auto &[id, _] : can->lastMessages()) {
if (auto m = dbc()->msg(id)) {
msgs_combo_.push_back({m->name + " (" + id.toString() + ")", id});
}
}
std::sort(msgs_combo_.begin(), msgs_combo_.end(), [](auto &a, auto &b) { return a.text < b.text; });
}
bool SignalSelector::draw() {
if (!open_) return false;
const std::string popup_id = title_ + "###SignalSelector";
if (!show_) {
ImGui::OpenPopup(popup_id.c_str());
show_ = true;
}
setNextDialogWindow(ImVec2(700.0f, 450.0f));
if (!ImGui::BeginPopupModal(popup_id.c_str(), nullptr, ImGuiWindowFlags_NoSavedSettings)) {
open_ = false;
return false;
}
const float btn_w = ImGui::GetFrameHeight() + 8.0f;
const float column_w = (ImGui::GetContentRegionAvail().x - btn_w - ImGui::GetStyle().ItemSpacing.x * 2) / 2;
const float lists_h = ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() * 3;
ImGui::BeginGroup();
ImGui::TextUnformatted("Available Signals");
const char *preview = msgs_combo_index_ >= 0 ? msgs_combo_[msgs_combo_index_].text.c_str() : "Select a msg...";
ImGui::SetNextItemWidth(column_w);
if (ImGui::BeginCombo("##msgs_combo", preview)) {
if (ImGui::IsWindowAppearing()) {
msgs_combo_filter_.clear();
ImGui::SetKeyboardFocusHere();
}
ImGui::SetNextItemWidth(-FLT_MIN);
inputText("##msgs_filter", &msgs_combo_filter_, "Select a msg...");
for (int i = 0; i < (int)msgs_combo_.size(); ++i) {
if (!msgs_combo_filter_.empty() && !utils::containsCI(msgs_combo_[i].text, msgs_combo_filter_)) continue;
if (ImGui::Selectable(msgs_combo_[i].text.c_str(), i == msgs_combo_index_)) {
msgs_combo_index_ = i;
updateAvailableList(i);
ImGui::CloseCurrentPopup();
}
}
ImGui::EndCombo();
}
bool add_dbl = false;
drawList("##available_list", available_list_, &available_row_, false, &add_dbl, ImVec2(column_w, lists_h));
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Dummy(ImVec2(btn_w, (lists_h + ImGui::GetFrameHeightWithSpacing() * 2) / 2 - ImGui::GetFrameHeight()));
ImGui::BeginDisabled(available_row_ == -1);
bool add_clicked = ImGui::Button(icon::CHEVRON_RIGHT, ImVec2(btn_w, 0));
ImGui::EndDisabled();
ImGui::BeginDisabled(selected_row_ == -1);
bool remove_clicked = ImGui::Button(icon::CHEVRON_LEFT, ImVec2(btn_w, 0));
ImGui::EndDisabled();
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::TextUnformatted("Selected Signals");
bool remove_dbl = false;
drawList("##selected_list", selected_list_, &selected_row_, true, &remove_dbl, ImVec2(column_w, lists_h + ImGui::GetFrameHeightWithSpacing()));
bool rejected = false;
dialogButtons("OK", &accepted_, &rejected);
const bool done = accepted_ || rejected;
ImGui::EndGroup();
if ((add_dbl || add_clicked) && available_row_ >= 0 && available_row_ < (int)available_list_.size()) {
add(available_row_);
} else if ((remove_dbl || remove_clicked) && selected_row_ >= 0 && selected_row_ < (int)selected_list_.size()) {
remove(selected_row_);
}
if (done) {
open_ = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
return open_;
}
void SignalSelector::drawList(const char *id, std::vector<ListItem> &list, int *current_row, bool show_msg_name, bool *double_clicked, const ImVec2 &size) {
if (!ImGui::BeginListBox(id, size)) return;
for (int i = 0; i < (int)list.size(); ++i) {
const auto &item = list[i];
ImGui::PushID(i);
const ImVec2 pos = ImGui::GetCursorScreenPos();
if (ImGui::Selectable("##item", i == *current_row)) *current_row = i;
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
*current_row = i;
*double_clicked = true;
}
ImDrawList *dl = ImGui::GetWindowDrawList();
float x = pos.x + 5;
drawColorMarker(dl, ImVec2(x, pos.y), toImU32(item.sig->color));
x += markerSize() + 4;
dl->AddText(ImVec2(x, pos.y), ImGui::GetColorU32(ImGuiCol_Text), item.sig->name.c_str());
if (show_msg_name) {
x += ImGui::CalcTextSize(item.sig->name.c_str()).x;
dl->AddText(ImVec2(x, pos.y), ImGui::GetColorU32(ImGuiCol_TextDisabled), msgLabel(item.msg_id).c_str());
}
ImGui::PopID();
}
ImGui::EndListBox();
}
void SignalSelector::add(int row) {
const auto &item = available_list_[row];
selected_list_.emplace_back(item.msg_id, item.sig);
available_list_.erase(available_list_.begin() + row);
available_row_ = -1;
}
void SignalSelector::remove(int row) {
const auto &item = selected_list_[row];
if (msgs_combo_index_ >= 0 && item.msg_id == msgs_combo_[msgs_combo_index_].id) {
available_list_.emplace_back(item.msg_id, item.sig);
}
selected_list_.erase(selected_list_.begin() + row);
selected_row_ = -1;
}
void SignalSelector::updateAvailableList(int index) {
if (index == -1) return;
available_list_.clear();
available_row_ = -1;
MessageId msg_id = msgs_combo_[index].id;
for (auto s : dbc()->msg(msg_id)->getSignals()) {
bool is_selected = std::any_of(selected_list_.begin(), selected_list_.end(),
[sig = s, &msg_id](auto &it) { return it.msg_id == msg_id && it.sig == sig; });
if (!is_selected) {
available_list_.emplace_back(msg_id, s);
}
}
}