In the java doc it says for setMouseTransparent
that is affects all children as well as the parent.
How can it be made so only the parent's transparent areas (can see other nodes below it but not responding to mouse events) are transparent to mouse events so that the nodes below it may receive them.
This happens when stacking two XYCharts in the same pane. Only the last one added can receive events.
Set pickOnBounds
for the relevant nodes to false
, then clicking on transparent areas in a node won't register a click with that node.
Defines how the picking computation is done for this node when triggered by a MouseEvent or a contains function call. If pickOnBounds is true, then picking is computed by intersecting with the bounds of this node, else picking is computed by intersecting with the geometric shape of this node.
Sample Output
This sample is actually far more complicated than is necessary to demonstrate the pickOnBounds
function - but I just did something this complicated so that it shows what happens "when stacking two XYCharts
in the same pane" as mentioned in the poster's question.
In the sample below two line charts are stacked on top of each other and the mouse is moved over the data line in one chart which has a glow function attached to it's mouseenter event. The mouse is then moved off of the first line chart data and the glow is removed from it. The mouse is then placed over the second line chart data of an underlying stacked chart and the glow is added to that linechart in the underlying stacked chart.
This sample was developed using Java8 and the coloring and behaviour described is what I exeperienced running the program on Mac OS X and Java 8b91.
Sample Code
The code below is just for demonstrating that pickOnBounds
does work for allowing you to pass mouse events through transparent regions stacked on top of opaque node shapes. It is not a recommended code practice to follow for styling lines in charts (you are better off using style sheets than lookups for that), it is also not necessary that you use a line chart stack to get multiple series on a single chart - it was only necessary or simpler to do these things to demonstrate the pick on bounds concept application for this answer.
Note the recursive call to set the pickOnBounds
property for the charts after the charts have been shown on a stage and all of their requisite nodes created.
Sample code is an adaption of JavaFX 2 XYChart.Series and setOnMouseEntered:
import javafx.application.Application; import javafx.collections.*; import javafx.event.EventHandler; import javafx.scene.*; import javafx.scene.chart.*; import javafx.scene.effect.Glow; import javafx.scene.input.MouseEvent; import javafx.scene.layout.StackPane; import javafx.scene.shape.Path; import javafx.stage.Stage; public class LineChartSample extends Application { @SuppressWarnings("unchecked") @Override public void start(Stage stage) { // initialize data ObservableList<XYChart.Data> data = FXCollections.observableArrayList( new XYChart.Data(1, 23),new XYChart.Data(2, 14),new XYChart.Data(3, 15),new XYChart.Data(4, 24),new XYChart.Data(5, 34),new XYChart.Data(6, 36),new XYChart.Data(7, 22),new XYChart.Data(8, 45),new XYChart.Data(9, 43),new XYChart.Data(10, 17),new XYChart.Data(11, 29),new XYChart.Data(12, 25) ); ObservableList<XYChart.Data> reversedData = FXCollections.observableArrayList( new XYChart.Data(1, 25), new XYChart.Data(2, 29), new XYChart.Data(3, 17), new XYChart.Data(4, 43), new XYChart.Data(5, 45), new XYChart.Data(6, 22), new XYChart.Data(7, 36), new XYChart.Data(8, 34), new XYChart.Data(9, 24), new XYChart.Data(10, 15), new XYChart.Data(11, 14), new XYChart.Data(12, 23) ); // create charts final LineChart<Number, Number> lineChart = createChart(data); final LineChart<Number, Number> reverseLineChart = createChart(reversedData); StackPane layout = new StackPane(); layout.getChildren().setAll( lineChart, reverseLineChart ); // show the scene. Scene scene = new Scene(layout, 800, 600); stage.setScene(scene); stage.show(); // make one line chart line green so it is easy to see which is which. reverseLineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: forestgreen;"); // turn off pick on bounds for the charts so that clicks only register when you click on shapes. turnOffPickOnBoundsFor(lineChart); turnOffPickOnBoundsFor(reverseLineChart); // add a glow when you mouse over the lines in the line chart so that you can see that they are chosen. addGlowOnMouseOverData(lineChart); addGlowOnMouseOverData(reverseLineChart); } @SuppressWarnings("unchecked") private void turnOffPickOnBoundsFor(Node n) { n.setPickOnBounds(false); if (n instanceof Parent) { for (Node c: ((Parent) n).getChildrenUnmodifiable()) { turnOffPickOnBoundsFor(c); } } } private void addGlowOnMouseOverData(LineChart<Number, Number> lineChart) { // make the first series in the chart glow when you mouse over it. Node n = lineChart.lookup(".chart-series-line.series0"); if (n != null && n instanceof Path) { final Path path = (Path) n; final Glow glow = new Glow(.8); path.setEffect(null); path.setOnMouseEntered(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent e) { path.setEffect(glow); } }); path.setOnMouseExited(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent e) { path.setEffect(null); } }); } } private LineChart<Number, Number> createChart(ObservableList<XYChart.Data> data) { final NumberAxis xAxis = new NumberAxis(); final NumberAxis yAxis = new NumberAxis(); xAxis.setLabel("Number of Month"); final LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis); lineChart.setTitle("Stock Monitoring, 2010"); XYChart.Series series = new XYChart.Series(data); series.setName("My portfolio"); series.getData().addAll(); lineChart.getData().add(series); lineChart.setCreateSymbols(false); lineChart.setLegendVisible(false); return lineChart; } public static void main(String[] args) { launch(args); } }
Instead of doing this:
// turn off pick on bounds for the charts so that clicks only register when you click on shapes. turnOffPickOnBoundsFor(lineChart); turnOffPickOnBoundsFor(reverseLineChart);
do this:
// turn off pick on bounds for the charts so that clicks only register when you click on shapes. turnOffPickOnBoundsFor(reverseLineChart, false);
with the folling methods.
private boolean turnOffPickOnBoundsFor(Node n, boolean plotContent) { boolean result = false; boolean plotContentFound = false; n.setPickOnBounds(false); if(!plotContent){ if(containsStyle(n)){ plotContentFound = true; result=true; } if (n instanceof Parent) { for (Node c : ((Parent) n).getChildrenUnmodifiable()) { if(turnOffPickOnBoundsFor(c,plotContentFound)){ result = true; } } } n.setMouseTransparent(!result); } return result; } private boolean containsStyle(Node node){ boolean result = false; for (String object : node.getStyleClass()) { if(object.equals("plot-content")){ result = true; break; } } return result; }
Also you will need to make the chart in front(reverseLineChart) transparent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With