cabana: contain tooltips and capture plot drags (#38896)

* cabana: contain chart tooltips within columns

* cabana: refresh tooltip bounds after chart resize

* cabana: capture plot drags to prevent window movement
This commit is contained in:
Trey Moen
2026-09-13 13:53:35 -07:00
committed by GitHub
parent 45a7747ca3
commit c5cf29cf5e
3 changed files with 52 additions and 5 deletions
+12 -2
View File
@@ -507,8 +507,10 @@ void ChartView::draw(float width) {
}
ImGui::EndChild();
// a chart scrolled out of the viewport draws no tip
const ImRect visible_rect = charts_widget_->chartVisibleRect(this);
if (!drawing_ghost_ && visible_rect.GetWidth() > 0 && visible_rect.GetHeight() > 0) tip_label_.draw();
ImRect visible_rect = charts_widget_->chartVisibleRect(this);
visible_rect.ClipWith(ImRect(ImVec2(layout_.rect.Min.x, layout_.plot_area.Min.y),
ImVec2(layout_.rect.Max.x, layout_.plot_area.Max.y)));
if (!drawing_ghost_ && visible_rect.GetWidth() > 0 && visible_rect.GetHeight() > 0) tip_label_.draw(visible_rect);
ImGui::PopID();
}
@@ -575,6 +577,14 @@ void ChartView::drawAxes() {
// ImPlotFlags_NoInputs disables implot's own hover tracking
layout_.plot_hovered = layout_.plot_area.Contains(ImGui::GetMousePos()) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem);
drawSeries();
if (!drawing_ghost_) {
// Own plot clicks so custom scrubbing/zooming cannot also move the floating window.
const ImGuiID input_id = ImGui::GetID("plot_input");
if (ImGui::ItemAdd(layout_.plot_area, input_id)) {
bool hovered, held;
ImGui::ButtonBehavior(layout_.plot_area, input_id, &hovered, &held);
}
}
handleMousePress();
handleMouseMove();
handleMouseRelease();
+38 -2
View File
@@ -19,6 +19,31 @@ ImVec2 TipLabel::layoutLines(ImDrawList *p, const ImVec2 &origin, ImU32 fg) cons
};
const char *heading = !text_.empty() && !text_[0].has_marker ? text_[0].name.c_str() : "Signal";
const char *headers[] = {heading, "Value", "Min", "Max"};
if (compact_) {
const float right = origin.x + std::max(0.0f, area_.GetWidth() - MARGIN * 2 - 1);
auto cell = [&](float left, const std::string &text, ImU32 color) {
if (p) drawElidedText(p, ImRect(ImVec2(left, y), ImVec2(right, y + font_size)), text, color);
};
cell(origin.x, heading, muted);
y += line_height;
for (const auto &line : text_) {
if (!line.has_marker) continue;
if (p) p->AddRectFilled(ImVec2(origin.x, y + (font_size - marker) * 0.5f),
ImVec2(origin.x + marker, y + (font_size + marker) * 0.5f), line.marker);
cell(origin.x + marker + 6, line.name, fg);
y += line_height;
const std::string *values[] = {&line.value, &line.min, &line.max};
const float label_width = ImGui::CalcTextSize("Value").x + gap;
for (int i = 0; i < 3; ++i) {
draw(origin.x, headers[i + 1], muted);
pushMonoFont(font_size);
cell(origin.x + label_width, *values[i], i == 0 ? fg : muted);
popMonoFont();
y += line_height;
}
}
return ImVec2(right - origin.x, y - origin.y);
}
float x = origin.x;
for (int i = 0; i < 4; ++i) {
draw(i ? x + column_widths_[i] - ImGui::CalcTextSize(headers[i]).x : x, headers[i], muted);
@@ -77,8 +102,15 @@ void TipLabel::updateLayout() {
}
popMonoFont();
const ImGuiViewport *viewport = ImGui::GetWindowViewport();
const ImRect bounds(viewport->WorkPos, viewport->WorkPos + viewport->WorkSize);
ImRect bounds(viewport->WorkPos, viewport->WorkPos + viewport->WorkSize);
bounds.ClipWith(area_);
if (bounds.GetWidth() <= 0 || bounds.GetHeight() <= 0) {
visible_ = false;
return;
}
area_ = bounds;
const float numeric_width = column_widths_[1] + column_widths_[2] + column_widths_[3] + 24 + MARGIN * 2 + 1;
compact_ = bounds.GetWidth() < numeric_width + ImGui::GetFontSize() * 4;
column_widths_[0] = std::min(column_widths_[0], std::max(40.0f, std::min(ImGui::GetFontSize() * 16, bounds.GetWidth() - numeric_width)));
if (!text_.empty()) {
ImVec2 extra(1, 1);
@@ -96,13 +128,17 @@ void TipLabel::updateLayout() {
visible_ = false;
}
void TipLabel::draw() {
void TipLabel::draw(const ImRect &rect) {
if (!visible_) return;
area_ = rect;
updateLayout();
if (!visible_) return;
ImDrawList *p = ImGui::GetForegroundDrawList();
p->PushClipRect(area_.Min, area_.Max, true);
// filled panel with a 1px frame
p->AddRectFilled(pos_, pos_ + size_, ImGui::GetColorU32(ImGuiCol_PopupBg), ImGui::GetStyle().PopupRounding);
p->AddRect(pos_, pos_ + size_, ImGui::GetColorU32(ImGuiCol_Border), ImGui::GetStyle().PopupRounding);
layoutLines(p, pos_ + ImVec2(MARGIN, MARGIN), ImGui::GetColorU32(ImGuiCol_Text));
p->PopClipRect();
}
+2 -1
View File
@@ -20,7 +20,7 @@ public:
void showText(const ImVec2 &pt, const std::vector<TipLine> &text, const ImRect &rect);
void hide() { visible_ = false; }
bool isVisible() const { return visible_; }
void draw(); // draws the tip on the foreground draw list; call once per frame
void draw(const ImRect &rect); // draws the tip on the foreground draw list; call once per frame
private:
// lays the lines out from origin, drawing them when p is given; returns the size of the text block
@@ -35,5 +35,6 @@ private:
ImRect area_;
ImVec2 pos_;
ImVec2 size_;
bool compact_ = false;
bool visible_ = false;
};