Query and hover

Read the cursor’s position in plot data coordinates live, and annotate it. Inside a plot scope IsPlotHovered() tells you the cursor is over the plot area and GetPlotMousePos() gives its position in data coordinates — so the example draws a vertical crosshair and a text label that follow the mouse. The v2 plot scope also serializes hovered + mouse-plot-pos into its snapshot, so a headless test can move the synthetic cursor onto the plot and assert on exactly the values the annotation uses.

plot(CHART, (title = "hover me", size = float2(-1.0f, 600.0f), flags = ImPlotFlags.None)) {
    setup_axes("x", "y")
    setup_axes_limits(0.0lf, double(N), -1.2lf, 1.2lf)
    plot_line("signal", g_wave)
    if (IsPlotHovered()) {
        let mp = GetPlotMousePos(ImAxis.X1, ImAxis.Y1)   // data coords
        g_cursor_x[0] = mp.x
        plot_inf_lines("cursor", g_cursor_x)             // vertical crosshair
        plot_text("({mp.x:.1f}, {mp.y:.2f})", mp.x, mp.y, float2(10.0f, 10.0f))
    }
}

Source: examples/tutorial/query_and_hover.das.

 1options gen2
 2options _comment_hygiene = true
 3
 4require imgui/imgui_harness
 5require imgui/imgui_containers_builtin
 6require imgui/imgui_widgets_builtin
 7require imgui/imgui_implot_boost_v2
 8require implot
 9require math
10require strings
11
12// =============================================================================
13// TUTORIAL: query_and_hover — read the cursor's plot position live and annotate it.
14//
15//   IsPlotHovered()                       — is the cursor over the plot area?
16//   GetPlotMousePos(ImAxis.X1, ImAxis.Y1) — the cursor in DATA coords (valid in-scope).
17//   plot_inf_lines(id, [x])               — a vertical reference line at data-x.
18//   plot_text(text, x, y, pix_offset)     — a label anchored at a data point.
19//
20// The v2 `plot` scope ALSO serializes hovered + mouse-plot-pos into its snapshot,
21// so a headless test can move the synthetic cursor onto the plot and assert on them.
22//
23// STANDALONE: daslang.exe modules/dasImguiImplot/examples/tutorial/query_and_hover.das
24// LIVE:       daslang-live modules/dasImguiImplot/examples/tutorial/query_and_hover.das
25// =============================================================================
26
27let N = 100
28
29var g_ctx : ImPlotContext?
30var g_wave : array<double>
31var g_cursor_x : array<double>   // 1-element, reused each frame for the crosshair
32
33[export]
34def init() {
35    harness_init("dasImguiImplot — query_and_hover", 1100, 720)
36    g_ctx = implot::CreateContext()
37    g_wave <- [for (i in range(N)); double(sin(float(i) * 0.12f)) * 0.9lf]
38    g_cursor_x <- [0.0lf]
39}
40
41[export]
42def update() {
43    if (!harness_begin_frame()) return
44    harness_new_frame()
45
46    SetNextWindowPos(float2(20.0, 20.0), ImGuiCond.Always)
47    SetNextWindowSize(float2(1060.0, 680.0), ImGuiCond.Always)
48    window(PLOT_WIN, (text = "query & hover", closable = false,
49                      flags = ImGuiWindowFlags.None)) {
50        text("Hover the plot: it reads GetPlotMousePos live and annotates the cursor.")
51        plot(CHART, (title = "hover me", size = float2(-1.0f, 600.0f),
52                     flags = ImPlotFlags.None)) {
53            setup_axes("x", "y")
54            setup_axes_limits(0.0lf, double(N), -1.2lf, 1.2lf)
55            next_line_style(float4(0.40f, 0.70f, 1.00f, 1.00f), 2.0f)
56            plot_line("signal", g_wave)
57            // Live query: while hovered, draw a crosshair + label at the cursor.
58            if (IsPlotHovered()) {
59                let mp = GetPlotMousePos(ImAxis.X1, ImAxis.Y1)
60                g_cursor_x[0] = mp.x
61                next_line_style(float4(1.00f, 1.00f, 1.00f, 0.50f), 1.0f)
62                plot_inf_lines("cursor", g_cursor_x)
63                plot_text("({mp.x:.1f}, {mp.y:.2f})", mp.x, mp.y, float2(10.0f, 10.0f))
64            }
65        }
66    }
67
68    harness_end_frame()
69}
70
71[export]
72def shutdown() {
73    if (g_ctx != null) {
74        DestroyContext(g_ctx)
75    }
76    harness_shutdown()
77}
78
79[export]
80def main() {
81    init()
82    while (!exit_requested()) {
83        update()
84    }
85    shutdown()
86}

Walkthrough

The recording glides the cursor across the plot with real synthetic input: the vertical crosshair and the (x, y) label track it every frame, and ImPlot’s corner mouse readout updates alongside. It self-verifies that hovered flips true and GetPlotMousePos resolves into the band the cursor was aimed at — left, then right — so a dead hover or a frozen readout fails at teardown.

Live query

IsPlotHovered() and GetPlotMousePos(x_axis, y_axis) are only valid between BeginPlot and EndPlot, so call them inside the scope body. GetPlotMousePos returns an ImPlotPoint (.x / .y doubles) in data coordinates — already projected through the axes, so a cursor at screen-x maps to the data value under it.

Annotating the cursor

plot_inf_lines(id, [x]) draws an infinite vertical line at each x in the array — here a one-element array reused each frame for the crosshair (kept as a global to avoid a per-frame allocation). plot_text(text, x, y, pix_offset) anchors a label at a data point, offset by screen pixels so it sits beside the cursor rather than under it. The label uses fmt precision specifiers in the interpolation — {mp.x:.1f} / {mp.y:.2f} — so it reads (74.0, -0.19) instead of full double precision.

Testing the hover

test_query_and_hover calls move_to(d, plot_center(...)) to put the synthetic cursor on the plot, wait_for_hovered until the snapshot reports it, then reads plot_mouse_pos and asserts it falls inside the axis range — the same synthetic-equals-real path the drag tools rely on.