I want to build a gui application, which should be completely drawn on a canvas, no other widgets will be used.
package cn.ggfan.fun.music.gui;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class GGPlayerGUI {
private static Shell shell;
/**
* #param args
*/
public static void main(String[] args) {
final Display display = new Display();
shell = new Shell(display, SWT.NO_TRIM | SWT.RESIZE);
shell.setLayout(new FillLayout());
shell.setSize(300, 500);
shell.setAlpha(200);
Canvas canvas = new Canvas(shell, SWT.NONE);
canvas.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
canvas.addPaintListener(new PaintListener(){
public void paintControl(PaintEvent e) {
e.gc.setAlpha(255);
e.gc.setAntialias(SWT.ON);
e.gc.setLineWidth(2);
e.gc.setForeground(new Color(display, 187, 30, 136));
e.gc.drawLine(0, 80, 300, 80);
e.gc.drawLine(0, 450, 300, 450);
//gc.fillPolygon(new int[] { 25,5,45,45,5,45 })
e.gc.setBackground(new Color(display, 187, 30, 136));
e.gc.fillPolygon(new int[]{100,470,90,475,100,480});
e.gc.fillPolygon(new int[]{110,470,100,475,110,480});
//e.gc.fillPolygon(new int[]{100,470,90,475,100,480});
e.gc.fillRoundRectangle(145,470,3,10,2,2);
e.gc.fillRoundRectangle(152,470,3,10,2,2);
e.gc.fillPolygon(new int[]{200,470,210,475,200,480});
e.gc.fillPolygon(new int[]{190,470,200,475,190,480});
e.gc.setForeground(new Color(display, 250, 250, 250));
//e.gc.setLineWidth(1);
e.gc.drawOval(80, 455, 40, 40);
e.gc.drawOval(130, 455, 40, 40);
e.gc.drawOval(180, 455, 40, 40);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
it looks like this:
what should I do to make the "buttons" can be clicked and have hover effect?
If I understand your code correctly, your buttons are circles (ovals) on a Canvas.
You need to write a position method that determines whether or not a particular x, y point on the Canvas is inside or outside of one of the ovals. You use a MouseMoveListener, the mouseMove method together with your position method, to determine when the mouse is inside or outside one of your ovals.
If the mouse is inside an oval, you draw the "hover" version of the oval. If the mouse is outside an oval, you draw the regular version of the oval.
You use a MouseListener, the mouseDown method together with your position method, to determine when the user has left clicked on one of the ovals.
Related
I need to make a group of toggle button like the following, with the white background being the selected button, and two buttons take 50% width of the parent container. Two toggle buttons are place inside HBox. The styling
So far I tried, stuck like this.
.viewType .toggle-button {
-fx-padding: 0 2 0 2;
-fx-background-color: #000;
-fx-text-fill: white;
}
.viewType .toggle-button:selected {
-fx-padding: 0;
-fx-background-color: white;
-fx-text-fill: black;
-fx-border-wdith: 2;
-fx-border-color: black;
-fx-border-radius: 4;
}
You can set the buttons maxWidth to max double in java side. This will provide your buttons to same width in HBox. Hope it is useful:
btn1.setMaxWidth(Double.MAX_VALUE);
btn2.setMaxWidth(Double.MAX_VALUE);
You can check the following link for useful information related with sizing and aligning nodes:
Sizing and Aligning Nodes
If you want both buttons to have equal width, use a GridPane instead of a HBox, and use column constraints to make the two columns equal widths:
GridPane grid = new GridPane();
grid.getColumnConstraints().addAll(createCol(), createCol());
ToggleButton toggle1 = new ToggleButton("...");
toggle1.setMaxWidth(Double.MAX_VALUE);
ToggleButton toggle2 = new ToggleButton("...");
toggle2.setMaxWidth(Double.MAX_VALUE);
grid.addRow(0, toggle1, toggle2);
// ...
private ColumnConstraints createCol() {
ColumnConstraints col = new ColumnConstraints();
col.setPercentWidth(50);
col.setFillWidth(true);
return col ;
}
You can further control how big the grid pane is in its parent by configuring the parent (details depend on what type of pane is used for the parent).
SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class EqualSizedButtons extends Application {
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
grid.getStyleClass().add("viewType");
grid.getColumnConstraints().addAll(createCol(), createCol());
ToggleButton toggle1 = new ToggleButton("A");
toggle1.setMaxWidth(Double.MAX_VALUE);
ToggleButton toggle2 = new ToggleButton("This is really big button B");
toggle2.setMaxWidth(Double.MAX_VALUE);
grid.addRow(0, toggle1, toggle2);
new ToggleGroup().getToggles().addAll(toggle1, toggle2);
BorderPane root = new BorderPane();
root.setBottom(grid);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private ColumnConstraints createCol() {
ColumnConstraints col = new ColumnConstraints();
col.setPercentWidth(50);
col.setFillWidth(true);
return col ;
}
public static void main(String[] args) {
launch(args);
}
}
How do you draw a line between the centers of translated nodes? Given for example the following code snippet:
public class Test extends Application{
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
circle1.setTranslateX(100);
Line line=new Line(circle1.getCenterX(), circle1.getCenterY(), circle2.getCenterX(), circle2.getCenterY());
root.getChildren().add(line);
}
public static void main(String[] args) {
launch(args);
}
}
Running this application will clearly show a red and a green circle. However, there won't be a line because each of the centers of the circles are at the coordinates (0,0). Nevertheless, the circles do not cover each other because one of the circles is translated. This doesn't work:
Line line=new Line(circle1.getCenterX()+circle1.getTranslateX(), circle1.getCenterY()+circle1.getTranslateY(), circle2.getCenterX()+circle2.getTranslateX(), circle2.getCenterY()+circle2.getTranslateY());
Finally, let's assume that there is an approach to draw a line connecting the centers of the two circles. If, after the line is drawn, I would invoke circle2.setTranslateX(50);, how do I ensure that the endpoint of the line on the side of circle2 moves accordingly?
A StackPane is a managed layout pane, meaning that it manages the positions of its child nodes (by default it centers them); the translation is applied after the StackPane positions the nodes. This is why the circles appear in different locations but the line is not where you expect. Using a Pane instead of a StackPane will make things work as you expect.
To keep the line in the correct position relative to the circles when the circles are repositioned dynamically, bind the startX, startY, endX, and endY properties, instead of just setting them.
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LineConnectingCircles extends Application{
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
// move circles so we can see them:
circle1.setTranslateX(100);
circle2.setTranslateY(50);
Line line = new Line();
// bind ends of line:
line.startXProperty().bind(circle1.centerXProperty().add(circle1.translateXProperty()));
line.startYProperty().bind(circle1.centerYProperty().add(circle1.translateYProperty()));
line.endXProperty().bind(circle2.centerXProperty().add(circle2.translateXProperty()));
line.endYProperty().bind(circle2.centerYProperty().add(circle2.translateYProperty()));
root.getChildren().add(line);
// create some animations for the circles to test the line binding:
Button button = new Button("Animate");
TranslateTransition circle1Animation = new TranslateTransition(Duration.seconds(1), circle1);
circle1Animation.setByY(150);
TranslateTransition circle2Animation = new TranslateTransition(Duration.seconds(1), circle2);
circle2Animation.setByX(150);
ParallelTransition animation = new ParallelTransition(circle1Animation, circle2Animation);
animation.setAutoReverse(true);
animation.setCycleCount(2);
button.disableProperty().bind(animation.statusProperty().isEqualTo(Animation.Status.RUNNING));
button.setOnAction(e -> animation.play());
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
Scene scene = new Scene(new BorderPane(root, null, null, button, null), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I want to provide an application that:
allows to move images around (here a rectangle)
if that object is moved out of the working area, start Drag-and-Drop for transfere to other applications
So the javafx DragDetected() would come too soon during the object move on the canvas area, I suppress the onDragDetected() handling and in the onMouseDragged() handler I tried to convert the MouseDrag event into a Drag event using
event.setDragDetect(true);
But the onDragDetected() comes never again..... what can I do?
The full sample application is:
package fx.samples;
import java.io.File;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DragRectangle extends Application {
Point2D lastXY = null;
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Scene scene = new Scene(mainPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
Rectangle area = new Rectangle(0, 0, 500 , 500);
Rectangle rect = new Rectangle(0, 0, 30, 30);
rect.setFill(Color.RED);
mainPane.getChildren().add(rect);
rect.setOnMouseDragged(event -> {
System.out.println("Move");
Node on = (Node)event.getTarget();
if (lastXY == null) {
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
}
double dx = event.getSceneX() - lastXY.getX();
double dy = event.getSceneY() - lastXY.getY();
on.setTranslateX(on.getTranslateX()+dx);
on.setTranslateY(on.getTranslateY()+dy);
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) {
System.out.println("->Drag");
event.setDragDetect(true);
} else {
event.consume();
}
});
rect.setOnDragDetected(event -> {
System.out.println("Drag:"+event);
if (area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) { event.consume(); return; }
Node on = (Node)event.getTarget();
Dragboard db = on.startDragAndDrop(TransferMode.COPY);
db.setContent(makeClipboardContent(event, on, null));
event.consume();
});
rect.setOnMouseReleased(d -> lastXY = null);
}
public static ClipboardContent makeClipboardContent(MouseEvent event, Node child, String text) {
ClipboardContent cb = new ClipboardContent();
if (text != null) {
cb.put(DataFormat.PLAIN_TEXT, text);
}
if (!event.isShiftDown()) {
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Bounds b = child.getBoundsInParent();
double f = 10;
params.setViewport(new Rectangle2D(b.getMinX()-f, b.getMinY()-f, b.getWidth()+f+f, b.getHeight()+f+f));
WritableImage image = child.snapshot(params, null);
cb.put(DataFormat.IMAGE, image);
try {
File tmpFile = File.createTempFile("snapshot", ".png");
LinkedList<File> list = new LinkedList<File>();
ImageIO.write(SwingFXUtils.fromFXImage(image, null),
"png", tmpFile);
list.add(tmpFile);
cb.put(DataFormat.FILES, list);
} catch (Exception e) {
}
}
return cb;
}
public static void main(String[] args) {
launch(args);
}
}
Ok, I spend some hours reading the JavaFX sources and playing arround with EventDispatcher etc... its finally easy:
In Short:
Suppress the system drag start proposal in the onMouseDragged() handler and set that flag on your behalf:
onMouseDragged(e -> {
e.setDragDetect(false); // clear the system proposal
if (...) e.setDragDetect(true); // trigger drag on your own decision
}
Long text:
The mechanism to start a DragDetected is consequently using the MouseEvent MOUSE_DRAGGED. The system Drag detection will apply some rules to determine if the current mouse-drag will be interpreted as a drag, here the original code:
if (dragDetected != DragDetectedState.NOT_YET) {
mouseEvent.setDragDetect(false);
return;
}
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
pressedX = mouseEvent.getSceneX();
pressedY = mouseEvent.getSceneY();
mouseEvent.setDragDetect(false);
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
double deltaX = Math.abs(mouseEvent.getSceneX() - pressedX);
double deltaY = Math.abs(mouseEvent.getSceneY() - pressedY);
mouseEvent.setDragDetect(deltaX > hysteresisSizeX ||
deltaY > hysteresisSizeY);
}
}
and it set
mouseEvent.setDragDetect(true)
in the normal MOUSE_DRAG event. That event is passed down and is being processed by all 'down-chain' EventDispatchers... only if this events finally arrives for processing and if the isDragDetect flag is still true, a follow up DragDetected event will be generated.
So I am able to delay the DragDetected by clearing the isDragDetect flag on its way down using an EventDispatcher:
mainPane.setEventDispatcher((event, chain) -> {
switch (event.getEventType().getName()) {
case "MOUSE_DRAGGED":
MouseEvent drag = (MouseEvent)event;
drag.setDragDetect(false);
if (!area.intersects(drag.getSceneX(), drag.getSceneY(), 1, 1)) {
System.out.println("->Drag down");
drag.setDragDetect(true);
}
break;
}
return chain.dispatchEvent(event);
});
And if this code decides that a drag condition is reached, it simply sets the flag.
drag.setDragDetect(true);
Now I am able to move precisely my objects and start the Drag if they are moved outside the application area.
And after some minutes of thinking: the EventDispatcher is not necessary, all can be done in the onMouseDragged handler...
Full code:
package fx.samples;
import java.io.File;
import java.util.LinkedList;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class DragRectangle extends Application {
Point2D lastXY = null;
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Scene scene = new Scene(mainPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
Rectangle area = new Rectangle(0, 0, 500 , 500);
Rectangle rect = new Rectangle(0, 0, 30, 30);
rect.setFill(Color.RED);
mainPane.getChildren().add(rect);
rect.setOnMouseDragged(event -> {
System.out.println("Move");
event.setDragDetect(false);
Node on = (Node)event.getTarget();
if (lastXY == null) {
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
}
double dx = event.getSceneX() - lastXY.getX();
double dy = event.getSceneY() - lastXY.getY();
on.setTranslateX(on.getTranslateX()+dx);
on.setTranslateY(on.getTranslateY()+dy);
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) event.setDragDetect(true);
event.consume();
});
rect.setOnDragDetected(event -> {
System.out.println("Drag:"+event);
Node on = (Node)event.getTarget();
Dragboard db = on.startDragAndDrop(TransferMode.COPY);
db.setContent(makeClipboardContent(event, on, "red rectangle"));
event.consume();
});
rect.setOnMouseReleased(d -> lastXY = null);
}
public static ClipboardContent makeClipboardContent(MouseEvent event, Node child, String text) {
ClipboardContent cb = new ClipboardContent();
if (text != null) {
cb.put(DataFormat.PLAIN_TEXT, text);
}
if (!event.isShiftDown()) {
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Bounds b = child.getBoundsInParent();
double f = 10;
params.setViewport(new Rectangle2D(b.getMinX()-f, b.getMinY()-f, b.getWidth()+f+f, b.getHeight()+f+f));
WritableImage image = child.snapshot(params, null);
cb.put(DataFormat.IMAGE, image);
try {
File tmpFile = File.createTempFile("snapshot", ".png");
LinkedList<File> list = new LinkedList<File>();
ImageIO.write(SwingFXUtils.fromFXImage(image, null),
"png", tmpFile);
list.add(tmpFile);
cb.put(DataFormat.FILES, list);
} catch (Exception e) {
}
}
return cb;
}
public static void main(String[] args) {
launch(args);
}
}
Based on this:
The default drag detection mechanism uses mouse movements with a pressed button in combination with hysteresis. This behavior can be augmented by the application. Each MOUSE_PRESSED and MOUSE_DRAGGED event has a dragDetect flag that determines whether a drag gesture has been detected. The default value of this flag depends on the default detection mechanism and can be modified by calling setDragDetect() inside of an event handler. When processing of one of these events ends with the dragDetect flag set to true, a DRAG_DETECTED MouseEvent is sent to the potential gesture source (the object on which a mouse button has been pressed). This event notifies about the gesture detection.
your assumption that just enabling setDragDetect(true) at some point after the mousedragged has started will fire the drag event, does not work.
The reason for that is there is some private DragDetectedState flag, that is used to set the drag state at the beginning of the gesture. And once it's already selected, it can't be changed.
So if the event it's not triggered, what you could do is manually trigger it yourself:
if (!area.intersects(event.getSceneX(), event.getSceneY(), 1, 1)) {
System.out.println("->Drag");
event.setDragDetect(true);
Event.fireEvent(rect, new MouseEvent(MouseEvent.DRAG_DETECTED, 0, 0, 0, 0, MouseButton.PRIMARY, 0, false, false, false, false, true, false, false, true, true, true, null));
}
This would effectively trigger a drag dectected event, and rect.setOnDragDetected() will be called.
But this is what you will get:
Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException:
Cannot start drag and drop outside of DRAG_DETECTED event handler
at javafx.scene.Scene.startDragAndDrop(Scene.java:5731)
at javafx.scene.Node.startDragAndDrop(Node.java:2187)
Basically, you can't combine DragDetected with MouseDragged, since the startDragAndDrop method has to be called within a Drag_Detected event:
Confirms a potential drag and drop gesture that is recognized over this Node. Can be called only from a DRAG_DETECTED event handler.
So instead of trying to combine two different events, my suggestion is you just use drag detected event, allowing your scene or parts of it to accept the drop, and translating your node there, while at the same time, if the drag&drop leaves the scene, you could drop the file on the new target.
Something like this:
#Override
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Rectangle rect = new Rectangle(0, 0, 30, 30);
rect.setFill(Color.RED);
mainPane.getChildren().add(rect);
Scene scene = new Scene(mainPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
rect.setOnDragDetected(event -> {
Node on = (Node)event.getSource();
Dragboard db = on.startDragAndDrop(TransferMode.ANY);
db.setContent(makeClipboardContent(event, on, null));
event.consume();
});
mainPane.setOnDragOver(e->{
e.acceptTransferModes(TransferMode.ANY);
});
mainPane.setOnDragExited(e->{
rect.setLayoutX(e.getSceneX()-rect.getWidth()/2d);
rect.setLayoutY(e.getSceneY()-rect.getHeight()/2d);
});
}
I am trying to make a JFrame scrollable by using Palette. In Netbeans if I make a panel with dimensions (300, 500) and a ScrollPane with dimensions (200,200) then if I drag and drop the panel into the ScrollPane it creates automatically the bars.
In eclipse I tried it with the same way and I cannot make it. Moreover the final code in eclipse after the attempt is the following:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
public class InsertWaterRawData extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
InsertWaterRawData frame = new InsertWaterRawData();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public InsertWaterRawData() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 577, 383);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(236, 87, 200, 200);
contentPane.add(scrollPane);
JPanel panel = new JPanel();
scrollPane.setViewportView(panel);
panel.setLayout(null);
}
}
Is there any way to make it with the palette ??
Thanks in advance
panel.setLayout(null);
Don't use a null layout!!!
The scrollbars will appear automatically when the preferred size of the panel is greater than the size of the scrollpane.
When you use a layout manager the preferred size of the panel will be calculated automatically as you add components to the panel.
If you are doing custom painting on your panel, then you need to override the getPreferredSize() method of your panel to return an appropriate size.
Based on the code you posted there is no need for scrollbars because no components have been added to the panel.
Simplified Problem:
Make one Node "A" that is on top of another Node "B" to be half transparent to MouseEvents, so the Events will reach the underlying Node "B". Both Nodes are of equal size but Node "A" has a half transparent background image so one half of Node "B" is visible.
Real Problem:
I have a menu of tabs. Each tab can be dragged to expand the corresponding menu layer. Therefore each tab layer is a Pane with a partially transparent background (basically a polygon) of which the transparent part should be also transparent to MouseEvents.
The illustration (which I can't post yet, see link: Illustration of tabs, the dark green line is the border of the green Pane) shows the basic principle: just imagine only the tabs are visible and the layer itself can be pulled to the right to view it's content.
So the question is, how do I make a region of a Node transparent to MouseEvents without making the whole Node transparent?
Thank you for your help!
Update:
To clarify the simple problem here is the corresponding code:
//Create parent group
Group root = new Group();
//Create linear gradient, so one side is transparent
Stop[] stops = new Stop[] { new Stop(0, Color.rgb(0, 255, 0, 0.0)), new Stop(1, Color.rgb(0, 255, 0, 1.0))};
LinearGradient lg1 = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, stops);
//Create the rectangles
Rectangle A = new Rectangle(100, 50, lg1);
Rectangle B = new Rectangle(100,50, Color.RED);
//Add eventHandlers
A.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("Clicked A");
}
});
B.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("Clicked B");
}
});
root.getChildren().addAll(B, A);
//Add to Scene..
Hope this helps.
Consider the pickOnBounds property, it may help in your situation, but it is not clear to me without seeing your code attempt which fails for the simplified problem.
node.setPickOnBounds(true)
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.
The code below demonstrates how this may be used by creating a square overlaid by an ImageView for an Image which contains tranparent pixels. If pickOnBounds is set to true for the ImageView, then, even if you click on the transparent pixels in the image, the ImageView will receive the mouseClick event. If pickOnBounds is set to false for the ImageView, then, even if you click on the transparent pixels in the image, the ImageView will not process the click and the click event will be received by the node behind the image.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class PickOnBoundsDemo extends Application {
public static void main(String[] args) { Application.launch(args); }
#Override public void start(Stage stage) {
final Rectangle back = new Rectangle(128, 128);
back.setFill(Color.FORESTGREEN);
final ImageView front = new ImageView("http://icons.iconarchive.com/icons/aha-soft/free-large-boss/128/Wizard-icon.png");
// icon: Linkware (Backlink to http://www.aha-soft.com required)
final StackPane pickArea = new StackPane();
pickArea.getChildren().addAll(
back,
front
);
final ToggleButton pickTypeSelection = new ToggleButton("Pick On Bounds");
final Label pickResult = new Label();
Bindings.bindBidirectional(front.pickOnBoundsProperty(), pickTypeSelection.selectedProperty());
front.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent t) {
pickResult.setText("Front clicked");
}
});
back.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent t) {
pickResult.setText("Back clicked");
}
});
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().setAll(
pickArea,
new Label("Click inside the above area to test mouse picking."),
pickTypeSelection,
pickResult
);
layout.setAlignment(Pos.CENTER);
stage.setScene(new Scene(layout));
stage.show();
}
}