diff --git a/openpilot/tools/cabana/ui/chart/chart.cc b/openpilot/tools/cabana/ui/chart/chart.cc index 060bc91541..4f1d7891ea 100644 --- a/openpilot/tools/cabana/ui/chart/chart.cc +++ b/openpilot/tools/cabana/ui/chart/chart.cc @@ -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(); diff --git a/openpilot/tools/cabana/ui/chart/tiplabel.cc b/openpilot/tools/cabana/ui/chart/tiplabel.cc index 0efc4e7ad6..6f3a1ee54e 100644 --- a/openpilot/tools/cabana/ui/chart/tiplabel.cc +++ b/openpilot/tools/cabana/ui/chart/tiplabel.cc @@ -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(); } diff --git a/openpilot/tools/cabana/ui/chart/tiplabel.h b/openpilot/tools/cabana/ui/chart/tiplabel.h index fe7b099d37..48f20b59f8 100644 --- a/openpilot/tools/cabana/ui/chart/tiplabel.h +++ b/openpilot/tools/cabana/ui/chart/tiplabel.h @@ -20,7 +20,7 @@ public: void showText(const ImVec2 &pt, const std::vector &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; };