mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-09-29 21:33:42 +08:00
Cabana: standardize floating dropdown menus (#38874)
* Cabana: standardize floating dropdown menus * Cabana: use uniform items in menus and combo lists * Cabana: remove dropdown test and explanatory comments
This commit is contained in:
@@ -49,20 +49,14 @@ ChartView::ChartView(const std::pair<double, double> &x_range, ChartsWidget *par
|
||||
}
|
||||
|
||||
void ChartView::drawMenuActions() {
|
||||
// the current series type is marked with a radio bullet on the left
|
||||
const float indent = ImGui::GetFontSize();
|
||||
float label_width = ImGui::CalcTextSize("Manage Signals").x;
|
||||
for (const char *type : SERIES_TYPE_NAMES) label_width = std::max(label_width, ImGui::CalcTextSize(type).x);
|
||||
for (int i = 0; i < (int)std::size(SERIES_TYPE_NAMES); ++i) {
|
||||
if (radioMenuItem(SERIES_TYPE_NAMES[i], i == (int)series_type_, indent + label_width + indent)) {
|
||||
if (dropdown::Item(SERIES_TYPE_NAMES[i], nullptr, i == (int)series_type_)) {
|
||||
setSeriesType((SeriesType)i);
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Indent(indent);
|
||||
if (ImGui::MenuItem("Manage Signals")) manageSignals();
|
||||
if (ImGui::MenuItem("Split Chart", nullptr, false, sigs_.size() > 1)) charts_widget_->splitChart(this);
|
||||
ImGui::Unindent(indent);
|
||||
if (dropdown::Item("Manage Signals")) manageSignals();
|
||||
if (dropdown::Item("Split Chart", nullptr, false, sigs_.size() > 1)) charts_widget_->splitChart(this);
|
||||
}
|
||||
|
||||
// the buttons and their menus are drawn every frame, at the rects updateLayout() placed them at
|
||||
@@ -72,9 +66,9 @@ void ChartView::createToolButtons() {
|
||||
|
||||
ImGui::SetCursorScreenPos(layout_.manage_btn_rect.Min);
|
||||
if (iconButton("manage_btn", icon::THREE_DOTS_VERTICAL, "")) ImGui::OpenPopup("manage_menu");
|
||||
if (ImGui::BeginPopup("manage_menu")) {
|
||||
if (dropdown::BeginPopup("manage_menu")) {
|
||||
drawMenuActions();
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
|
||||
if (close_clicked) charts_widget_->removeChart(this);
|
||||
@@ -331,23 +325,19 @@ void ChartView::drawContextMenu() {
|
||||
ImGui::OpenPopup("context_menu");
|
||||
}
|
||||
context_menu_id_ = ImGui::GetID("context_menu");
|
||||
if (ImGui::BeginPopup("context_menu")) {
|
||||
if (dropdown::BeginPopup("context_menu")) {
|
||||
drawMenuActions();
|
||||
// the menu holds checkable entries, so every entry keeps the same left margin
|
||||
const float indent = ImGui::GetFontSize();
|
||||
ImGui::Indent(indent);
|
||||
ImGui::Separator();
|
||||
// the zoom entries come from the toolbar, where they are only visible while zoomed
|
||||
if (can->timeRange().has_value()) {
|
||||
const std::string undo_text = std::string(icon::ARROW_COUNTERCLOCKWISE) + " Undo Zoom";
|
||||
const std::string redo_text = std::string(icon::ARROW_CLOCKWISE) + " Redo Zoom";
|
||||
if (ImGui::MenuItem(undo_text.c_str(), nullptr, false, charts_widget_->zoom_undo_stack_.canUndo())) charts_widget_->zoom_undo_stack_.undo();
|
||||
if (ImGui::MenuItem(redo_text.c_str(), nullptr, false, charts_widget_->zoom_undo_stack_.canRedo())) charts_widget_->zoom_undo_stack_.redo();
|
||||
if (dropdown::Item(undo_text.c_str(), nullptr, false, charts_widget_->zoom_undo_stack_.canUndo())) charts_widget_->zoom_undo_stack_.undo();
|
||||
if (dropdown::Item(redo_text.c_str(), nullptr, false, charts_widget_->zoom_undo_stack_.canRedo())) charts_widget_->zoom_undo_stack_.redo();
|
||||
ImGui::Separator();
|
||||
}
|
||||
if (ImGui::MenuItem("Close")) charts_widget_->removeChart(this);
|
||||
ImGui::Unindent(indent);
|
||||
ImGui::EndPopup();
|
||||
if (dropdown::Item("Close")) charts_widget_->removeChart(this);
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ ChartsWidget::ChartsWidget() {
|
||||
connections_.push_back(seriesChanged.connect([this]() { updateTabBar(); }));
|
||||
connections_.push_back(tabbar_.tabCloseRequested.connect([this](int index) { removeTab(index); }));
|
||||
connections_.push_back(tabbar_.tabContextMenu.connect([this](int index) {
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
if (ImGui::MenuItem("Close Other Tabs")) {
|
||||
if (dropdown::BeginPopupContextItem()) {
|
||||
if (dropdown::Item("Close Other Tabs")) {
|
||||
tabbar_.moveTab(index, 0);
|
||||
tabbar_.setCurrentIndex(0);
|
||||
while (tabbar_.count() > 1) removeTab(1);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
}));
|
||||
connections_.push_back(tabbar_.currentChanged.connect([this](int index) {
|
||||
@@ -180,7 +180,7 @@ void ChartsWidget::drawToolBar() {
|
||||
const std::string chart_type_text = std::string("Type: ") + SERIES_TYPE_NAMES[std::clamp(settings.chart_series_type, 0, type_count - 1)];
|
||||
auto chart_type_items = [this]() {
|
||||
for (int i = 0; i < type_count; ++i) {
|
||||
if (ImGui::MenuItem(SERIES_TYPE_NAMES[i], nullptr, settings.chart_series_type == i)) {
|
||||
if (dropdown::Item(SERIES_TYPE_NAMES[i], nullptr, settings.chart_series_type == i)) {
|
||||
settings.chart_series_type = i;
|
||||
settingChanged();
|
||||
}
|
||||
@@ -192,7 +192,7 @@ void ChartsWidget::drawToolBar() {
|
||||
if (columns_action_visible_) {
|
||||
auto column_items = [this]() {
|
||||
for (int i = 0; i < MAX_COLUMN_COUNT; ++i) {
|
||||
if (ImGui::MenuItem(std::to_string(i + 1).c_str(), nullptr, column_count_ == i + 1)) setColumnCount(i + 1);
|
||||
if (dropdown::Item(std::to_string(i + 1).c_str(), nullptr, column_count_ == i + 1)) setColumnCount(i + 1);
|
||||
}
|
||||
};
|
||||
items.push_back(toolbarMenu("columns", columns_action_text, "Columns", column_items));
|
||||
|
||||
@@ -46,7 +46,7 @@ bool SignalSelector::draw() {
|
||||
// a combo popup with a filter box
|
||||
const char *preview = msgs_combo_index_ >= 0 ? msgs_combo_[msgs_combo_index_].text.c_str() : "Select a message...";
|
||||
ImGui::SetNextItemWidth(column_w);
|
||||
if (ImGui::BeginCombo("##msgs_combo", preview)) {
|
||||
if (dropdown::BeginCombo("##msgs_combo", preview)) {
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
msgs_combo_filter_.clear(); // reopen showing the full list
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
@@ -55,13 +55,13 @@ bool SignalSelector::draw() {
|
||||
inputText("##msgs_filter", &msgs_combo_filter_, "Select a message...");
|
||||
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 (selectable(msgs_combo_[i].text.c_str(), i == msgs_combo_index_)) {
|
||||
if (dropdown::Item(msgs_combo_[i].text.c_str(), nullptr, i == msgs_combo_index_)) {
|
||||
msgs_combo_index_ = i;
|
||||
updateAvailableList(i);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
dropdown::EndCombo();
|
||||
}
|
||||
bool add_dbl = false;
|
||||
drawList("##available_list", available_list_, &available_row_, false, &add_dbl, ImVec2(column_w, lists_h));
|
||||
|
||||
@@ -95,7 +95,7 @@ void RoutesDialog::draw() {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if (ImGui::Combo("##period", &s_.period_index, PERIOD_NAMES, IM_ARRAYSIZE(PERIOD_NAMES))) fetchRoutes();
|
||||
if (dropdown::Combo("##period", &s_.period_index, PERIOD_NAMES, IM_ARRAYSIZE(PERIOD_NAMES))) fetchRoutes();
|
||||
|
||||
bool accepted = false, rejected = false;
|
||||
const float footer = ImGui::GetFrameHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y;
|
||||
|
||||
@@ -60,14 +60,14 @@ void SettingsDialog::draw() {
|
||||
static const char *themes[] = {"Light", "Dark"};
|
||||
formRow(THEME, label_width);
|
||||
int theme_index = theme_ - LIGHT_THEME;
|
||||
if (ImGui::Combo("##theme", &theme_index, themes, IM_ARRAYSIZE(themes))) theme_ = theme_index + LIGHT_THEME;
|
||||
if (dropdown::Combo("##theme", &theme_index, themes, IM_ARRAYSIZE(themes))) theme_ = theme_index + LIGHT_THEME;
|
||||
formRow(CACHED_MINUTES, label_width);
|
||||
settingInputInt("cached_minutes", &cached_minutes_, 1, 10, MIN_CACHE_MINUTES, MAX_CACHE_MINUTES);
|
||||
|
||||
ImGui::SeparatorText("New Signal Settings");
|
||||
static const char *directions[] = {"MSB First", "LSB First", "Always Little Endian", "Always Big Endian"};
|
||||
formRow(DRAG_DIRECTION, label_width);
|
||||
ImGui::Combo("##drag_direction", &drag_direction_, directions, IM_ARRAYSIZE(directions));
|
||||
dropdown::Combo("##drag_direction", &drag_direction_, directions, IM_ARRAYSIZE(directions));
|
||||
|
||||
ImGui::SeparatorText("Chart");
|
||||
formRow(CHART_HEIGHT, label_width);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "tools/cabana/ui/theme.h"
|
||||
|
||||
namespace dropdown {
|
||||
constexpr float PADDING_X = 9.0f;
|
||||
constexpr float PADDING_Y = 6.0f;
|
||||
constexpr float SPACING_X = 10.0f;
|
||||
constexpr float SPACING_Y = 8.0f;
|
||||
constexpr float ROUNDING = 6.0f;
|
||||
constexpr float BORDER = 1.0f;
|
||||
constexpr int WINDOW_STYLE_VARS = 3;
|
||||
|
||||
inline void pushStyle() {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(PADDING_X, PADDING_Y));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, ROUNDING);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, BORDER);
|
||||
}
|
||||
|
||||
inline bool finishBegin(bool open) {
|
||||
if (open) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(SPACING_X, SPACING_Y));
|
||||
else ImGui::PopStyleVar(WINDOW_STYLE_VARS);
|
||||
return open;
|
||||
}
|
||||
inline bool BeginPopup(const char *id, ImGuiWindowFlags flags = 0) {
|
||||
pushStyle();
|
||||
return finishBegin(ImGui::BeginPopup(id, flags));
|
||||
}
|
||||
inline bool BeginPopupContextItem(const char *id = nullptr, ImGuiPopupFlags flags = ImGuiPopupFlags_MouseButtonRight) {
|
||||
pushStyle();
|
||||
return finishBegin(ImGui::BeginPopupContextItem(id, flags));
|
||||
}
|
||||
inline void EndPopup() {
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndPopup();
|
||||
ImGui::PopStyleVar(WINDOW_STYLE_VARS);
|
||||
}
|
||||
inline bool BeginMenu(const char *label, bool enabled = true) {
|
||||
pushStyle();
|
||||
return finishBegin(ImGui::BeginMenu(label, enabled));
|
||||
}
|
||||
inline void EndMenu() {
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndMenu();
|
||||
ImGui::PopStyleVar(WINDOW_STYLE_VARS);
|
||||
}
|
||||
inline bool BeginCombo(const char *label, const char *preview, ImGuiComboFlags flags = 0) {
|
||||
pushStyle();
|
||||
return finishBegin(ImGui::BeginCombo(label, preview, flags));
|
||||
}
|
||||
inline void EndCombo() {
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndCombo();
|
||||
ImGui::PopStyleVar(WINDOW_STYLE_VARS);
|
||||
}
|
||||
|
||||
inline bool Item(const char *label, const char *shortcut = nullptr, bool selected = false, bool enabled = true) {
|
||||
return ImGui::MenuItem(label, shortcut, selected, enabled);
|
||||
}
|
||||
inline bool Item(const char *label, const char *shortcut, bool *selected, bool enabled = true) {
|
||||
if (!Item(label, shortcut, selected && *selected, enabled)) return false;
|
||||
if (selected) *selected = !*selected;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Combo(const char *label, int *index, const char *const items[], int count) {
|
||||
bool changed = false;
|
||||
if (BeginCombo(label, *index >= 0 && *index < count ? items[*index] : "")) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ImGui::PushID(i);
|
||||
if (Item(items[i], nullptr, i == *index) && i != *index) {
|
||||
*index = i;
|
||||
changed = true;
|
||||
}
|
||||
if (i == *index) ImGui::SetItemDefaultFocus();
|
||||
ImGui::PopID();
|
||||
}
|
||||
EndCombo();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
inline bool Combo(const char *label, int *index, const char *items) {
|
||||
std::vector<const char *> labels;
|
||||
for (const char *item = items; *item; item += std::strlen(item) + 1) labels.push_back(item);
|
||||
return Combo(label, index, labels.data(), labels.size());
|
||||
}
|
||||
} // namespace dropdown
|
||||
@@ -91,70 +91,47 @@ void MainWindow::loadFingerprints() {
|
||||
|
||||
void MainWindow::drawFileMenu() {
|
||||
const bool has_stream = hasStream();
|
||||
if (ImGui::MenuItem("Open Stream...")) selectAndOpenStream();
|
||||
if (ImGui::MenuItem("Close Stream", nullptr, false, has_stream)) closeStream();
|
||||
if (ImGui::MenuItem("Export to CSV...", nullptr, false, has_stream)) exportToCSV();
|
||||
if (dropdown::Item("Open Stream...")) selectAndOpenStream();
|
||||
if (dropdown::Item("Close Stream", nullptr, false, has_stream)) closeStream();
|
||||
if (dropdown::Item("Export to CSV...", nullptr, false, has_stream)) exportToCSV();
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("New DBC File", shortcut("N").c_str())) newFile();
|
||||
if (ImGui::MenuItem("Open DBC File...", shortcut("O").c_str())) openFile();
|
||||
if (dropdown::Item("New DBC File", shortcut("N").c_str())) newFile();
|
||||
if (dropdown::Item("Open DBC File...", shortcut("O").c_str())) openFile();
|
||||
|
||||
if (ImGui::BeginMenu("Manage DBC Files", has_stream)) {
|
||||
if (dropdown::BeginMenu("Manage DBC Files", has_stream)) {
|
||||
drawManageDBCsMenu();
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Open Recent")) {
|
||||
if (dropdown::BeginMenu("Open Recent")) {
|
||||
drawRecentFilesMenu();
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginMenu("Load DBC from commaai/opendbc")) {
|
||||
if (dropdown::BeginMenu("Load DBC from commaai/opendbc")) {
|
||||
for (const auto &name : opendbc_names_) {
|
||||
if (ImGui::MenuItem(name.c_str())) loadDBCFromOpendbc(name);
|
||||
if (dropdown::Item(name.c_str())) loadDBCFromOpendbc(name);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("Load DBC from Clipboard")) loadFromClipboard();
|
||||
if (dropdown::Item("Load DBC from Clipboard")) loadFromClipboard();
|
||||
|
||||
ImGui::Separator();
|
||||
const int cnt = dbc()->nonEmptyDBCCount();
|
||||
const std::string save_text = cnt > 1 ? "Save " + std::to_string(cnt) + " DBCs..." : "Save DBC...";
|
||||
if (ImGui::MenuItem(save_text.c_str(), shortcut("S").c_str(), false, cnt > 0)) save();
|
||||
if (ImGui::MenuItem("Save DBC As...", shortcut("Shift+S").c_str(), false, cnt == 1)) saveAs();
|
||||
if (dropdown::Item(save_text.c_str(), shortcut("S").c_str(), false, cnt > 0)) save();
|
||||
if (dropdown::Item("Save DBC As...", shortcut("Shift+S").c_str(), false, cnt == 1)) saveAs();
|
||||
// TODO: Support clipboard for multiple files
|
||||
if (ImGui::MenuItem("Copy DBC to Clipboard", nullptr, false, cnt == 1)) saveToClipboard();
|
||||
if (dropdown::Item("Copy DBC to Clipboard", nullptr, false, cnt == 1)) saveToClipboard();
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Settings...")) openSettings();
|
||||
if (dropdown::Item("Settings...")) openSettings();
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Exit", shortcut("Q").c_str())) close();
|
||||
if (dropdown::Item("Exit", shortcut("Q").c_str())) close();
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool beginTopMenu(const char *label, bool enabled = true) {
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiCol_Header));
|
||||
const bool open = ImGui::BeginMenu(label, enabled);
|
||||
ImGui::PopStyleColor();
|
||||
if (open) {
|
||||
// Erase the popup's rounded top border so it joins the menu bar's separator.
|
||||
const ImGuiStyle &style = ImGui::GetStyle();
|
||||
const ImGuiWindow *w = ImGui::GetCurrentWindow();
|
||||
const float r = style.PopupRounding, b = style.PopupBorderSize;
|
||||
const ImVec2 min = w->Pos, max(w->Pos.x + w->Size.x, w->Pos.y + w->Size.y);
|
||||
const ImU32 bg = ImGui::GetColorU32(ImGuiCol_PopupBg), border = ImGui::GetColorU32(ImGuiCol_Border);
|
||||
ImDrawList *dl = w->DrawList;
|
||||
dl->PushClipRect(min, max, false); // the window's own clip rect excludes its border
|
||||
dl->AddRectFilled(min, ImVec2(max.x, min.y + r), bg);
|
||||
dl->AddRectFilled(ImVec2(min.x, min.y), ImVec2(min.x + b, min.y + r), border);
|
||||
dl->AddRectFilled(ImVec2(max.x - b, min.y), ImVec2(max.x, min.y + r), border);
|
||||
dl->PopClipRect();
|
||||
}
|
||||
return open;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MainWindow::drawMenuBar() {
|
||||
// Avoid a double border with the separator drawn below.
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
@@ -166,43 +143,43 @@ void MainWindow::drawMenuBar() {
|
||||
const ImVec2 max(min.x + ImGui::GetWindowWidth(), min.y + ImGui::GetWindowHeight());
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(min.x, max.y - 1.0f), max, ImGui::GetColorU32(ImGuiCol_Border));
|
||||
}
|
||||
if (beginTopMenu("File")) {
|
||||
if (dropdown::BeginMenu("File")) {
|
||||
drawFileMenu();
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
|
||||
if (beginTopMenu("Edit")) {
|
||||
if (dropdown::BeginMenu("Edit")) {
|
||||
auto stack = UndoStack::instance();
|
||||
const std::string undo_text = stack->canUndo() ? "Undo " + stack->undoText() : "Undo";
|
||||
const std::string redo_text = stack->canRedo() ? "Redo " + stack->redoText() : "Redo";
|
||||
if (ImGui::MenuItem(undo_text.c_str(), shortcut("Z").c_str(), false, stack->canUndo())) stack->undo();
|
||||
if (ImGui::MenuItem(redo_text.c_str(), shortcut("Shift+Z").c_str(), false, stack->canRedo())) stack->redo();
|
||||
ImGui::EndMenu();
|
||||
if (dropdown::Item(undo_text.c_str(), shortcut("Z").c_str(), false, stack->canUndo())) stack->undo();
|
||||
if (dropdown::Item(redo_text.c_str(), shortcut("Shift+Z").c_str(), false, stack->canRedo())) stack->redo();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
|
||||
if (beginTopMenu("View")) {
|
||||
if (ImGui::MenuItem("Full Screen", shortcut("F11").c_str())) toggleFullScreen();
|
||||
if (dropdown::BeginMenu("View")) {
|
||||
if (dropdown::Item("Full Screen", shortcut("F11").c_str())) toggleFullScreen();
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem(messages_widget_ ? messages_widget_->title().c_str() : "MESSAGES", nullptr, &messages_visible_);
|
||||
ImGui::MenuItem(video_dock_title_.empty() ? "Video" : video_dock_title_.c_str(), nullptr, &video_visible_);
|
||||
dropdown::Item(messages_widget_ ? messages_widget_->title().c_str() : "MESSAGES", nullptr, &messages_visible_);
|
||||
dropdown::Item(video_dock_title_.empty() ? "Video" : video_dock_title_.c_str(), nullptr, &video_visible_);
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Reset Window Layout")) {
|
||||
if (dropdown::Item("Reset Window Layout")) {
|
||||
messages_visible_ = video_visible_ = true;
|
||||
video_splitter_ratio_ = -1.0f;
|
||||
reset_layout_ = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
|
||||
if (beginTopMenu("Tools", hasStream())) {
|
||||
if (ImGui::MenuItem("Find Similar Bits")) findSimilarBits();
|
||||
if (ImGui::MenuItem("Find Signal")) findSignal();
|
||||
ImGui::EndMenu();
|
||||
if (dropdown::BeginMenu("Tools", hasStream())) {
|
||||
if (dropdown::Item("Find Similar Bits")) findSimilarBits();
|
||||
if (dropdown::Item("Find Signal")) findSignal();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
|
||||
if (beginTopMenu("Help")) {
|
||||
if (ImGui::MenuItem("Help", "F1")) toggleHelp();
|
||||
ImGui::EndMenu();
|
||||
if (dropdown::BeginMenu("Help")) {
|
||||
if (dropdown::Item("Help", "F1")) toggleHelp();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
@@ -527,22 +504,22 @@ void MainWindow::drawManageDBCsMenu() {
|
||||
auto dbc_file = dbc()->findDBCFile(source);
|
||||
const std::string title = "Bus " + std::to_string(source) + " (" + (dbc_file ? dbc_file->name() : "No DBCs loaded") + ")";
|
||||
ImGui::PushID(source);
|
||||
if (ImGui::BeginMenu(title.c_str())) {
|
||||
if (ImGui::MenuItem("New DBC File")) newFile(ss);
|
||||
if (ImGui::MenuItem("Open DBC File...")) openFile(ss);
|
||||
if (ImGui::MenuItem("Load DBC from Clipboard")) loadFromClipboard(ss, false);
|
||||
if (dropdown::BeginMenu(title.c_str())) {
|
||||
if (dropdown::Item("New DBC File")) newFile(ss);
|
||||
if (dropdown::Item("Open DBC File...")) openFile(ss);
|
||||
if (dropdown::Item("Load DBC from Clipboard")) loadFromClipboard(ss, false);
|
||||
|
||||
// Show sub-menu for each dbc for this source.
|
||||
if (dbc_file) {
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem((dbc_file->name() + " (" + toString(dbc()->sources(dbc_file)) + ")").c_str(), nullptr, false, false);
|
||||
if (ImGui::MenuItem("Save...")) saveFile(dbc_file);
|
||||
if (ImGui::MenuItem("Save As...")) saveFileAs(dbc_file);
|
||||
if (ImGui::MenuItem("Copy to Clipboard")) saveFileToClipboard(dbc_file);
|
||||
if (ImGui::MenuItem("Remove from This Bus...")) closeFile(ss, {});
|
||||
if (ImGui::MenuItem("Remove from All Buses...")) closeFile(dbc_file);
|
||||
dropdown::Item((dbc_file->name() + " (" + toString(dbc()->sources(dbc_file)) + ")").c_str(), nullptr, false, false);
|
||||
if (dropdown::Item("Save...")) saveFile(dbc_file);
|
||||
if (dropdown::Item("Save As...")) saveFileAs(dbc_file);
|
||||
if (dropdown::Item("Copy to Clipboard")) saveFileToClipboard(dbc_file);
|
||||
if (dropdown::Item("Remove from This Bus...")) closeFile(ss, {});
|
||||
if (dropdown::Item("Remove from All Buses...")) closeFile(dbc_file);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
@@ -560,14 +537,14 @@ void MainWindow::updateRecentFiles(const std::string &fn) {
|
||||
void MainWindow::drawRecentFilesMenu() {
|
||||
int num_recent_files = std::min<int>(settings.recent_files.size(), MAX_RECENT_FILES);
|
||||
if (!num_recent_files) {
|
||||
ImGui::MenuItem("No Recent Files", nullptr, false, false);
|
||||
dropdown::Item("No Recent Files", nullptr, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_recent_files; ++i) {
|
||||
std::string text = std::to_string(i + 1) + " " + std::filesystem::path(settings.recent_files[i]).filename().string();
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::MenuItem(text.c_str())) loadFile(settings.recent_files[i]);
|
||||
if (dropdown::Item(text.c_str())) loadFile(settings.recent_files[i]);
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ void FindSignalDlg::drawFindGroup() {
|
||||
ImGui::TextUnformatted("Value");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(90);
|
||||
ImGui::Combo("##compare", &compare_, compare_items, compare_count);
|
||||
dropdown::Combo("##compare", &compare_, compare_items, compare_count);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(80);
|
||||
if (ImGui::IsWindowAppearing()) ImGui::SetKeyboardFocusHere();
|
||||
@@ -319,12 +319,12 @@ void FindSignalDlg::setInitialSignals() {
|
||||
}
|
||||
|
||||
void FindSignalDlg::drawContextMenu(int row) {
|
||||
if (ImGui::BeginPopupContextItem("menu")) {
|
||||
if (ImGui::MenuItem("Create Signal")) {
|
||||
if (dropdown::BeginPopupContextItem("menu")) {
|
||||
if (dropdown::Item("Create Signal")) {
|
||||
auto &s = search_.filtered_signals[row];
|
||||
UndoStack::instance()->push(new AddSigCommand(s.id, s.sig));
|
||||
openMessage(s.id);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ bool FindSimilarBitsDlg::draw() {
|
||||
ImGui::TextUnformatted("Equal");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(60);
|
||||
ImGui::Combo("##equal", &equal_, "Yes\0No\0");
|
||||
dropdown::Combo("##equal", &equal_, "Yes\0No\0");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted("Minimum Message Count");
|
||||
ImGui::SameLine();
|
||||
|
||||
@@ -103,17 +103,17 @@ bool selectable(const char *label, bool selected, ImGuiSelectableFlags flags, co
|
||||
bool comboBox(const char *label, int *index, const std::vector<std::string> &items) {
|
||||
bool changed = false;
|
||||
const int count = (int)items.size();
|
||||
if (ImGui::BeginCombo(label, *index >= 0 && *index < count ? items[*index].c_str() : "")) {
|
||||
if (dropdown::BeginCombo(label, *index >= 0 && *index < count ? items[*index].c_str() : "")) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ImGui::PushID(i);
|
||||
if (selectable(items[i].c_str(), i == *index) && *index != i) {
|
||||
if (dropdown::Item(items[i].c_str(), nullptr, i == *index) && *index != i) {
|
||||
*index = i;
|
||||
changed = true;
|
||||
}
|
||||
if (i == *index) ImGui::SetItemDefaultFocus();
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
dropdown::EndCombo();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
@@ -248,18 +248,6 @@ void disabledItemTooltip(const char *text) {
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_AllowWhenDisabled)) ImGui::SetTooltip("%s", text);
|
||||
}
|
||||
|
||||
bool radioMenuItem(const char *label, bool checked, float width) {
|
||||
const float indent = ImGui::GetFontSize();
|
||||
const ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
const bool clicked = selectable((std::string("##") + label).c_str(), false, ImGuiSelectableFlags_None,
|
||||
ImVec2(ImMax(width, ImGui::GetContentRegionAvail().x), 0.0f));
|
||||
const ImU32 color = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
ImDrawList *painter = ImGui::GetWindowDrawList();
|
||||
if (checked) ImGui::RenderBullet(painter, ImVec2(pos.x + indent / 2, pos.y + ImGui::GetTextLineHeight() / 2), color);
|
||||
painter->AddText(ImVec2(pos.x + indent, pos.y), color, label);
|
||||
return clicked;
|
||||
}
|
||||
|
||||
bool PopupOwner::begin(const char *id) {
|
||||
ImGuiWindow *window = ImGui::GetCurrentWindowRead(); // GetCurrentWindow() would mark the fallback window as used
|
||||
if (popup_id == 0) {
|
||||
@@ -510,9 +498,9 @@ ToolbarItem toolbarMenu(const char *id, const std::string &text, const char *lab
|
||||
ToolbarItem item{width, [id, text, items, bold, width]() {
|
||||
const std::string popup_id = std::string(id) + "_menu";
|
||||
menuButton(id, text, popup_id.c_str(), bold, width);
|
||||
if (ImGui::BeginPopup(popup_id.c_str())) {
|
||||
if (dropdown::BeginPopup(popup_id.c_str())) {
|
||||
items();
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
}, label};
|
||||
item.submenu = std::move(items);
|
||||
@@ -579,21 +567,21 @@ void drawToolbar(const std::vector<ToolbarItem> &items, size_t spacer_index, flo
|
||||
}
|
||||
// the popup opens inward: its right edge is aligned with the button so it stays inside the window
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y), ImGuiCond_Always, ImVec2(1, 0));
|
||||
if (ImGui::BeginPopup("toolbar_extension_menu")) {
|
||||
if (dropdown::BeginPopup("toolbar_extension_menu")) {
|
||||
for (size_t i = visible; i < items.size(); ++i) {
|
||||
if (!items[i].in_menu) continue;
|
||||
if (items[i].menu_label.empty()) {
|
||||
items[i].draw();
|
||||
} else if (items[i].submenu) {
|
||||
if (ImGui::BeginMenu(items[i].menu_label.c_str(), items[i].enabled)) {
|
||||
if (dropdown::BeginMenu(items[i].menu_label.c_str(), items[i].enabled)) {
|
||||
items[i].submenu();
|
||||
ImGui::EndMenu();
|
||||
dropdown::EndMenu();
|
||||
}
|
||||
} else if (ImGui::MenuItem(items[i].menu_label.c_str(), nullptr, false, items[i].enabled)) {
|
||||
} else if (dropdown::Item(items[i].menu_label.c_str(), nullptr, false, items[i].enabled)) {
|
||||
items[i].trigger();
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "tools/cabana/ui/theme.h"
|
||||
#include "tools/cabana/ui/dropdown.h"
|
||||
#include "tools/cabana/utils/util.h"
|
||||
|
||||
struct GLFWwindow;
|
||||
@@ -47,17 +47,17 @@ template <typename T>
|
||||
inline bool comboBox(const char *label, int *index, const T *values, int count) {
|
||||
bool changed = false;
|
||||
const std::string preview = *index >= 0 && *index < count ? std::to_string(values[*index]) : "";
|
||||
if (ImGui::BeginCombo(label, preview.c_str())) {
|
||||
if (dropdown::BeginCombo(label, preview.c_str())) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ImGui::PushID(i);
|
||||
if (selectable(std::to_string(values[i]).c_str(), i == *index) && *index != i) {
|
||||
if (dropdown::Item(std::to_string(values[i]).c_str(), nullptr, i == *index) && *index != i) {
|
||||
*index = i;
|
||||
changed = true;
|
||||
}
|
||||
if (i == *index) ImGui::SetItemDefaultFocus();
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
dropdown::EndCombo();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
@@ -90,10 +90,6 @@ float iconTextButtonWidth(const char *icon, const std::string &text);
|
||||
// tooltip for the last item that also shows while the item is disabled
|
||||
void disabledItemTooltip(const char *text);
|
||||
|
||||
// exclusive menu action: the bullet sits in the check column and the whole row highlights. `width` is the
|
||||
// minimum row width, so a narrow popup stays wide enough for every row while the highlight spans the popup.
|
||||
bool radioMenuItem(const char *label, bool checked, float width = 0.0f);
|
||||
|
||||
// A queued modal popup submitted from whichever call site is nested in the top-most modal. draw() is called
|
||||
// both nested in a modal dialog and at the root level; only the level that opened the popup may submit it
|
||||
// (opening at level 0 would make imgui close the parent modal).
|
||||
|
||||
@@ -86,11 +86,11 @@ void DetailWidget::drawToolBar() {
|
||||
const size_t spacer_index = items.size();
|
||||
const std::string heatmap_text = "Heatmap: " + (heatmap_live_ ? std::string("Live") : heatmap_all_text_);
|
||||
auto heatmap_items = [this]() {
|
||||
if (ImGui::MenuItem("Live", nullptr, heatmap_live_) && !heatmap_live_) {
|
||||
if (dropdown::Item("Live", nullptr, heatmap_live_) && !heatmap_live_) {
|
||||
heatmap_live_ = true;
|
||||
binary_view_->setHeatmapLiveMode(true);
|
||||
}
|
||||
if (ImGui::MenuItem(heatmap_all_text_.c_str(), nullptr, !heatmap_live_) && heatmap_live_) {
|
||||
if (dropdown::Item(heatmap_all_text_.c_str(), nullptr, !heatmap_live_) && heatmap_live_) {
|
||||
heatmap_live_ = false;
|
||||
binary_view_->setHeatmapLiveMode(false);
|
||||
}
|
||||
@@ -111,13 +111,13 @@ void DetailWidget::drawToolBar() {
|
||||
}
|
||||
|
||||
void DetailWidget::showTabBarContextMenu(int index) {
|
||||
if (ImGui::BeginPopupContextItem()) {
|
||||
if (ImGui::MenuItem("Close Other Tabs")) {
|
||||
if (dropdown::BeginPopupContextItem()) {
|
||||
if (dropdown::Item("Close Other Tabs")) {
|
||||
tabbar_.moveTab(index, 0);
|
||||
tabbar_.setCurrentIndex(0);
|
||||
while (tabbar_.count() > 1) tabbar_.removeTab(1);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ void LogsWidget::draw() {
|
||||
const float value_w = std::clamp(ImGui::GetContentRegionAvail().x - fixed, 30.0f, 120.0f);
|
||||
|
||||
ImGui::SetNextItemWidth(DISPLAY_TYPE_WIDTH);
|
||||
if (ImGui::Combo("##display_type", &display_type_cb_, "Signal\0Hex\0")) {
|
||||
if (dropdown::Combo("##display_type", &display_type_cb_, "Signal\0Hex\0")) {
|
||||
hex_mode_ = display_type_cb_;
|
||||
reset();
|
||||
}
|
||||
@@ -155,10 +155,10 @@ void LogsWidget::draw() {
|
||||
}
|
||||
sig_items += '\0';
|
||||
ImGui::SetNextItemWidth(SIGNALS_WIDTH);
|
||||
if (ImGui::Combo("##signals", &signals_cb_, sig_items.c_str())) filterChanged();
|
||||
if (dropdown::Combo("##signals", &signals_cb_, sig_items.c_str())) filterChanged();
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(COMPARE_WIDTH);
|
||||
if (ImGui::Combo("##comp", &comp_box_, ">\0=\0!=\0<\0")) filterChanged();
|
||||
if (dropdown::Combo("##comp", &comp_box_, ">\0=\0!=\0<\0")) filterChanged();
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(value_w);
|
||||
if (clearableInput("##value", &value_edit_, "", doubleValidator)) {
|
||||
|
||||
@@ -301,22 +301,22 @@ void MessagesWidget::suppressHighlighted(bool from_suppress_add) {
|
||||
}
|
||||
|
||||
void MessagesWidget::drawContextMenu() {
|
||||
if (!ImGui::BeginPopup("menu")) return;
|
||||
if (!dropdown::BeginPopup("menu")) return;
|
||||
for (int i = 0; i < MessageList::COLUMN_COUNT; ++i) {
|
||||
const int column = display_order_[i];
|
||||
// can't hide the name column
|
||||
if (ImGui::MenuItem(COLUMN_TITLES[column], nullptr, !hidden_[column], column > 0)) {
|
||||
if (dropdown::Item(COLUMN_TITLES[column], nullptr, !hidden_[column], column > 0)) {
|
||||
pending_hidden_.emplace_back(column, !hidden_[column]);
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Multiline Bytes", nullptr, settings.multiple_lines_hex)) {
|
||||
if (dropdown::Item("Multiline Bytes", nullptr, settings.multiple_lines_hex)) {
|
||||
setMultiLineBytes(!settings.multiple_lines_hex);
|
||||
}
|
||||
if (ImGui::MenuItem("Show Inactive Messages", nullptr, list_.show_inactive_messages)) {
|
||||
if (dropdown::Item("Show Inactive Messages", nullptr, list_.show_inactive_messages)) {
|
||||
list_.showInactiveMessages(!list_.show_inactive_messages);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
dropdown::EndPopup();
|
||||
}
|
||||
|
||||
void MessagesWidget::setMultiLineBytes(bool multi) {
|
||||
|
||||
@@ -375,7 +375,7 @@ void SignalView::drawEditor(SignalModel::Item *item) {
|
||||
}
|
||||
const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, ImGui::GetID("##editor"));
|
||||
if (take_focus) ImGui::SetKeyboardFocusHere();
|
||||
if (ImGui::Combo("##editor", ¤t, names.data(), names.size())) {
|
||||
if (dropdown::Combo("##editor", ¤t, names.data(), names.size())) {
|
||||
queueCommit(item, items[current].second);
|
||||
open_item_ = nullptr; // commit and close the editor
|
||||
}
|
||||
|
||||
@@ -234,16 +234,9 @@ void VideoWidget::createSpeedDropdown() {
|
||||
}
|
||||
|
||||
void VideoWidget::drawSpeedMenuItems() {
|
||||
// every row declares the same width, so the popup is exactly as wide as the widest one and all the
|
||||
// highlights reach both edges; the label is padded on the right as much as the check column on the left
|
||||
const float indent = ImGui::GetFontSize();
|
||||
float label_width = 0;
|
||||
for (int i = 0; i < (int)std::size(speeds); ++i) {
|
||||
label_width = std::max(label_width, ImGui::CalcTextSize(speedText(speeds[i]).c_str()).x);
|
||||
}
|
||||
for (int i = 0; i < (int)std::size(speeds); ++i) {
|
||||
const float speed = speeds[i];
|
||||
if (radioMenuItem(speedText(speed).c_str(), speed_index_ == i, indent + label_width + indent)) {
|
||||
if (dropdown::Item(speedText(speed).c_str(), nullptr, speed_index_ == i)) {
|
||||
speed_index_ = i;
|
||||
can->setSpeed(speed);
|
||||
speed_text_ = speedText(speed);
|
||||
|
||||
Reference in New Issue
Block a user