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

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.