Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions daslib/implot_boost_v2.das
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ let private LEGEND_LABEL_RETRY_FRAMES = 240 // bounded window to resolve a jus

def private capture_legend(var state : PlotScopeState; title : string) {
// Read the plot's legend into the snapshot AFTER EndPlot, by title — the rect / shown are
// written during EndPlot's legend render, so GetPlot(title) gives current-frame values. Labels
// come from ImPlot as a fresh string per call, so we cache them (rebuild only when the entry set
// written during EndPlot's legend render, so GetPlot(title) gives current-frame values.
// LegendEntryLabel returns a das-heap-owned copy (it allocateString's into our context — see
// the C++ forwarder, issue #9), so the cached label stays valid even after ImPlot resets its
// internal Legend.Labels buffer next frame. We cache labels (rebuild only when the entry set
// changes) rather than re-allocating every frame. Rects / shown are cheap scalars, refreshed
// every frame. (A relabel with no count change reads stale — exotic for a steady plot.)
let n = LegendEntryCount(title)
Expand Down
15 changes: 11 additions & 4 deletions src/dasIMPLOT.main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,17 @@ int LegendEntryCount(const char* title) {
ImPlotPlot* plot = ImPlot::GetPlot(title);
return plot ? plot->Items.GetLegendCount() : 0;
}
const char* LegendEntryLabel(const char* title, int index) {
// allocateString (das heap), NOT the raw GetLegendLabel pointer: that points into ImPlot's
// per-frame Legend.Labels buffer, which the next BeginPlot/SetupFinish resets and may realloc — a
// raw return would dangle by the time the snapshot serializes a frame later (empty/garbage on
// macOS, issue #9). Owning the bytes here keeps the das string valid; mirrors the rest of the
// string-returning API (dasImgui's text_range_string / ImGTB_Slice).
char* LegendEntryLabel(const char* title, int index, das::Context* context, das::LineInfoArg* at) {
ImPlotPlot* plot = ImPlot::GetPlot(title);
if (!plot || index < 0 || index >= plot->Items.GetLegendCount()) return "";
return plot->Items.GetLegendLabel(index);
if (!plot || index < 0 || index >= plot->Items.GetLegendCount())
return context->allocateString(nullptr, 0, at);
const char* label = plot->Items.GetLegendLabel(index);
return context->allocateString(label, strlen(label), at);
}
bool LegendEntryShown(const char* title, int index) {
ImPlotPlot* plot = ImPlot::GetPlot(title);
Expand Down Expand Up @@ -287,7 +294,7 @@ void Module_dasIMPLOT::initMain () {
addExtern<DAS_BIND_FUN(das::LegendEntryCount)>(*this, lib, "LegendEntryCount",
SideEffects::accessExternal, "das::LegendEntryCount")->args({"title"});
addExtern<DAS_BIND_FUN(das::LegendEntryLabel)>(*this, lib, "LegendEntryLabel",
SideEffects::accessExternal, "das::LegendEntryLabel")->args({"title", "index"});
SideEffects::accessExternal, "das::LegendEntryLabel")->args({"title", "index", "context", "lineinfo"});
addExtern<DAS_BIND_FUN(das::LegendEntryShown)>(*this, lib, "LegendEntryShown",
SideEffects::accessExternal, "das::LegendEntryShown")->args({"title", "index"});
addExtern<DAS_BIND_FUN(das::LegendEntryHovered)>(*this, lib, "LegendEntryHovered",
Expand Down
9 changes: 3 additions & 6 deletions tests/integration/test_multi_axes.das
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,9 @@ def test_multi_axes(t : T?) {
t |> success(abs(plot_axis_limit(m, s, "y2_max") - 1050.0lf) < 1.0lf, "Y2 (pressure) max captured ~1050")
// The Y2-routed series' legend label resolves (the first-frame label fix). Poll, don't
// one-shot: ImPlot can resolve the label a few frames after the item first registers, so a
// read on the axis-convergence snapshot races it. Skipped on macOS — the Y2-routed label
// stays empty there (issue #9, to be debugged on an osx box); the rail's drag checks below
// still run on all platforms.
if (get_platform_name() != "darwin") {
t |> success(wait_for_series_shown(s, "pressure", true) != null, "pressure series present in legend (Y2 label captured)")
}
// read on the axis-convergence snapshot races it. Now runs on all platforms — issue #9 (the
// macOS empty/garbage label) is fixed in the LegendEntryLabel forwarder (returns an owned copy).
t |> success(wait_for_series_shown(s, "pressure", true) != null, "pressure series present in legend (Y2 label captured)")

// Drag ONLY the Y2 gutter: pressure's range must move while temp (Y1) and X1 stay put — the
// independent-axis pan the tutorial teaches, distinct from a whole-plot body pan (moves all).
Expand Down
Loading