cabana: better colors (#38758)

This commit is contained in:
Trey Moen
2026-09-02 20:20:23 -07:00
committed by GitHub
parent 06d76bdb24
commit c163743e32
9 changed files with 69 additions and 60 deletions
+5 -4
View File
@@ -108,8 +108,9 @@ void Sparkline::render(const CabanaColor &color, int range, ImVec2 sz, double wi
xscale_ = xscale;
}
void Sparkline::draw(ImDrawList *draw_list, ImVec2 pos) const {
void Sparkline::draw(ImDrawList *draw_list, ImVec2 pos, ImU32 color) const {
if (render_points_.empty()) return;
if (color == 0) color = color_;
// update() only runs when a message of this id arrives, so a slow message would hold the sparkline
// still for many frames and then move it in one step. scroll the rendered polyline by the time that
@@ -132,14 +133,14 @@ void Sparkline::draw(ImDrawList *draw_list, ImVec2 pos) const {
draw_list->PushClipRect(pos, ImVec2(pos.x + size.x, pos.y + size.y), true);
// a point is a 3x3 square
auto draw_point = [&](const ImVec2 &p) { draw_list->AddRectFilled(ImVec2(p.x - 1.5f, p.y - 1.5f), ImVec2(p.x + 1.5f, p.y + 1.5f), color_); };
auto draw_point = [&](const ImVec2 &p) { draw_list->AddRectFilled(ImVec2(p.x - 1.5f, p.y - 1.5f), ImVec2(p.x + 1.5f, p.y + 1.5f), color); };
if (draw_individual_points_) {
for (const auto &p : render_points_) {
draw_list->PathLineTo(point_at(p));
draw_point(point_at(p));
}
draw_list->PathStroke(color_, ImDrawFlags_None, 1.5f);
draw_list->PathStroke(color, ImDrawFlags_None, 1.5f);
} else {
// one sample per pixel column: several strokes in a column overlap into a blur, and a dense
// high-contrast texture scrolling by is hard on the eyes
@@ -167,7 +168,7 @@ void Sparkline::draw(ImDrawList *draw_list, ImVec2 pos) const {
while (j + 1 < pts.size() && steep(j) == is_steep) ++j;
draw_list->Flags = is_steep ? (saved & ~ImDrawListFlags_AntiAliasedLines) : saved;
for (size_t n = i; n <= j; ++n) draw_list->PathLineTo(pts[n]);
draw_list->PathStroke(color_, ImDrawFlags_None, 1.0f);
draw_list->PathStroke(color, ImDrawFlags_None, 1.0f);
i = j;
}
draw_list->Flags = saved;
+1 -1
View File
@@ -12,7 +12,7 @@ public:
inline double freq() const { return freq_; }
bool isEmpty() const { return render_points_.empty(); }
// emits the rendered polyline at pos (top-left, screen coordinates)
void draw(ImDrawList *draw_list, ImVec2 pos) const;
void draw(ImDrawList *draw_list, ImVec2 pos, ImU32 color = 0) const;
ImVec2 size = {}; // empty when isEmpty()
double min_val = 0;
@@ -263,16 +263,19 @@ void StreamSelector::open(Callback on_done) {
void StreamSelector::draw() {
if (!open_) return;
if (!beginDialog("Open stream", &popup_, ImVec2(640.0f, 0.0f))) return;
if (!beginDialog("Open stream", &popup_, ImVec2(768.0f, 0.0f))) return;
AbstractOpenStreamWidget *current = nullptr;
const ImVec4 pane = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
ImGui::PushStyleColor(ImGuiCol_ChildBg, pane);
ImGui::PushStyleColor(ImGuiCol_TabSelected, pane);
if (ImGui::BeginTabBar("streams")) {
for (auto &w : widgets_) {
// a fresh dialog every time, so the first tab is always the current one
ImGuiTabItemFlags tab_flags = (first_frame_ && w == widgets_.front()) ? ImGuiTabItemFlags_SetSelected : 0;
if (ImGui::BeginTabItem(w->title(), nullptr, tab_flags)) {
current = w.get();
ImGui::BeginChild("tab", ImVec2(0, 130.0f));
ImGui::BeginChild("tab", ImVec2(0, 130.0f), ImGuiChildFlags_Borders);
w->draw();
ImGui::EndChild();
ImGui::EndTabItem();
@@ -280,6 +283,7 @@ void StreamSelector::draw() {
}
ImGui::EndTabBar();
}
ImGui::PopStyleColor(2);
first_frame_ = false;
ImGui::AlignTextToFramePadding();
+38 -34
View File
@@ -86,46 +86,44 @@ void applyTheme(int theme) {
auto c = [](const CabanaColor &col, float a = 1.0f) { return colorRgb(col.r, col.g, col.b, a); };
ImVec4 *colors = style.Colors;
if (dark) {
// the low contrast Darcula grays are opened up: text and outlines sit further from the window and
// base grays
const ImVec4 highlight = c(DarkTheme::highlight);
const ImVec4 outline = colorRgb(0x5a, 0x5d, 0x60);
const ImVec4 outline = colorRgb(0x26, 0x26, 0x26);
colors[ImGuiCol_WindowBg] = c(DarkTheme::window);
colors[ImGuiCol_ChildBg] = c(DarkTheme::base);
colors[ImGuiCol_PopupBg] = c(DarkTheme::base);
colors[ImGuiCol_PopupBg] = c(DarkTheme::window);
colors[ImGuiCol_MenuBarBg] = c(DarkTheme::window);
colors[ImGuiCol_DockingEmptyBg] = c(DarkTheme::window);
colors[ImGuiCol_Text] = colorRgb(0xdc, 0xdc, 0xdc);
colors[ImGuiCol_TextDisabled] = colorRgb(0x8c, 0x8c, 0x8c);
colors[ImGuiCol_Text] = c(DarkTheme::text);
colors[ImGuiCol_TextDisabled] = c(DarkTheme::disabled_text);
colors[ImGuiCol_Border] = outline;
colors[ImGuiCol_BorderShadow] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_FrameBg] = colorRgb(0x2e, 0x30, 0x32); // darker than the base so fields read as sunken
colors[ImGuiCol_FrameBgHovered] = colorRgb(0x3a, 0x3d, 0x40);
colors[ImGuiCol_FrameBgActive] = colorRgb(0x45, 0x48, 0x4b);
colors[ImGuiCol_Button] = c(DarkTheme::button);
colors[ImGuiCol_ButtonHovered] = colorRgb(0x52, 0x56, 0x59);
colors[ImGuiCol_ButtonActive] = colorRgb(0x2b, 0x2d, 0x30);
colors[ImGuiCol_FrameBg] = c(DarkTheme::base);
colors[ImGuiCol_FrameBgHovered] = colorRgb(0x1f, 0x1f, 0x1f);
colors[ImGuiCol_FrameBgActive] = colorRgb(0x24, 0x24, 0x24);
colors[ImGuiCol_Button] = colorRgb(0x3a, 0x3a, 0x3a);
colors[ImGuiCol_ButtonHovered] = colorRgb(0x42, 0x42, 0x42);
colors[ImGuiCol_ButtonActive] = colorRgb(0x30, 0x30, 0x30);
colors[ImGuiCol_Header] = highlight;
colors[ImGuiCol_HeaderHovered] = c(DarkTheme::highlight, 0.8f);
colors[ImGuiCol_HeaderActive] = highlight;
colors[ImGuiCol_CheckMark] = c(DarkTheme::bright_text);
colors[ImGuiCol_SliderGrab] = colorRgb(0x8f, 0x92, 0x95);
colors[ImGuiCol_SliderGrabActive] = colorRgb(0xa8, 0xab, 0xae);
colors[ImGuiCol_SliderGrab] = colorRgb(0x6a, 0x6a, 0x6a);
colors[ImGuiCol_SliderGrabActive] = colorRgb(0x80, 0x80, 0x80);
colors[ImGuiCol_ScrollbarBg] = c(DarkTheme::window);
colors[ImGuiCol_ScrollbarGrab] = colorRgb(0x70, 0x73, 0x76);
colors[ImGuiCol_ScrollbarGrabHovered] = colorRgb(0x85, 0x88, 0x8b);
colors[ImGuiCol_ScrollbarGrabActive] = c(DarkTheme::light);
colors[ImGuiCol_ScrollbarGrab] = colorRgb(0x5a, 0x5a, 0x5a);
colors[ImGuiCol_ScrollbarGrabHovered] = colorRgb(0x6a, 0x6a, 0x6a);
colors[ImGuiCol_ScrollbarGrabActive] = colorRgb(0x7a, 0x7a, 0x7a);
colors[ImGuiCol_Separator] = outline;
colors[ImGuiCol_SeparatorHovered] = c(DarkTheme::highlight, 0.6f);
colors[ImGuiCol_SeparatorActive] = highlight;
colors[ImGuiCol_ResizeGrip] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_ResizeGripHovered] = c(DarkTheme::highlight, 0.6f);
colors[ImGuiCol_ResizeGripActive] = highlight;
colors[ImGuiCol_Tab] = c(DarkTheme::window);
colors[ImGuiCol_TabHovered] = colorRgb(0x4b, 0x4e, 0x52);
colors[ImGuiCol_Tab] = colorRgb(0x2c, 0x2c, 0x2c);
colors[ImGuiCol_TabHovered] = colorRgb(0x3a, 0x3a, 0x3a);
colors[ImGuiCol_TabSelected] = c(DarkTheme::base);
colors[ImGuiCol_TabSelectedOverline] = highlight;
colors[ImGuiCol_TabDimmed] = c(DarkTheme::window);
colors[ImGuiCol_TabDimmed] = colorRgb(0x2c, 0x2c, 0x2c);
colors[ImGuiCol_TabDimmedSelected] = c(DarkTheme::base);
colors[ImGuiCol_TabDimmedSelectedOverline] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_TitleBg] = c(DarkTheme::window);
@@ -133,7 +131,7 @@ void applyTheme(int theme) {
colors[ImGuiCol_TitleBgCollapsed] = c(DarkTheme::window);
colors[ImGuiCol_TableHeaderBg] = c(DarkTheme::window);
colors[ImGuiCol_TableBorderStrong] = outline;
colors[ImGuiCol_TableBorderLight] = colorRgb(0x23, 0x26, 0x28); // darker than the cells, like the qt grid
colors[ImGuiCol_TableBorderLight] = colorRgb(0x2c, 0x2c, 0x2c);
colors[ImGuiCol_TableRowBg] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_TableRowBgAlt] = colorRgb(0xff, 0xff, 0xff, 0.06f);
colors[ImGuiCol_TextSelectedBg] = c(DarkTheme::highlight, 0.6f);
@@ -145,11 +143,11 @@ void applyTheme(int theme) {
} else {
const ImVec4 window = colorRgb(0xef, 0xef, 0xef);
const ImVec4 base = colorRgb(0xff, 0xff, 0xff);
const ImVec4 outline = colorRgb(0xb9, 0xb9, 0xb9);
const ImVec4 outline = colorRgb(0xab, 0xab, 0xab);
const ImVec4 highlight = colorRgb(0x30, 0x8c, 0xc6);
colors[ImGuiCol_WindowBg] = window;
colors[ImGuiCol_ChildBg] = base;
colors[ImGuiCol_PopupBg] = colorRgb(0xfb, 0xfb, 0xfb);
colors[ImGuiCol_PopupBg] = colorRgb(0xf8, 0xf8, 0xf8);
colors[ImGuiCol_MenuBarBg] = window;
colors[ImGuiCol_DockingEmptyBg] = window;
colors[ImGuiCol_Text] = colorRgb(0x00, 0x00, 0x00);
@@ -159,9 +157,9 @@ void applyTheme(int theme) {
colors[ImGuiCol_FrameBg] = base;
colors[ImGuiCol_FrameBgHovered] = colorRgb(0xf7, 0xf7, 0xf7);
colors[ImGuiCol_FrameBgActive] = colorRgb(0xef, 0xef, 0xef);
colors[ImGuiCol_Button] = colorRgb(0xf3, 0xf3, 0xf3);
colors[ImGuiCol_ButtonHovered] = colorRgb(0xf9, 0xf9, 0xf9);
colors[ImGuiCol_ButtonActive] = colorRgb(0xdc, 0xdc, 0xdc);
colors[ImGuiCol_Button] = colorRgb(0xf5, 0xf5, 0xf5);
colors[ImGuiCol_ButtonHovered] = colorRgb(0xfa, 0xfa, 0xfa);
colors[ImGuiCol_ButtonActive] = colorRgb(0xd9, 0xd9, 0xd9);
colors[ImGuiCol_Header] = highlight;
colors[ImGuiCol_HeaderHovered] = colorRgb(0x30, 0x8c, 0xc6, 0.8f);
colors[ImGuiCol_HeaderActive] = highlight;
@@ -178,11 +176,11 @@ void applyTheme(int theme) {
colors[ImGuiCol_ResizeGrip] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_ResizeGripHovered] = colorRgb(0x30, 0x8c, 0xc6, 0.6f);
colors[ImGuiCol_ResizeGripActive] = highlight;
colors[ImGuiCol_Tab] = colorRgb(0xe2, 0xe2, 0xe2);
colors[ImGuiCol_Tab] = colorRgb(0xdc, 0xdc, 0xdc);
colors[ImGuiCol_TabHovered] = colorRgb(0xf5, 0xf5, 0xf5);
colors[ImGuiCol_TabSelected] = base;
colors[ImGuiCol_TabSelectedOverline] = highlight;
colors[ImGuiCol_TabDimmed] = colorRgb(0xe2, 0xe2, 0xe2);
colors[ImGuiCol_TabDimmed] = colorRgb(0xdc, 0xdc, 0xdc);
colors[ImGuiCol_TabDimmedSelected] = base;
colors[ImGuiCol_TabDimmedSelectedOverline] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_TitleBg] = window;
@@ -190,7 +188,7 @@ void applyTheme(int theme) {
colors[ImGuiCol_TitleBgCollapsed] = window;
colors[ImGuiCol_TableHeaderBg] = colorRgb(0xf2, 0xf2, 0xf2);
colors[ImGuiCol_TableBorderStrong] = outline;
colors[ImGuiCol_TableBorderLight] = colorRgb(0xd8, 0xd8, 0xd8);
colors[ImGuiCol_TableBorderLight] = colorRgb(0xc7, 0xc7, 0xc7);
colors[ImGuiCol_TableRowBg] = colorRgb(0, 0, 0, 0.0f);
colors[ImGuiCol_TableRowBgAlt] = colorRgb(0, 0, 0, 0.03f);
colors[ImGuiCol_TextSelectedBg] = colorRgb(0x30, 0x8c, 0xc6, 0.35f);
@@ -207,6 +205,12 @@ void applyTheme(int theme) {
bool isDarkTheme() { return g_dark; }
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);
}
ImU32 highlightedTextColor() {
return g_dark ? IM_COL32(DarkTheme::window_text.r, DarkTheme::window_text.g, DarkTheme::window_text.b, 255)
: IM_COL32(255, 255, 255, 255);
@@ -219,11 +223,11 @@ ImU32 paletteBrightText() {
void drawSliderHandle(ImDrawList *p, const ImRect &r) {
const bool dark = isDarkTheme();
const ImU32 top = dark ? IM_COL32(0x3e, 0x41, 0x43, 255) : IM_COL32(255, 255, 255, 255);
const ImU32 bottom = dark ? IM_COL32(0x39, 0x3c, 0x3e, 255) : IM_COL32(0xf0, 0xf0, 0xf0, 255);
const ImU32 top = dark ? IM_COL32(0x41, 0x41, 0x41, 255) : IM_COL32(255, 255, 255, 255);
const ImU32 bottom = dark ? IM_COL32(0x36, 0x36, 0x36, 255) : IM_COL32(0xf0, 0xf0, 0xf0, 255);
// the top/left edge is one step lighter than the bottom/right edge
const ImU32 outline_top = dark ? IM_COL32(0xa3, 0xa3, 0xa3, 255) : IM_COL32(0xab, 0xab, 0xab, 255);
const ImU32 outline_bottom = dark ? IM_COL32(0x9c, 0x9c, 0x9c, 255) : IM_COL32(0xa4, 0xa4, 0xa4, 255);
const ImU32 outline_top = dark ? IM_COL32(0x5c, 0x5c, 0x5c, 255) : IM_COL32(0xab, 0xab, 0xab, 255);
const ImU32 outline_bottom = dark ? IM_COL32(0x26, 0x26, 0x26, 255) : IM_COL32(0xa4, 0xa4, 0xa4, 255);
p->AddRectFilled(r.Min, r.Max, top, 2.0f);
p->AddRectFilled(ImVec2(r.Min.x, r.GetCenter().y), r.Max, bottom, 2.0f, ImDrawFlags_RoundCornersBottom);
p->AddRect(r.Min, r.Max, outline_bottom, 2.0f, 0, 1.0f);
@@ -237,7 +241,7 @@ void drawSliderHandle(ImDrawList *p, const ImRect &r) {
bool fusionSliderInt(const char *label, int *v, int min, int max, float width) {
// a grey groove over the full width with the part left of the handle filled, and a 13x13 handle on top
const ImU32 groove_col = isDarkTheme() ? IM_COL32(0x2a, 0x2c, 0x2e, 255) : IM_COL32(0xc4, 0xc4, 0xc4, 255);
const ImU32 groove_col = isDarkTheme() ? IM_COL32(0x2b, 0x2b, 0x2b, 255) : IM_COL32(0xc4, 0xc4, 0xc4, 255);
const ImU32 fill_col = ImGui::GetColorU32(ImGuiCol_Header);
ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, IM_COL32_BLACK_TRANS);
+1
View File
@@ -137,6 +137,7 @@ void drawColorMarker(ImDrawList *dl, const ImVec2 &pos, ImU32 col);
void loadFonts();
void applyTheme(int theme); // safe to call at runtime
bool isDarkTheme(); // the theme applyTheme() resolved
CabanaColor signalFillColor(const CabanaColor &c);
ImU32 highlightedTextColor();
ImU32 paletteBrightText();
@@ -455,13 +455,14 @@ 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(s->color.darker(125))); // 4/5x brightness
painter->AddRectFilled(rect.Min, rect.Max, toImU32(signalFillColor(s->color).darker(125))); // 4/5x brightness
} else {
drawSignalCell(painter, rect, index, s);
}
}
} else if (item->valid && item->bg_color.alpha() > 0) {
painter->AddRectFilled(rect.Min, rect.Max, toImU32(item->bg_color));
} else if (item->valid) {
if (isDarkTheme()) painter->AddRectFilled(rect.Min, rect.Max, IM_COL32(255, 255, 255, 20));
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();
pen = bright ? paletteBrightText() : paletteText(is_message_active_);
@@ -524,9 +525,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 = sig->color;
CabanaColor color = signalFillColor(sig->color);
color.a = item->bg_color.alpha();
const ImU32 edge = toImU32(sig->color.darker(125));
const ImU32 edge = toImU32(signalFillColor(sig->color).darker(125));
for (const ImRect &clip : region) {
painter->PushClipRect(clip.Min, clip.Max, true);
@@ -267,7 +267,7 @@ void SignalView::paintCell(ImDrawList *painter, const ImRect &option_rect, const
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(item->sig->color.darker(item->highlight ? 125 : 0)), 3.0f);
painter->AddRectFilled(icon_rect.Min, icon_rect.Max, toImU32(signalFillColor(item->sig->color).darker(item->highlight ? 125 : 0)), 3.0f);
drawText(painter, icon_rect, std::to_string(item->row() + 1).c_str(), item->highlight ? IM_COL32_WHITE : IM_COL32_BLACK,
nullptr, LABEL_FONT);
@@ -289,7 +289,7 @@ void SignalView::paintCell(ImDrawList *painter, const ImRect &option_rect, const
} else if (column == 1) {
if (!item->sparkline.isEmpty()) {
const ImVec2 sparkline_size = item->sparkline.size;
item->sparkline.draw(painter, rect.Min);
item->sparkline.draw(painter, rect.Min, selected ? text_color : 0);
// min-max value
rect.Min.x += sparkline_size.x + 1;
float value_adjust = 10;
-1
View File
@@ -242,7 +242,6 @@ static std::unordered_map<std::string, std::string> load_bootstrap_icons() {
}
namespace utils {
std::string homePath() {
const char *home = ::getenv("HOME");
return home ? home : "";
+10 -11
View File
@@ -60,19 +60,18 @@ ValidState validateIpAddress(const std::string &input);
// C-locale floating-point
ValidState validateDouble(const std::string &input);
// "Darcula" like dark theme
struct DarkTheme {
static constexpr CabanaColor window{0x35, 0x35, 0x35};
static constexpr CabanaColor window_text{0xbb, 0xbb, 0xbb};
static constexpr CabanaColor base{0x3c, 0x3f, 0x41};
static constexpr CabanaColor tooltip_text{0xbb, 0xbb, 0xbb};
static constexpr CabanaColor text{0xbb, 0xbb, 0xbb};
static constexpr CabanaColor button{0x3c, 0x3f, 0x41};
static constexpr CabanaColor highlight{0x2f, 0x65, 0xca};
static constexpr CabanaColor bright_text{0xf0, 0xf0, 0xf0};
static constexpr CabanaColor disabled_text{0x77, 0x77, 0x77};
static constexpr CabanaColor light{0x77, 0x77, 0x77};
static constexpr CabanaColor dark{0x35, 0x35, 0x35};
static constexpr CabanaColor window_text{0xff, 0xff, 0xff};
static constexpr CabanaColor base{0x19, 0x19, 0x19};
static constexpr CabanaColor tooltip_text{0xff, 0xff, 0xff};
static constexpr CabanaColor text{0xff, 0xff, 0xff};
static constexpr CabanaColor button{0x35, 0x35, 0x35};
static constexpr CabanaColor highlight{0x2a, 0x82, 0xda};
static constexpr CabanaColor bright_text{0xff, 0xff, 0xff};
static constexpr CabanaColor disabled_text{0x7f, 0x7f, 0x7f};
static constexpr CabanaColor light{0x50, 0x50, 0x50};
static constexpr CabanaColor dark{0x23, 0x23, 0x23};
};
namespace utils {