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.