cabana: restore Qt palettes and contrast (#38875)

* cabana: restore Qt dark palette and contrast

* cabana: restore Qt standard light palette and selection text

* cabana: drive component colors from shared style values

* cabana: remove historical palette comments
This commit is contained in:
Trey Moen
2026-09-12 17:44:43 -07:00
committed by GitHub
parent f235784042
commit f147094cb7
12 changed files with 81 additions and 67 deletions
@@ -54,7 +54,7 @@ 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 (ImGui::Selectable(msgs_combo_[i].text.c_str(), i == msgs_combo_index_)) {
if (selectable(msgs_combo_[i].text.c_str(), i == msgs_combo_index_)) {
msgs_combo_index_ = i;
updateAvailableList(i);
ImGui::CloseCurrentPopup();
@@ -107,7 +107,7 @@ void SignalSelector::drawList(const char *id, std::vector<ListItem> &list, int *
const auto &item = list[i];
ImGui::PushID(i);
const ImVec2 pos = ImGui::GetCursorScreenPos();
if (ImGui::Selectable("##item", i == *current_row)) *current_row = i;
if (selectable("##item", i == *current_row)) *current_row = i;
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
*current_row = i;
*double_clicked = true;
@@ -117,10 +117,10 @@ void SignalSelector::drawList(const char *id, std::vector<ListItem> &list, int *
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());
dl->AddText(ImVec2(x, pos.y), ImGui::GetColorU32(i == *current_row ? palette().text_selected : palette().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());
dl->AddText(ImVec2(x, pos.y), ImGui::GetColorU32(i == *current_row ? palette().text_selected : palette().text_disabled), msgLabel(item.msg_id).c_str());
}
ImGui::PopID();
}
+1 -6
View File
@@ -100,12 +100,7 @@ void Sparkline::render(const CabanaColor &color, int range, ImVec2 sz, double wi
}
size = sz;
CabanaColor line_color = color;
if (!isDarkTheme()) {
auto [h, s, v] = color.hsv();
line_color = CabanaColor::fromHsv(h, std::min(1.0f, s * 2.0f), v * 0.7f, color.a / 255.0f);
}
color_ = toImU32(line_color);
color_ = toImU32(sparklineColor(color));
draw_individual_points_ = draw_individual_points;
window_end_ = window_end;
xscale_ = xscale;
@@ -165,7 +165,7 @@ void draw() {
const std::string label = (is_dir ? std::string(icon::FOLDER) : std::string(icon::FILE_EARMARK)) + " " + name;
ImGui::PushID(static_cast<int>(i));
const bool selected = !is_dir && name == s.filename;
if (ImGui::Selectable(label.c_str(), selected, ImGuiSelectableFlags_AllowDoubleClick)) {
if (selectable(label.c_str(), selected, ImGuiSelectableFlags_AllowDoubleClick)) {
const bool double_clicked = ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left);
if (is_dir) {
if (double_clicked) {
@@ -108,7 +108,7 @@ void RoutesDialog::draw() {
}
for (int i = 0; i < static_cast<int>(s_.routes.size()); ++i) {
ImGui::PushID(i);
if (ImGui::Selectable(s_.routes[i].label.c_str(), s_.route_index == i, ImGuiSelectableFlags_AllowDoubleClick)) {
if (selectable(s_.routes[i].label.c_str(), s_.route_index == i, ImGuiSelectableFlags_AllowDoubleClick)) {
s_.route_index = i;
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) accepted = true;
}
+32 -29
View File
@@ -12,30 +12,35 @@ namespace fs = std::filesystem;
namespace {
constexpr Palette DARK_PALETTE = {
.text = rgb(0xf8f9f9), .text_disabled = rgb(0xb8c0c4),
.window = rgb(0x1d2225), .surface = rgb(0x30373b),
.frame = rgb(0x1e2224), .frame_hovered = rgb(0x394044), .frame_active = rgb(0x424a4f),
.button = rgb(0x424a4f), .button_hovered = rgb(0x535f64), .button_active = rgb(0x175886),
.header = rgb(0x175886), .header_hovered = rgb(0x24455e), .header_active = rgb(0x1c6ea8),
.accent = rgb(0x57a9e3),
.border = rgb(0x65737a), .separator = rgb(0x4b5559),
.tab = rgb(0x272c2f), .tab_hovered = rgb(0x424a4f), .table_header = rgb(0x424a4f),
.grid = rgb(0x65737a, 0.45f), .badge = rgb(0x808080),
.text = rgb(0xbbbbbb), .text_disabled = rgb(0x777777), .text_selected = rgb(0xbbbbbb),
.window = rgb(0x353535), .surface = rgb(0x3c3f41),
.frame = rgb(0x3c3f41), .frame_hovered = rgb(0x484b4d), .frame_active = rgb(0x505355),
.button = rgb(0x484b4d), .button_hovered = rgb(0x535658), .button_active = rgb(0x3c3f41),
.header = rgb(0x2f65ca), .header_hovered = rgb(0x414e65), .header_active = rgb(0x2f65ca),
.accent = rgb(0x2f65ca),
.border = rgb(0x282828), .separator = rgb(0x353535), .scrollbar_grab = rgb(0x484b4d),
.tab = rgb(0x353535), .tab_hovered = rgb(0x484b4d), .table_header = rgb(0x484b4d),
.grid = rgb(0xbbbbbb, 50.0f / 255.0f), .badge = rgb(0x808080),
.bit_background = rgb(0xffffff, 20.0f / 255.0f),
.heatmap_signal_alpha = 70.0, .heatmap_bit_alpha = 28.0, .heatmap_gamma = 0.6,
.sparkline_saturation = 1.0f, .sparkline_value = 1.0f,
};
constexpr Palette LIGHT_PALETTE = {
.text = rgb(0x1e2224), .text_disabled = rgb(0x535f64),
.window = rgb(0xeeeff0), .surface = rgb(0xffffff),
.frame = rgb(0xf8f9f9), .frame_hovered = rgb(0xeeeff0), .frame_active = rgb(0xddeef9),
.button = rgb(0xe3e6e8), .button_hovered = rgb(0xd8dcdf), .button_active = rgb(0xbcddf4),
.header = rgb(0xbcddf4), .header_hovered = rgb(0xddeef9), .header_active = rgb(0x9fcbec),
.accent = rgb(0x1c6ea8),
.border = rgb(0x98a3a9), .separator = rgb(0xcdd3d6),
.tab = rgb(0xe3e6e8), .tab_hovered = rgb(0xddeef9), .table_header = rgb(0xd8dcdf),
.grid = rgb(0x98a3a9, 0.4f), .badge = rgb(0xa0a0a4),
.text = rgb(0x000000), .text_disabled = rgb(0xbebebe), .text_selected = rgb(0xffffff),
.window = rgb(0xefefef), .surface = rgb(0xffffff),
.frame = rgb(0xffffff), .frame_hovered = rgb(0xf5f9fc), .frame_active = rgb(0xe7f3fb),
.button = rgb(0xefefef), .button_hovered = rgb(0xe7f3fb), .button_active = rgb(0xd4e7f4),
.header = rgb(0x308cc6), .header_hovered = rgb(0xe7f3fb), .header_active = rgb(0x308cc6),
.accent = rgb(0x308cc6),
.border = rgb(0xb6b6b6), .separator = rgb(0xd0d0d0), .scrollbar_grab = rgb(0xb6b6b6),
.tab = rgb(0xe5e5e5), .tab_hovered = rgb(0xefefef), .table_header = rgb(0xefefef),
.grid = rgb(0x000000, 50.0f / 255.0f), .badge = rgb(0xa0a0a4),
.bit_background = rgb(0xffffff, 0.0f),
.heatmap_signal_alpha = 25.0, .heatmap_bit_alpha = 10.0, .heatmap_gamma = 1.0,
.sparkline_saturation = 2.0f, .sparkline_value = 0.7f,
};
bool g_dark = false;
const Palette *g_palette = &LIGHT_PALETTE;
ImFont *g_ui_font = nullptr;
ImFont *g_bold_font = nullptr;
@@ -82,8 +87,7 @@ void loadFonts() {
}
void applyTheme(int theme) {
g_dark = theme == DARK_THEME;
g_palette = g_dark ? &DARK_PALETTE : &LIGHT_PALETTE;
g_palette = theme == DARK_THEME ? &DARK_PALETTE : &LIGHT_PALETTE;
const Palette &p = *g_palette;
const ImVec4 none(0, 0, 0, 0);
@@ -110,7 +114,7 @@ void applyTheme(int theme) {
c[ImGuiCol_Text] = p.text;
c[ImGuiCol_TextDisabled] = p.text_disabled;
c[ImGuiCol_WindowBg] = c[ImGuiCol_ScrollbarBg] = c[ImGuiCol_DockingEmptyBg] = p.window;
c[ImGuiCol_MenuBarBg] = p.surface;
c[ImGuiCol_MenuBarBg] = p.window;
c[ImGuiCol_TitleBg] = c[ImGuiCol_TitleBgActive] = c[ImGuiCol_TitleBgCollapsed] = p.window;
c[ImGuiCol_ChildBg] = c[ImGuiCol_PopupBg] = p.surface;
c[ImGuiCol_Border] = c[ImGuiCol_TableBorderStrong] = p.border;
@@ -121,7 +125,7 @@ void applyTheme(int theme) {
c[ImGuiCol_FrameBgActive] = p.frame_active;
c[ImGuiCol_Button] = p.button;
c[ImGuiCol_ButtonHovered] = c[ImGuiCol_SliderGrab] = p.button_hovered;
c[ImGuiCol_ScrollbarGrab] = p.border;
c[ImGuiCol_ScrollbarGrab] = p.scrollbar_grab;
c[ImGuiCol_ScrollbarGrabHovered] = p.text_disabled;
c[ImGuiCol_ButtonActive] = p.button_active;
c[ImGuiCol_Header] = p.header;
@@ -138,7 +142,7 @@ void applyTheme(int theme) {
c[ImGuiCol_TabSelected] = c[ImGuiCol_TabDimmedSelected] = p.surface;
c[ImGuiCol_TabDimmedSelectedOverline] = none;
c[ImGuiCol_TableHeaderBg] = p.table_header;
c[ImGuiCol_TableRowBgAlt] = g_dark ? ImVec4(1, 1, 1, 0.065f) : ImVec4(0, 0, 0, 0.045f);
c[ImGuiCol_TableRowBgAlt] = none;
c[ImGuiCol_PlotLines] = p.text;
// ImGuiStyle() seeds every slot from the dark theme: set the rest so the light theme does not keep a white caret.
c[ImGuiCol_InputTextCursor] = c[ImGuiCol_UnsavedMarker] = p.text;
@@ -152,13 +156,12 @@ void applyTheme(int theme) {
ImPlot::GetStyle().Colors[ImPlotCol_AxisGrid] = p.grid;
}
bool isDarkTheme() { return g_dark; }
const Palette &palette() { return *g_palette; }
CabanaColor signalFillColor(const CabanaColor &c) {
if (!g_dark) return c;
auto [h, s, v] = c.hsv();
return CabanaColor::fromHsv(h, std::min(1.0f, s * 1.4f), v * 0.8f, c.a / 255.0f);
CabanaColor sparklineColor(const CabanaColor &color) {
const Palette &p = palette();
auto [h, s, v] = color.hsv();
return CabanaColor::fromHsv(h, std::min(1.0f, s * p.sparkline_saturation), v * p.sparkline_value, color.a / 255.0f);
}
ImFont *boldFont() { return g_bold_font; }
+6 -7
View File
@@ -5,20 +5,21 @@
#include "tools/cabana/core/color.h"
// Palette source: commaai/connect src/{colors,theme}.js at 7091050.
// Dark colors follow connect; light colors use its lightGrey and lightBlue families.
struct Palette {
ImVec4 text, text_disabled;
ImVec4 text, text_disabled, text_selected;
ImVec4 window; // the background behind panels and docked windows
ImVec4 surface; // panels, popups, table bodies: what content is drawn on
ImVec4 frame, frame_hovered, frame_active;
ImVec4 button, button_hovered, button_active;
ImVec4 header, header_hovered, header_active; // selections
ImVec4 accent;
ImVec4 border, separator;
ImVec4 border, separator, scrollbar_grab;
ImVec4 tab, tab_hovered, table_header;
ImVec4 grid;
ImVec4 badge; // the fill behind the time labels drawn over a chart
ImVec4 bit_background; // overlay beneath heatmap bits without a signal
double heatmap_signal_alpha, heatmap_bit_alpha, heatmap_gamma;
float sparkline_saturation, sparkline_value; // HSV multipliers for signal colors
};
constexpr ImVec4 rgb(unsigned hex, float alpha = 1.0f) {
@@ -35,10 +36,8 @@ constexpr float UI_FONT_SIZE = 16.0f;
void loadFonts();
void applyTheme(int theme); // Safe to call at runtime.
bool isDarkTheme();
const Palette &palette();
CabanaColor signalFillColor(const CabanaColor &c);
CabanaColor sparklineColor(const CabanaColor &color);
ImFont *boldFont();
void pushMonoFont(float size = 0.0f);
+13 -3
View File
@@ -89,13 +89,23 @@ bool clearableInput(const char *label, std::string *s, const char *hint, ImGuiIn
return changed;
}
bool selectable(const char *label, bool selected, ImGuiSelectableFlags flags, const ImVec2 &size) {
if (selected) {
ImGui::PushStyleColor(ImGuiCol_Text, palette().text_selected);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, palette().header);
}
const bool clicked = ImGui::Selectable(label, selected, flags, size);
if (selected) ImGui::PopStyleColor(2);
return clicked;
}
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() : "")) {
for (int i = 0; i < count; ++i) {
ImGui::PushID(i);
if (ImGui::Selectable(items[i].c_str(), i == *index) && *index != i) {
if (selectable(items[i].c_str(), i == *index) && *index != i) {
*index = i;
changed = true;
}
@@ -241,7 +251,7 @@ void disabledItemTooltip(const char *text) {
bool radioMenuItem(const char *label, bool checked, float width) {
const float indent = ImGui::GetFontSize();
const ImVec2 pos = ImGui::GetCursorScreenPos();
const bool clicked = ImGui::Selectable((std::string("##") + label).c_str(), false, ImGuiSelectableFlags_None,
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();
@@ -328,7 +338,7 @@ int tableHeadersRow() {
bool viewSelectable(const char *label, bool selected, ImGuiSelectableFlags flags, const ImVec2 &size) {
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, selected ? ImGui::GetColorU32(ImGuiCol_Header) : IM_COL32(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiCol_Header));
const bool clicked = ImGui::Selectable(label, selected, flags, size);
const bool clicked = selectable(label, selected, flags, size);
ImGui::PopStyleColor(2);
return clicked;
}
+3 -1
View File
@@ -38,6 +38,8 @@ bool beginControlChild(const char *id, const ImVec2 &size, ImGuiWindowFlags flag
// SetNextItemWidth includes the field and clear button. Returns true when text changes.
bool clearableInput(const char *label, std::string *s, const char *hint = "", ImGuiInputTextCallback validator = nullptr);
bool selectable(const char *label, bool selected, ImGuiSelectableFlags flags = 0, const ImVec2 &size = ImVec2(0, 0));
bool comboBox(const char *label, int *index, const std::vector<std::string> &items);
// numeric items (bus ids, bus speeds) are formatted as they are drawn
@@ -48,7 +50,7 @@ inline bool comboBox(const char *label, int *index, const T *values, int count)
if (ImGui::BeginCombo(label, preview.c_str())) {
for (int i = 0; i < count; ++i) {
ImGui::PushID(i);
if (ImGui::Selectable(std::to_string(values[i]).c_str(), i == *index) && *index != i) {
if (selectable(std::to_string(values[i]).c_str(), i == *index) && *index != i) {
*index = i;
changed = true;
}
@@ -367,11 +367,11 @@ void BinaryView::updateState() {
}
}
const bool dark = isDarkTheme();
const Palette &p = palette();
const double max_alpha = 255.0;
const double min_alpha_with_signal = dark ? 70.0 : 25.0; // Base alpha for small flip counts
const double min_alpha_no_signal = dark ? 28.0 : 10.0; // Base alpha for small flip counts for no signal bits
const double alpha_gamma = dark ? 0.6 : 1.0;
const double min_alpha_with_signal = p.heatmap_signal_alpha; // Base alpha for small flip counts
const double min_alpha_no_signal = p.heatmap_bit_alpha; // Base alpha for small flip counts for no signal bits
const double alpha_gamma = p.heatmap_gamma;
const double log_factor = 1.0 + 0.2;
const double log_scaler = max_alpha / log2(log_factor * max_bit_flip_count);
@@ -456,13 +456,13 @@ void BinaryView::paintCell(ImDrawList *painter, const ImRect &rect, const Binary
if (item->sigs.size() > 0) {
for (auto &s : item->sigs) {
if (s == hovered_sig_) {
painter->AddRectFilled(rect.Min, rect.Max, toImU32(signalFillColor(s->color).darker(125))); // 4/5x brightness
painter->AddRectFilled(rect.Min, rect.Max, toImU32(s->color.darker(125))); // 4/5x brightness
} else {
drawSignalCell(painter, rect, index, s);
}
}
} else if (item->valid) {
if (isDarkTheme()) painter->AddRectFilled(rect.Min, rect.Max, IM_COL32(255, 255, 255, 20));
painter->AddRectFilled(rect.Min, rect.Max, ImGui::GetColorU32(palette().bit_background));
if (item->bg_color.alpha() > 0) painter->AddRectFilled(rect.Min, rect.Max, toImU32(item->bg_color));
}
bool bright = std::find(item->sigs.begin(), item->sigs.end(), hovered_sig_) != item->sigs.end();
@@ -526,9 +526,9 @@ void BinaryView::drawSignalCell(ImDrawList *painter, const ImRect &rect, const B
if (bottom_notch) band(bottom_notch, rc.Max.y - spacing, rc.Max.y);
auto item = &cellAt(index);
CabanaColor color = signalFillColor(sig->color);
CabanaColor color = sig->color;
color.a = item->bg_color.alpha();
const ImU32 edge = toImU32(signalFillColor(sig->color).darker(125));
const ImU32 edge = toImU32(sig->color.darker(125));
for (const ImRect &clip : region) {
painter->PushClipRect(clip.Min, clip.Max, true);
@@ -262,13 +262,15 @@ void DetailWidget::drawTabWidget() {
const bool selected = tab_widget_index_ == i;
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(selected ? ImGuiCol_Header : ImGuiCol_Button, selected ? 1.0f : 0.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetColorU32(selected ? ImGuiCol_HeaderActive : ImGuiCol_ButtonHovered));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, selected ? palette().header_active : palette().button_active);
ImGui::PushStyleColor(ImGuiCol_Text, selected ? palette().text_selected : palette().text);
if (i) ImGui::SameLine();
if (ImGui::Button(labels[i].c_str()) && !selected) {
tab_widget_index_ = i;
if (i == 1) history_log_->onShown();
updateState();
}
ImGui::PopStyleColor(2);
ImGui::PopStyleColor(4);
}
ImGui::PopStyleVar(2);
ImGui::EndChild();
@@ -24,7 +24,10 @@ ImVec2 bytesCellSize(int n, bool multiple_lines) {
}
ImU32 cellTextColor(bool selected, bool inactive) {
if (selected && inactive) return withAlpha(ImGui::GetColorU32(ImGuiCol_Text), 100);
if (selected) {
const ImU32 text = ImGui::GetColorU32(palette().text_selected);
return inactive ? withAlpha(text, 100) : text;
}
return ImGui::GetColorU32(inactive ? ImGuiCol_TextDisabled : ImGuiCol_Text);
}
@@ -260,13 +260,13 @@ void SignalView::paintCell(ImDrawList *painter, const ImRect &option_rect, const
ImRect rect(option_rect.Min.x + h_margin, option_rect.Min.y + v_margin, option_rect.Max.x - h_margin, option_rect.Max.y - v_margin);
// selection background is painted by the row's Selectable
const ImU32 text_color = ImGui::GetColorU32(ImGuiCol_Text);
const ImU32 text_color = ImGui::GetColorU32(selected ? palette().text_selected : palette().text);
if (column == 0) {
if (item->type == SignalModel::Item::Sig) {
// color label
ImRect icon_rect(rect.Min.x, rect.Min.y, rect.Min.x + COLOR_LABEL_WIDTH, rect.Max.y);
painter->AddRectFilled(icon_rect.Min, icon_rect.Max, toImU32(signalFillColor(item->sig->color).darker(item->highlight ? 125 : 0)), ImGui::GetStyle().FrameRounding);
painter->AddRectFilled(icon_rect.Min, icon_rect.Max, toImU32(item->sig->color.darker(item->highlight ? 125 : 0)), ImGui::GetStyle().FrameRounding);
drawText(painter, icon_rect, std::to_string(item->row() + 1).c_str(), item->highlight ? IM_COL32_WHITE : IM_COL32_BLACK,
nullptr, LABEL_FONT);
@@ -688,7 +688,7 @@ float SignalView::minimumWidth() {
}
void SignalView::draw() {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
ImGui::PushStyleColor(ImGuiCol_ChildBg, palette().surface);
if (!ImGui::BeginChild("SignalView", ImVec2(0, 0), ImGuiChildFlags_Borders)) {
ImGui::EndChild();
ImGui::PopStyleColor();
@@ -823,7 +823,7 @@ bool SignalView::drawItem(SignalModel::Item *item, int depth, DrawContext &ctx)
if (!item->children.empty()) {
const float arrow_size = ImGui::GetFontSize() * 0.7f;
ImGui::RenderArrow(ctx.draw_list, ImVec2(row_min.x + depth * INDENTATION + 4.0f, row_min.y + (row_height - arrow_size) * 0.5f),
ImGui::GetColorU32(ImGuiCol_Text), item->expanded ? ImGuiDir_Down : ImGuiDir_Right, 0.7f);
ImGui::GetColorU32(selected ? palette().text_selected : palette().text), item->expanded ? ImGuiDir_Down : ImGuiDir_Right, 0.7f);
}
// every row is measured, the header sizes column 0 to the contents of the whole tree
@@ -936,7 +936,7 @@ bool ValueDescriptionDlg::draw() {
if (row == current_row_) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, ImGui::GetColorU32(ImGuiCol_Header));
ImGui::TableSetColumnIndex(0);
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(std::to_string(row + 1).c_str());
ImGui::TextColored(row == current_row_ ? palette().text_selected : palette().text, "%d", row + 1);
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-FLT_MIN);
if (valueDescriptionEditor(0, &table_[row].first)) current_row_ = row;