How to Achieve Javafx Mouse Event "Push and Hold"

how to achieve javafx mouse event push and hold ?

Just use a PauseTransition as a timer for the "hold". Start it if the mouse is pressed, stop it if it's released or dragged.

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class MousePressAndHoldTest extends Application {

@Override
public void start(Stage primaryStage) {
Pane root = new Pane();

addPressAndHoldHandler(root, Duration.seconds(1),
event -> System.out.println("Press and hold"));

primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}

private void addPressAndHoldHandler(Node node, Duration holdTime,
EventHandler<MouseEvent> handler) {

class Wrapper<T> { T content ; }
Wrapper<MouseEvent> eventWrapper = new Wrapper<>();

PauseTransition holdTimer = new PauseTransition(holdTime);
holdTimer.setOnFinished(event -> handler.handle(eventWrapper.content));

node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
eventWrapper.content = event ;
holdTimer.playFromStart();
});
node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> holdTimer.stop());
node.addEventHandler(MouseEvent.DRAG_DETECTED, event -> holdTimer.stop());
}

public static void main(String[] args) {
launch(args);
}
}

mouse event that keeps running when pressing mouse key

As pointed out in the comments, this is the default behavior for a scroll bar. However, if you want to implement something like this yourself, for some reason, one option is to use an AnimationTimer in conjunction with mouse listeners:

AnimationTimer scrollDownTimer = new AnimationTimer() {

long lastMoveTime = -1 ;

@Override
public void handle(long now) {
if (lastMoveTime > 0) {
long elapsedNanos = now-lastMoveTime ;
// scroll down amount based on time elapsed...
}
lastMoveTime = now ;
}

@Override
public void start() {
lastMoveTime = -1 ;
super.start();
}
};

And now just

scrollDownButton.setOnMousePressed(e -> scrollDownTimer.start());
scrollDownButton.setOnMouseReleased(e -> scrollDownTimer.stop());

Here's a complete example:

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class ScrollingExample extends Application {

private Rectangle rect ;
private Pane rectPane ;

@Override
public void start(Stage primaryStage) {
rect = new Rectangle(0, 200, 40, 40);
rect.setFill(Color.CORAL);

Label left = new Label("Left");
Label right = new Label("Right");

ScrollTimer scrollTimer = new ScrollTimer();

left.setOnMousePressed(e -> {
scrollTimer.setDirection(Direction.LEFT);
scrollTimer.start();
});

left.setOnMouseReleased(e -> scrollTimer.stop());

right.setOnMousePressed(e -> {
scrollTimer.setDirection(Direction.RIGHT);
scrollTimer.start();
});

right.setOnMouseReleased(e -> scrollTimer.stop());

AnchorPane buttons = new AnchorPane(left, right);
AnchorPane.setLeftAnchor(left, 5.0);
AnchorPane.setRightAnchor(right, 5.0);

rectPane = new Pane(rect);

BorderPane root = new BorderPane(rectPane);
root.setTop(buttons);

Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
}

public enum Direction {
LEFT(-1), RIGHT(1) ;
private int multiplier ;

Direction(int multiplier) {
this.multiplier = multiplier ;
}

public double multiply(double value) {
return value * multiplier ;
}
}

public class ScrollTimer extends AnimationTimer {
private double speed = 250 ; // pixels/second

private Direction direction = Direction.LEFT ;

private long lastUpdate ;

@Override
public void start() {
lastUpdate = -1 ;
super.start();
}

@Override
public void handle(long now) {

if (lastUpdate < 0) {
lastUpdate = now ;
return ;
}

long elapsedNanos = now - lastUpdate ;

double elapsedSeconds = elapsedNanos / 1_000_000_000.0 ;
double deltaX = elapsedSeconds * direction.multiply(speed);

rect.setX(clamp(rect.getX() + deltaX, 0, rectPane.getWidth() - rect.getWidth()));

lastUpdate = now ;
}

private double clamp(double value, double min, double max) {
if (value < min) return min ;
if (value > max) return max ;
return value ;
}

public void setDirection(Direction direction) {
this.direction = direction ;
}

}

public static void main(String[] args) {
launch(args);
}
}

Handle mouse event anywhere with JavaFX

You can add an event filter to the scene with addEventFilter(). This will be called before the event is consumed by any child controls. Here's what the code for the event filter looks like.

scene.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent mouseEvent) {
System.out.println("mouse click detected! " + mouseEvent.getSource());
}
});

generating a MouseEvent in JavaFX

You can generate a MouseEvent using the deprecated MouseEvent.impl_mouseEvent API. I did this previously in this forum thread for JavaFX 2.0. Note that the API is deprecated for a reason - it is private API used in the implementation of JavaFX and the API is not guaranteed to maintain the same signature or even exist in future versions (which can be evidenced because the original code I posted in the forum thread no longer compiles.

The correct solution to generating such an event is to have a public API so support this. There has already been a request filed to supply this functionality RT-9383 "Add proper constructors & factory methods to event classes, remove impl". This jira is scheduled to be completed next year for JavaFX 3.0.

In the meantime, usage of the Robot class as Sergey suggests is probably your best method.


Update: Java 8 added public constructors for javafx.event.MouseEvent and the (as indicated in Jay Thakkar's answer), you can fire such an event using Event.fireEvent (you can also fire events on Windows).

JavaFX Events for Mouse Interactions are not triggered if Key is pressed

That is odd you can implement it yourself like so

public class Main extends Application {

@Override
public void start(Stage primaryStage) throws Exception{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);

CheckBox checkBox = new CheckBox();
checkBox.setOnMouseClicked(event -> {
if(event.isControlDown()) {
System.out.print("Control down click ");
checkBox.setSelected(!checkBox.isSelected());
}
else
System.out.print("Normal click ");

System.out.println("Checked Status:"+checkBox.isSelected());
});

Button button = new Button("Button");
button.setOnMouseClicked(event -> {
if(event.isControlDown())
System.out.println("Control down click");
else
System.out.println("Normal click");
});

vBox.getChildren().addAll(new Label("Click the box"),checkBox,button);

primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}

public static void main(String[] args) { launch(args); }

}

The output for CheckBox:

Normal click Checked Status:true
Normal click Checked Status:false
Control down click Checked Status:true
Control down click Checked Status:false

The output for Button:

Normal click
Control down click

JavaFX mouse event

No, you can only fix things which are broken but this is by design. How shall the system know that you have clicked on something before you release the mouse button? The first mouse press could also be the start of a drag. Look at the documentation: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/MouseEvent.html#MOUSE_CLICKED Depending on your use-case you might use the mouse pressed event instead. https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/MouseEvent.html#MOUSE_PRESSED

MouseDragged detection for multiple nodes while holding the button JavaFX

From the documentation of javafx.scene.input.MouseEvent:

Dragging gestures


There are three types of dragging gestures. They are all initiated by a mouse press event and terminated as a result of a mouse released event, the source node decides which gesture will take place.

The simple press-drag-release gesture is default. It's best used to allow changing size of a shape, dragging it around and so on. Whole press-drag-release gesture is delivered to one node. When mouse button is pressed, the top-most node is picked and all subsequent mouse events are delivered to the same node until the button is released. If a mouse clicked event is generated from these events, it is still delivered to the same node.

During simple press-drag-release gesture, the other nodes are not involved and don't get any events. If these nodes need to be involved in the gesture, full press-drag-release gesture has to be activated. This gesture is best used for connecting nodes by "wires", dragging nodes to other nodes etc. This gesture type is more closely described at MouseDragEvent which contains the events delivered to the gesture targets.

The third gesture type is platform-supported drag-and-drop gesture. It serves best to transfer data and works also between (not necessarily FX) applications. This gesture type is more closely described at DragEvent.

In a short summary, simple press-drag-release gesture is activated automatically when a mouse button is pressed and delivers all MouseEvents to the gesture source. When you start dragging, eventually the DRAG_DETECTED event arrives. In its handler you can either start full press-drag-release gesture by calling startFullDrag method on a node or scene - the MouseDragEvents start to be delivered to gesture targets, or you can start drag and drop gesture by calling startDragAndDrop method on a node or scene - the system switches into the drag and drop mode and DragEvents start to be delivered instead of MouseEvents. If you don't call any of those methods, the simple press-drag-release gesture continues.

[...]

If I understand your question correctly, you want to be able to drag the mouse over multiple nodes and have them react, all in one gesture. You'll want to use a full
press-drag-release
gesture to accomplish this. As documented, you have to listen for a DRAG_DETECTED event and call Node#startFullDrag() or Scene#startFullDrag() to activate the full press-drag-release gesture. Then each "square" in your UI needs to listen for MOUSE_DRAG_ENTERED events. Notice that the event type is MOUSE_DRAG_ENTERED and not MOUSE_ENTERED.

Here's an example:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class App extends Application {

@Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
root.setPadding(new Insets(2));
root.setVgap(2);
root.setHgap(2);

// start full press-drag-release gesture
root.setOnDragDetected(
event -> {
if (event.getButton() == MouseButton.PRIMARY) {
event.consume();
root.startFullDrag();
}
});

for (int i = 0; i < 12; i++) {
for (int j = 0; j < 12; j++) {
Rectangle rect = new Rectangle(50, 50, Color.WHITE);
rect.setStroke(Color.BLACK);
root.add(rect, i, j);

// detect MOUSE_DRAG_ENTERED events
rect.setOnMouseDragEntered(
event -> {
event.consume();
rect.setFill(Color.BLACK);
});
}
}

primaryStage.setTitle("MouseDragEvent Example");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}

The above listens for DRAG_DETECTED events by setting the Node#onDragDetected property on the root GridPane. Note that if you start dragging on one of the Rectangles then the event will bubble up to the root and be handled by the aforementioned handler. Also, since you explicitly mention the left mouse button I added a check for if the mouse button is the primary or not.

Then each Rectangle listens for MOUSE_DRAG_ENTERED events by having their Node#onMouseDragEntered property set. These events will only be delivered if a full press-drag-release gesture is in effect.

Create a method for a long click or pressed Button in javafx

what about just use the start and end times:

button.addEventFilter(MouseEvent.ANY, new EventHandler<MouseEvent>() {

long startTime;

@Override
public void handle(MouseEvent event) {
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED)) {
startTime = System.currentTimeMillis();
} else if (event.getEventType().equals(MouseEvent.MOUSE_RELEASED)) {
if (System.currentTimeMillis() - startTime > 2 * 1000) {
System.out.println("Pressed for at least 2 seconds (" + (System.currentTimeMillis() - startTime) + " milliseconds)");
} else
System.out.println("Pressed for " + (System.currentTimeMillis() - startTime) + " milliseconds");
}
}
});

Can't get mouse event from any other javafx 8 node after getting MOUSE_PRESSED event from one node

The documentation for MouseEvent details three different modes for handling mouse drag. In the default mode ("simple press-drag-release gesture"), as you've observed, mouse events are delivered only to the node on which the gesture originated.

In "full press-drag-release gesture" mode, MouseDragEvents are delivered to other nodes during the drag. This is the mode you need, and you activate it by calling startFullDrag on the originating node.

(The third mode is "drag-and-drop" gesture, which is for transferring data between nodes and is typically supported by the underlying platform, so you can drag and drop between your JavaFX application and other applications, as well as within the application.)

Try the following code for your event handlers:

        label.setOnDragDetected(mouseEvent -> label.startFullDrag());
label.setOnMouseDragEntered(mouseEvent -> System.out.println("entering " + label.getText()));
label.setOnMouseDragReleased(mouseEvent -> System.out.println("release mouse button on " + label.getText()));


Related Topics



Leave a reply



Submit