Skip to content

Issue #492: DataPointListener for data-point hover/exit/click collision detection#1002

Merged
timmolter merged 3 commits into
developfrom
issue-492-datapoint-listener
Jul 15, 2026
Merged

Issue #492: DataPointListener for data-point hover/exit/click collision detection#1002
timmolter merged 3 commits into
developfrom
issue-492-datapoint-listener

Conversation

@timmolter

Copy link
Copy Markdown
Member

Closes #492.

What

Adds a public collision-detection API so users can react to the mouse interacting with an individual rendered data point (a bar, marker, bubble, pie slice, etc.) — for drill-down, custom pop-ups, linked views, or context menus — instead of only seeing a tool tip.

Requested in #492 ("detect mouse collisions on a shape rendered ... fire a mouse listener when the cursor moves over the first blue bar").

API

sw.getXChartPanel().addDataPointListener(new DataPointListener() {
  @Override public void onDataPointHover(ChartDataPoint p, MouseEvent e) { ... }
  @Override public void onDataPointExit (ChartDataPoint p, MouseEvent e) { ... }
  @Override public void onDataPointClick(ChartDataPoint p, MouseEvent e) { ... }
});
  • DataPointListener — all three callbacks are default no-ops, so implementations override only what they need. Each receives the originating MouseEvent.
  • ChartDataPoint — immutable: series name, data-point index within the series, formatted x/y values (or single label), pixel coordinates, and the hit Shape.
  • XChartPanel.addDataPointListener / removeDataPointListener.

How it works

  • Internal DataPointDispatcher mirrors the existing ToolTips / Cursor pattern and reuses the per-data-point hit shapes already collected for the tool-tip feature, so collision detection works for every chart type and does not require tool tips to be enabled.
  • Series name + index are threaded through the XY, Category, HorizontalBar and Bubble plots via a new ToolTipData.withSeries(...); other chart types still fire the callbacks (with null series / -1 index) since all 11 plot types already populate hit shapes.
  • When a DataPointListener is registered and the user right-clicks directly on a data point, the built-in Save As... / Print pop-up is auto-suppressed so a custom context menu can be shown. Right-clicking elsewhere is unchanged.

Testing

  • DataPointListenerTest — renders a bar chart offscreen, drives synthetic mouse events across the plot, and asserts hover/exit/click fire with the correct series name and index for all five bars; plus ChartDataPoint equality and listener add/remove wiring.
  • Full xchart suite: 137/137 pass.
  • TestForIssue492 demo (hover/left-click console output + right-click custom context menu on a bar).
  • README: new Data Point Listeners section + feature bullet.

Notes

  • Inside a click handler, use SwingUtilities.isRightMouseButton(e) rather than MouseEvent.isPopupTrigger() (the latter is only meaningful on press/release); the demo and README call this out.

🤖 Generated with Claude Code

Adds a public collision-detection API so users can react to the mouse
interacting with individual rendered data points (bars, markers, bubbles,
pie slices, etc.) rather than only seeing a tool tip.

- New public DataPointListener interface (default no-op hover/exit/click,
  each receiving the originating MouseEvent) and immutable ChartDataPoint
  value object (series name, data-point index, x/y values or label, pixel
  coordinates, hit shape).
- New XChartPanel.addDataPointListener / removeDataPointListener.
- Internal DataPointDispatcher mirrors the ToolTips/Cursor pattern and
  reuses the per-data-point hit shapes already collected for tool tips, so
  it works for every chart type without enabling tool tips.
- Series name + index threaded through the XY, Category, HorizontalBar and
  Bubble plots via ToolTipData.withSeries(...); other chart types still
  fire the callbacks with a null series / -1 index.
- Right-clicking a listened data point auto-suppresses the built-in
  Save As.../Print menu so a custom context menu can be shown; right-click
  elsewhere is unchanged.
- TestForIssue492 demo, DataPointListenerTest coverage, and README section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new public collision-detection API to XChart’s Swing integration so callers can react to mouse hover/exit/click on individual rendered data points (bars/markers/bubbles/etc.) by reusing the existing per-data-point hit shapes already collected for tooltips.

Changes:

  • Introduces DataPointListener and ChartDataPoint, and wires listener registration into XChartPanel.
  • Adds an internal DataPointDispatcher fed from PlotInteractionData after each repaint to perform hit-testing and dispatch callbacks.
  • Threads series name + data-point index through selected plot types via ToolTipData.withSeries(...), plus adds tests and a demo.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
xchart/src/main/java/org/knowm/xchart/XChartPanel.java Adds listener registration/removal, wires DataPointDispatcher, and suppresses default popup menu over listened data points.
xchart/src/main/java/org/knowm/xchart/DataPointListener.java New public listener interface with hover/exit/click callbacks.
xchart/src/main/java/org/knowm/xchart/ChartDataPoint.java New public value object describing the hit data point and its hit shape.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/DataPointDispatcher.java New internal dispatcher doing hit-testing and callback dispatch.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/Chart.java Plumbs dispatcher into consumeInteractionData(...) so it receives fresh hit-shape data.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotInteractionData.java Makes addToolTip(...) return ToolTipData and adds series/index fields + withSeries(...).
xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_XY.java Attaches series name + index to tooltip data for XY plots.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_Category.java Attaches series name + index to tooltip data for category bars.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_HorizontalBar.java Attaches series name + index to tooltip data for horizontal bars.
xchart/src/main/java/org/knowm/xchart/internal/chartpart/PlotContent_Bubble.java Attaches series name + index to tooltip data for bubbles.
xchart/src/test/java/org/knowm/xchart/DataPointListenerTest.java New offscreen-render + synthetic mouse-event test coverage for hover/exit/click behavior and listener wiring.
xchart-demo/src/main/java/org/knowm/xchart/standalone/issues/TestForIssue492.java New demo showcasing hover/click output and custom context menu suppression behavior.
README.md Documents the new “Data Point Listeners” feature and adds it to the feature list.

Comment on lines +19 to +23
* @Override
* public void onDataPointClick(ChartDataPoint dataPoint) {
* System.out.println("Clicked " + dataPoint.getSeriesName()
* + " point #" + dataPoint.getDataPointIndex());
* }
Comment on lines +47 to +52
/**
* Called when a data point's shape is clicked. Use {@link MouseEvent#getButton()} /{@link
* MouseEvent#isPopupTrigger()} to distinguish left-clicks from right-clicks (e.g. to show a
* context menu).
*
* @param dataPoint the clicked data point
Comment on lines +79 to +81
for (DataPointListener listener : listeners) {
listener.onDataPointExit(exited, e);
}
Comment on lines +88 to +90
for (DataPointListener listener : listeners) {
listener.onDataPointHover(hit, e);
}
Comment on lines +102 to +104
for (DataPointListener listener : listeners) {
listener.onDataPointClick(hit, e);
}
Comment on lines +92 to +96
}

@Override
public void mouseClicked(MouseEvent e) {

Comment on lines +42 to +44
* @param dataPoint the data point the mouse just left
* @param e the mouse-move event during which the point was left
*/
- DataPointDispatcher: iterate over a snapshot of the listeners list in the
  hover/exit/click dispatch loops so a listener can add/remove listeners from
  inside a callback (e.g. one-shot handlers) without a ConcurrentModificationException.
- DataPointDispatcher: handle mouseExited so leaving the panel while hovering a
  data point clears the hover state and fires onDataPointExit (previously the
  listener was left "stuck" and hovered never reset).
- DataPointListener javadoc: fix the code example to include the MouseEvent
  parameter (it wouldn't compile), recommend SwingUtilities.isRightMouseButton
  over isPopupTrigger inside click handlers, and loosen the onDataPointExit
  wording to cover the panel-exit case.
- Test: cover the mouseExited -> onDataPointExit path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@timmolter

Copy link
Copy Markdown
Member Author

Addressed the Copilot review feedback in 3f1c9a1:

  • CME risk (DataPointDispatcher, ×3) — the hover/exit/click dispatch loops now iterate over a snapshot (new ArrayList<>(listeners)), so a listener can safely addDataPointListener/removeDataPointListener from inside a callback.
  • mouseExited not handled — added a mouseExited handler that clears the hover state and fires onDataPointExit, so leaving the panel while hovering a point no longer leaves listeners "stuck". Covered by a new test.
  • Javadoc example wouldn't compile — the onDataPointClick snippet now includes the MouseEvent parameter.
  • isPopupTrigger() guidance — the onDataPointClick javadoc now recommends SwingUtilities.isRightMouseButton(e) (matching the README/demo) and notes isPopupTrigger() is only reliable on press/release.
  • onDataPointExit wording — loosened to cover the panel-exit case as well as move-off.

Full xchart suite: 138/138 passing.

The previous tests constructed an XChartPanel, whose constructor calls
Toolkit.getMenuShortcutKeyMaskEx() and throws HeadlessException on the
headless CI runner (no X11 DISPLAY). No other test in the repo instantiates
XChartPanel -- the convention is headless-safe testing.

Refactored to test the collision logic directly against DataPointDispatcher
in the internal.chartpart package (fed a PlotInteractionData, driven with
synthetic MouseEvents sourced from a plain JPanel), covering hover/exit/
click, mouseExited, listener-mutation-during-callback safety, and
isOverDataPoint. Kept the pure ChartDataPoint equality test. Verified with
-Djava.awt.headless=true.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@timmolter timmolter merged commit 0a6a94d into develop Jul 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Collission detection

2 participants