JavaFX increasing a circle's radius by clicking on it - java

I'm trying to create a JavaFX program that creates a circle when you click on the screen. There can be many circles at once. But I can't think of a solution to how to increase a circle's radius when I click on it again.
public class Controller implements Initializable {
#FXML
private Button reset;
#FXML
private AnchorPane anchor;
#FXML
private BorderPane border;
Circle circle = new Circle();
int radius = 20;
public void initialize (URL location, ResourceBundle resources) {
anchor.setOnMouseClicked(event -> {
border.getChildren().add(circle = new Circle());
circle.setCenterX(event.getX());
circle.setCenterY(event.getY());
circle.setRadius(radius);
});
reset.setOnAction(event -> {
border.getChildren().clear();
});
circle.setOnMouseClicked(event -> {
circle.setRadius(radius * 1.5);
});
}
}

The field you declare as circle is never added to the scene graph. So it never appears and its mouseClicked handler is never invoked.
On the other hand, the circles you do add to the scene graph have no mouse clicked handler associated with them. You need to register a handler when you create them:
anchor.setOnMouseClicked(event -> {
Circle circle = new Circle();
border.getChildren().add(circle);
circle.setCenterX(event.getX());
circle.setCenterY(event.getY());
circle.setRadius(radius);
circle.setOnMouseClicked(e -> {
circle.setRadius(circle.getRadius() * 1.5);
// prevent event from propagating to pane:
e.consume();
});
});
And now just get rid of the circle instance field and the handler you associate with it entirely.

Related

javafxml add second circle in a pane with button

i wrote a program that moves a circle left and right but i want to add another button "add ball" so that when I click on it will add a new circle if it was javafx or java i can do this but in javafxml i couldnt find it
controller:
public int step = 10;
#FXML
private Circle c;
#FXML
private Button b, b1;
#Override
public void initialize(URL url, ResourceBundle rb) {
b.setOnAction(e ->{
c.setCenterX(c.getCenterX() + (-step));
});
b1.setOnAction(e ->{
c.setCenterX(c.getCenterX() + step);
});
}
}

Can't drop a rectangle at the desired location with JavaFX

I am trying to implement a full press-drag-release gesture with JavaFX. I want to drag a rectangle from one VBox to another. On the MOUSE_DRAG_RELEASED event that happens on the target VBox, I'm trying to add the dragged rectangle as a child of the target VBox.
The problem is that when I release the mouse on the target VBox, the rectangle does not get into the expected position inside the VBox, but is always offset to the right by a fixed distance.
public class DragFromOneVBoxToAnother extends Application {
private Disk sourceDisk = new Disk();
private VBox targetVBox = new VBox();
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
// Build the UI
GridPane root = getUI();
// Add the event handlers
this.addEventHandlers();
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
private GridPane getUI() {
GridPane pane = new GridPane();
VBox sourceVBox = new VBox();
sourceDisk.setWidth(90);
sourceDisk.setHeight(20);
sourceVBox.setStyle(" -fx-border-color:red; -fx-border-width: 1; -fx-border-style: solid;");
targetVBox.setStyle(" -fx-border-color:green; -fx-border-width: 1; -fx-border-style: solid;");
sourceVBox.getChildren().add(sourceDisk);
targetVBox.getChildren().add(new Rectangle(200, 20));
pane.setHgap(200);
pane.addColumn(0, sourceVBox);
pane.addColumn(1, targetVBox);
pane.setPadding(new Insets(200, 100, 200, 100));
return pane;
}
private void addEventHandlers() {
sourceDisk.setOnMouseEntered(event -> sourceDisk.setCursor(Cursor.HAND));
sourceDisk.setOnMousePressed(event -> {
sourceDisk.setOrgSceneX(event.getSceneX());
sourceDisk.setOrgSceneY(event.getSceneY());
sourceDisk.setOrgTranslateX(sourceDisk.getTranslateX());
sourceDisk.setOrgTranslateY(sourceDisk.getTranslateY());
sourceDisk.setMouseTransparent(true);
sourceDisk.setCursor(Cursor.CLOSED_HAND);
});
sourceDisk.setOnDragDetected(event -> sourceDisk.startFullDrag());
sourceDisk.setOnMouseDragged(event -> {
double offsetX = event.getSceneX() - sourceDisk.getOrgSceneX();
double offsetY = event.getSceneY() - sourceDisk.getOrgSceneY();
double newTranslateX = sourceDisk.getOrgTranslateX() + offsetX;
double newTranslateY = sourceDisk.getOrgTranslateY() + offsetY;
sourceDisk.setTranslateX(newTranslateX);
sourceDisk.setTranslateY(newTranslateY);
});
sourceDisk.setOnMouseReleased(event -> {
sourceDisk.setMouseTransparent(false);
sourceDisk.setCursor(Cursor.DEFAULT);
});
targetVBox.setOnMouseDragReleased(event ->
targetVBox.getChildren().add(sourceDisk));
}
private class Disk extends Rectangle {
private double orgSceneX;
private double orgSceneY;
private double orgTranslateX;
private double orgTranslateY;
// below, the getters and setters for all the instance variables
// were removed for brevity
}
I have found that, even though the visual representation of the dragged rectangle seems to be offset when it's dropped, a child appears to actually be added to the target VBox (this can be seen because the border of the VBox expands after the MOUSE_DRAG_RELEASED event).
What could be the issue?
During the mouse drag gesture you modify the translateX/translateY properties of the node. This results in the dragged node being offset from the position where the new parent places it by this transformation. You need to reset those values to properly add the node to the bottom of the VBox:
targetVBox.setOnMouseDragReleased(event -> {
targetVBox.getChildren().add(sourceDisk);
// reset translate values
sourceDisk.setTranslateX(0);
sourceDisk.setTranslateY(0);
});

Mouse release event doesn't happen on Ubuntu when target Node changes scene?

This question deals with mouse behavior across operating systems; specifically, my code works on Windows and Mac OS X, but not on Ubuntu.
Ultimately what I am trying to do is make a special Pane subclass ("ConvertiblePane") that exists within a parent Pane on a main stage/scene, but is magically transferred into its own temporary stage/scene when dragged, thus becoming independent and able to be placed anywhere on the screen. When the user releases the mouse button, the ConvertiblePane should drop back onto its original parent Pane and lose the temporary stage. (In my full program, the original parent Stage resizes/repositions itself to accommodate the ConvertiblePane wherever it is dropped.)
This brings me to my issue. When I press the mouse on the ConvertiblePane, it triggers a MousePress within the main scene as expected, at which point the ConvertiblePane is moved to the temporary stage. As I drag the mouse, it triggers MouseDrag within the temporary scene and moves the temp stage. Ok, great.
When I release the mouse button, however, I experience different behavior on different operating systems. On Windows (7) and Mac OS X (10.12.6), a MouseRelease occurs in the temp scene, sending the pane back to its original parent in the main stage as expected. On Ubuntu, however, no MouseRelease seems to be generated in either the main scene or the temp scene.
Here's the relevant code as an MCV example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class ConvertibleTest extends Application {
#Override
public void start(Stage primaryStage) {
// Set up the main stage and scene with a
// Pane as root and a ConvertiblePane child:
primaryStage.initStyle(StageStyle.TRANSPARENT);
Pane root = new Pane();
ConvertiblePane conv = new ConvertiblePane();
root.getChildren().add(conv);
Scene scene = new Scene(root, 400, 400, Color.PINK);
primaryStage.setTitle("Convertible Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class ConvertiblePane extends Pane
{
private final Group TEMP_ROOT = new Group();
private final Stage TEMP_STAGE = new Stage(StageStyle.TRANSPARENT);
private final Scene TEMP_SCENE = new Scene(TEMP_ROOT);
private Pane originalParent = null;
private double deltaX = 0.0;
private double deltaY = 0.0;
private String name = null;
public void onMousePress(MouseEvent event)
{
// Save deltaX/Y for later:
Point2D delta = this.sceneToLocal(event.getX(), event.getY());
deltaX = delta.getX();
deltaY = delta.getY();
if (!isIndependent())
{
makeIndependent();
}
}
public void onMouseDrag(MouseEvent event)
{
// Keep the TEMP_STAGE relative to the original click point:
TEMP_STAGE.setX(event.getScreenX()-deltaX);
TEMP_STAGE.setY(event.getScreenY()-deltaY);
}
public void onMouseRelease(MouseEvent event)
{
if (isIndependent())
{
returnToParent();
}
}
public ConvertiblePane()
{
this.setPrefSize(100, 100);
this.setBackground(new Background(new BackgroundFill(Color.GREEN, new CornerRadii(10), Insets.EMPTY)));
this.setVisible(true);
// Attach event code and report to System.out what is happening:
this.setOnMousePressed((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Pressed as Independent");
else
System.out.println("Pressed as Child");
onMousePress(event);
});
this.setOnMouseDragged((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Dragged as Independent");
else
System.out.println("Dragged as Child");
onMouseDrag(event);
});
this.setOnMouseReleased((MouseEvent event) -> {
if (this.getScene() == TEMP_SCENE)
System.out.println("Released as Independent");
else
System.out.println("Released as Child");
onMouseRelease(event);
});
}
public boolean isIndependent()
{
// Return whether this ConvertiblePane is "independent" (exists in its own temp scene)
return this.getScene() == TEMP_SCENE;
}
public void makeIndependent()
{
// Get the point where this ConvertiblePane appears on screen:
Point2D screenPt = this.localToScreen(0, 0);
// Save the originaParent of this ConvertiblePane; we will return to it later:
originalParent = (Pane)getParent();
// Remove this ConvertiblePane from its originalParent:
originalParent.getChildren().remove(this);
// Set this ConvertiblePane as the root of the TEMP_SCENE on the TEMP_STAGE:
TEMP_SCENE.setRoot(this);
TEMP_STAGE.setScene(TEMP_SCENE);
System.out.println("Transferred to TEMP.");
this.relocate(0, 0);
// Show the TEMP_STAGE in the same location on screen where this ConvertiblePane originally was:
TEMP_STAGE.setX(screenPt.getX());
TEMP_STAGE.setY(screenPt.getY());
TEMP_STAGE.show();
}
public void returnToParent()
{
// Reset deltas:
deltaX = 0;
deltaY = 0;
// Get the location of this ConvertiblePane on screen:
Point2D screenPt = this.localToScreen(0, 0);
// Set TEMP_ROOT as the root of TEMP_SCENE; this will allow us to detach
// this ConvertiblePane from being the scene root (since root cannot == null).
TEMP_SCENE.setRoot(TEMP_ROOT);
// Hide the TEMP_STAGE:
TEMP_STAGE.hide();
// Add this ConvertiblePane back to the originalParent:
originalParent.getChildren().add(this);
System.out.println("Transferred to MAIN.");
// Relocate this ConvertiblePane within the originalParent to maintain its position on screen
Point2D parentPt = originalParent.screenToLocal(screenPt);
this.relocate(parentPt.getX(), parentPt.getY());
}
}
As you can see, there is some basic reporting in the event handling methods; and the makeIndependent() and returnToParent() methods output "Transferred to TEMP." and "Transferred to MAIN." respectively.
If I click the mouse on the ConvertiblePane, drag a few pixels, and release it, this is the output:
(on Windows or Mac OS X)
Pressed as Child
Transferred to TEMP.
Dragged as Independent
Dragged as Independent
Dragged as Independent
Released as Independent
Transferred to MAIN.
(on Ubuntu)
Pressed as Child
Transferred to TEMP.
Dragged as Independent
Dragged as Independent
Dragged as Independent
I have also tried adding event filters to the two Scenes; but the result is the same: MouseRelease occurs on Win/Mac, but not Ubuntu.
If anyone can explain this behavior, or suggest something, that would be great. Alternatively... is there any "global" (pre-scene) creation of MouseEvents that I could catch? I mean, I don't really care about the details of the mouse release; I just want an event to tell me when to add the ConvertiblePane back to the main stage.
Thanks!
After spending several weeks on this, I couldn't find a way to fire a proper MouseReleased event on Ubuntu for this situation; however, I did come up with a hack that does the job well enough. Basically, instead of being notified when MouseReleased occurs, I'm checking every 10 milliseconds to see whether the mouse button is no longer down.
Explanation: When the node is transferred to the temporary Scene, the Timeline is started to "move" the mouse pointer in place every 10 milliseconds. This triggers either a MouseDragged event (if the mouse button is still down) or a MouseMoved event (if the mouse button is up); so I can then simulate the MouseReleased event and call my procedure for adding the node back to the main stage. At that point, of course, I also stop the Timeline.
Here's the relevant code to demonstrate this; perhaps it will be of use to someone else as well.
// The robot is needed to "move" the mouse in place,
// triggering a MOUSE_MOVED event.
private static Robot robot = null;
static {
try {
robot = new Robot();
} catch (AWTException ex) {
Logger.getLogger(ConvertiblePane.class.getName()).log(Level.SEVERE, null, ex);
}
}
// clickWaiter will move the mouse in place every 10 milliseconds,
// triggering a MOUSE_MOVED event if the mouse is no longer pressed.
private final Timeline clickWaiter = new Timeline(new KeyFrame(Duration.millis(10), (ActionEvent event) -> {
// Get the mouse position from MouseInfo class.
Point mouse = MouseInfo.getPointerInfo().getLocation();
// "Move" the mouse in place to trigger a mouseMove or mouseDrag event.
robot.mouseMove(mouse.x, mouse.y);
}));
public ConvertiblePane()
{
...
// MOUSE_MOVED will be triggered by the robot when the mouse button is no longer pressed:
TEMP_SCENE.addEventFilter(MouseEvent.MOUSE_MOVED, (MouseEvent event) ->
{
if (!event.isPrimaryButtonDown())
{
System.out.println("Temp.release (sim)");
clickWaiter.stop();
// Simulate MOUSE_RELEASED event.
}
});
}
public void makeIndependent()
{
...
// Start the clickWaiter, part of the Linux hack:
clickWaiter.playFromStart();
}

JavaFX RippleAnimation over a Button

I want to create a RippleAnimation, so I made a circle and used ScaleTransition and put it in a Button using the method setGraphic(..);
Sample Code:
final Circle circle = new Circle(3);
circle.setFill(Color.color(0, 0, 0, 0.2));
ScaleTransition scaleTransition = new ScaleTransition(Duration.millis(500), circle);
scaleTransition.setToX(50f);
scaleTransition.setToY(50f);
scaleTransition.setAutoReverse(false);
scaleTransition.setCycleCount(1);
button.setGraphic(circle);
button.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
scaleTransition.play();
}
});
...
but it gets over the Button:
How i can achieve that the circle doesn't get over the button?
Simply use shape clipping:
Rectangle clip = new Rectangle();
clip.widthProperty().bind(button.widthProperty());
clip.heightPropety().bind(button.heightProperty());
circle.setClip(clip);
Hope this works, cause haven't worked with setGraphics() ever. Maybe try putting button + circle into a StackPane to achieve same results.

How to apply a blur only to a rectangular area within a pane?

Let's say I've got a StackPane which has a BackgroundImage as background and another StackPane (or another component, if neccessary) as a child. The child covers only a part of the parent StackPane.
I'd like to know how to apply a GaussianBlur just to the area the child covers, so that the BackgroundImageis blurry in this area.
The size of the child changes when the parent is resized. It would be perfect to get a solution that will resize just in time, too.
If you want to do it manually, you can use the snapshot function to create a snapshot image, blur it and apply it to the child every time the parent is resized.
However, invoking snapshot all the time will cause performance loss. I rather suggest you create 2 images, one normal and one blurred, and display a viewport of the blurred one.
Here's a more "complex" example with a circle where the viewport isn't sufficient. The clip method is used in this case:
public class Lens extends Application {
Image image = new Image( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
CirclePane circlePane;
#Override
public void start(Stage primaryStage) {
ImageView normalImageView = new ImageView( image);
ImageView blurredImageView = new ImageView( image);
blurredImageView.setEffect(new GaussianBlur( 40));
Group root = new Group();
root.getChildren().addAll( normalImageView);
Scene scene = new Scene( root, 1024, 768);
primaryStage.setScene( scene);
primaryStage.show();
// pane with clipped area
circlePane = new CirclePane( blurredImageView);
makeDraggable( circlePane);
root.getChildren().addAll( circlePane);
}
public static void main(String[] args) {
launch(args);
}
private class CirclePane extends Pane {
ImageView blurredImageView;
ImageView clippedView = new ImageView();
public CirclePane( ImageView blurredImageView) {
this.blurredImageView = blurredImageView;
// new imageview
update();
getChildren().addAll( clippedView);
}
public void update() {
// create circle
Circle circle = new Circle( 200);
circle.relocate( getLayoutX(), getLayoutY());
// clip image by circle
blurredImageView.setClip(circle);
// non-clip area should be transparent
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
// new image from clipped image
WritableImage wim = null;
wim = blurredImageView.snapshot(parameters, wim);
clippedView.setImage( wim);
}
}
// make node draggable
class DragContext {
double x;
double y;
}
public void makeDraggable( Node node) {
final DragContext dragDelta = new DragContext();
node.setOnMousePressed(mouseEvent -> {
dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
});
node.setOnMouseDragged(mouseEvent -> {
node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y);
circlePane.update();
});
}
}
Just click on the circle and drag it around.

Categories