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.