don't recieve mouse press event on child node JavaFX - javafx

I want draw path between two imageview same as picture
.
this path start from one of these imageview by mouse press, continue by mouse press and move event on pane and must be end in another imageview by mouse press.here is the problem after first mouse press didn't recieve any mouse press event on imageviews, the event just recieves on the pane becuase of that draw line didn't stop. what is wrong in my code ?
here's my controller code :
public class DrawLine {
#FXML
ImageView imageView1 ;
#FXML
ImageView imageView2 ;
#FXML
AnchorPane pane ;
private Line currentLine ;
private String state ;
private DoubleProperty mouseX = new SimpleDoubleProperty();
private DoubleProperty mouseY = new SimpleDoubleProperty();
#FXML
private void initialize(){
state = "dfkl" ;
imageView1.setPreserveRatio( false);
imageView2.setPreserveRatio( false);
imageView1.setOnMousePressed( event -> {
imageMousePress( event);
});
imageView2.setOnMousePressed( event -> {
imageMousePress( event);
});
pane.setOnMousePressed( event -> {
paneMousePress( event) ;
});
imageView2.setPickOnBounds(false);
imageView1.setPickOnBounds(false);
pane.setOnMouseMoved( event -> {
paneMouseMove( event);
});
}
public void paneMouseMove( MouseEvent e) {
if( this.state.equals("DRAWLINE") && this.currentLine != null) {
makeLine( e);
}
}
public void paneMousePress( MouseEvent e) {
if( this.state.equals("DRAWLINE") && this.currentLine != null) {
endLine(e);
startLine(e);
}
}
private void startLine( MouseEvent e ){
currentLine = new Line();
currentLine.setStyle( "-fx-stroke: #a86a6a ; -fx-stroke-width: 5");
currentLine.setStartX( e.getSceneX());
currentLine.setStartY(e.getSceneY());
mouseX.set( e.getSceneX()) ;
mouseY.set( e.getSceneY());
currentLine.endXProperty().bind(mouseX);
currentLine.endYProperty().bind(mouseY);
pane.getChildren().add(currentLine);
}
private void endLine ( MouseEvent e){
currentLine.endXProperty().unbind();
currentLine.endYProperty().unbind();
currentLine.setEndX(e.getX());
currentLine.setEndY(e.getY());
currentLine = null;
}
private void makeLine( MouseEvent e){
mouseX.set(e.getX());
mouseY.set(e.getY());
}
private void imageMousePress( MouseEvent event){
if( currentLine == null){
startLine(event);
state = "DRAWLINE" ;
}else if( currentLine != null & state.equals("DRAWLINE")){
endLine( event);
}
}
}
help me please.

When dragging the end point of the line around, the end is positioned below the mouse cursor. This way the target of the mouse event is the Line, not the ImageView and since there is no event handler for the event for the Line that consumes it, the event is delivered to the parent of the Line which is the AnchorPane, not the ImageView.
To fix this set the mouseTransparent property of the Line to true:
private void startLine(MouseEvent e) {
currentLine = new Line();
currentLine.setMouseTransparent(true);
...
}
Also you should consume the events for the ImageViews to not trigger the event handler for the AnchorPane too:
imageView1.setOnMousePressed(event -> {
imageMousePress(event);
event.consume();
});
imageView2.setOnMousePressed(event -> {
imageMousePress(event);
event.consume();
});
Also note that x and y properties of the MouseEvent are relative to the coordinate system of the Node where the handler is added.
private void endLine(MouseEvent e) {
currentLine.endXProperty().unbind();
currentLine.endYProperty().unbind();
currentLine.setEndX(e.getX());
currentLine.setEndY(e.getY());
currentLine = null;
}
needs to be changed to
private void endLine(MouseEvent e) {
currentLine.endXProperty().unbind();
currentLine.endYProperty().unbind();
currentLine = null;
}
Furthermore if there are a limited number of states, I recommend using a enum instead since this way you get compile time checks for typos. Using strings for this purpose you could accidentally add bugs, e.g. if you accidentally use "DRAWLlNE" instead of "DRAWLINE" which can be hard to spot. Additionally enum constants can be compared using ==.
private enum States {
DRAWLINE
}

Related

JavaFX detect when scrollbar movement is complete

Swing provided java.awt.event.AdjustmentEvent.getValueIsAdjusting() which could be used to detect when a JScrollBar movement was completed. This was used in a legacy application so that it only requested data once rather than on every little scroll movement.
JFrame frame = new JFrame();
JScrollBar comp = new JScrollBar();
comp.addAdjustmentListener(e -> {
if (!e.getValueIsAdjusting()) {
System.out.println("Finished " + e.getValue());
}
});
frame.add(comp);
comp.setPreferredSize(new Dimension(25, 300));
frame.pack();
frame.setVisible(true);
I'm looking for an equivalent method in JavaFX with ScrollBar. It appears that ScrollBar only has a way of monitoring the current value. I've reviewed the code in com.sun.javafx.scene.control.behavior.ScrollBarBehavior but can see no obvious way to do this.
My workaround for now is fairly ugly. It uses a pair of event listeners to drag if the mouse is pressed, this state is used to maintain another valueProperty() which is only updated when the mouse is released.
Is there a better/easier way to do this?
#Override
public void start(Stage primaryStage) throws Exception {
ScrollBar scrollbar = new ScrollBar();
new ScrollBarFinishedAdjusting(scrollbar).valueProperty().addListener((obs, oldValue, newValue) -> {
System.out.println("Finished " + newValue);
});
primaryStage.setScene(new Scene(scrollbar));
primaryStage.show();
}
private class ScrollBarFinishedAdjusting {
private boolean mouseDown;
private DoubleProperty value = new SimpleDoubleProperty();
private ScrollBar scrollbar;
public ScrollBarFinishedAdjusting(ScrollBar scrollbar) {
this.scrollbar = scrollbar;
scrollbar.valueProperty().addListener((obs, oldValue, newValue) -> {
if (!mouseDown) {
update();
}
});
scrollbar.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
mouseDown = true;
});
scrollbar.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
mouseDown = false;
update();
});
update();
}
private void update() {
value.set(scrollbar.getValue());
}
public ReadOnlyDoubleProperty valueProperty() {
return value;
}
}

Javafx setText for label is not working if scene is changed after

maybe someone will explain. I have a method, that sets label text if login is successful.
#FXML
private void loginUser(ActionEvent event) throws IOException {
String user = username.getText();
String pass = password.getText();
if(validateFields(user, pass) && validateLogin(user, pass)) {
welcome.setText("Welcome, " + globalUser.getUserName()); //works
infoLine.setText("Redirecting to main dashboard..."); //works
}
}
And if I add additional code, which changes the scene after login, the label text is not changing:
#FXML
private void loginUser(ActionEvent event) throws IOException {
String user = username.getText();
String pass = password.getText();
if(validateFields(user, pass) && validateLogin(user, pass)) {
welcome.setText("Welcome, " + CurrentUser.getCurrentUser().getUserName());//not working
infoLine.setText("Redirecting to main dashboard..."); //not working
//Changing scene after successful login
Parent home = FXMLLoader.load(getClass().getResource(ScenePath.HOME.getPath()));
Scene homeScene = new Scene(home);
Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
appStage.setScene(homeScene);
appStage.show();
}
}
How to solve this problem?
My controller class looks like this. Nothing special. After 2 validations it set texts of labels and changes scenes.
public class LoginController {
#FXML
private TextField username;
#FXML
private PasswordField password;
#FXML
private Label infoLine;
#FXML
private Label welcome;
#FXML
private Button exitBtn;
UserDao userDao = new UserDao();
#FXML
private void initialize() {
close();
}
#FXML
private void loginUser(ActionEvent event) throws IOException {
String user = username.getText();
String pass = password.getText();
if(validateFields(user, pass) && validateLogin(user, pass)) {
welcome.setText("Welcome, " + CurrentUser.getCurrentUser().getUserName());
infoLine.setText("Redirecting to main dashboard...");
//Changing scene after successful login
Parent home = FXMLLoader.load(getClass().getResource(ScenePath.HOME.getPath()));
Scene homeScene = new Scene(home);
Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
appStage.setScene(homeScene);
appStage.show();
}
}
private boolean validateFields(String userName, String password) {
if (userName.isEmpty() || password.isEmpty()) {
infoLine.setText("Username and password can't be empty!");
return false;
}
return true;
}
private synchronized boolean validateLogin(String userName, String password) {
User user = userDao.getConnectedUser(userName, password);
if (user == null) {
infoLine.setText("User not found!");
return false;
}
CurrentUser.setCurrentUser(user);
return true;
}
private void close() {
exitBtn.setOnAction(SceneController::close);
}
}
Basically, the text is changing. The problem is as soon as the text is changed, you load the next view. This is happening really fast. The solution is to slow things down. Mainly, give the user time to see the text change before loading the new view. This can be done using PauseTransition.
After the text change, try the following code. After the text changes, this code gives a two-second delay before loading the new view.
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(
e -> {
Parent home = FXMLLoader.load(getClass().getResource(ScenePath.HOME.getPath()));
Scene homeScene = new Scene(home);
Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
appStage.setScene(homeScene);
appStage.show();
}
);
pause.play();

setFullscreen in a ComboBox

Hello I am programming a UI for a game. In this UI I want a Scene with settings. In the settings I have a ComboBox where I want setFullscreen in true or false. Actually I get the Error "Cannot make a static reference to the non-static method setFullScreen(boolean) from the type " how can I solve my Problem. I want that the BorderlessWindow setFullscreen true the println are working.
CONTROLLER CLASS;
package Menue;
public class SettingEinstellungen {
#FXML
private ComboBox<String> Combobox;
ObservableList <String> Auswahl =
FXCollections.observableArrayList("Fullscree","Windowmode","Borderless Window");
#FXML
Button exit;
#FXML
public void initialize() {
Combobox.setValue("Fullscree");
Combobox.setItems(Auswahl);
Combobox.getSelectionModel().select("Fullscreen");
Combobox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>(){
public void changed(ObservableValue<? extends String> observable, String alt, String new) {
if(new != null) {
switch(new) {
case "Fullscreen": System.out.println("Vollbildgeklickt" +alt +neu);
break;
case "Window-mode": System.out.println("Fenster\t" +alt);
break;
case "Borderless Window": Stage.setFullScreen(true);
break;
default: ;
break;
}
}
}
});}
//public void changeCombo(ActionEvent event) {
//Stage.setFullscreen(true)(comboBox.getValue(Vollbild));
//}
#FXML
public void exit_press (ActionEvent event) throws IOException {
Stage window = (Stage)((Node)event.getSource()).getScene().getWindow();
//window.setFullScreen(true);
//window.setScene(new Scene(FXMLLoader.load(new File("menue_UI_1.fxml").toURI().toURL())));
Parent root_3 = FXMLLoader.load(getClass().getResource("menue_UI_2.fxml"));
Scene scene_3 = new Scene(root_3);
window.setScene(scene_3);
window.setTitle("Hauptmenü");
window.show();
}
}
The problem is that you are not referencing the actual stage which is why you are getting that error you need to reference the actual stage that is shown you can do this by getting the window during execution like so or you can initialize it at the top when you start the program
comboBox.getSelectionModel()
.selectedItemProperty()
.addListener((obs, oldVal, newVal) -> {
if(newVal != null) {
System.out.println(newVal);
switch(newVal) {
case "Fullscreen":
System.out.println("Vollbildgeklickt" +oldVal + newVal);
break;
case "Window-mode":
System.out.println("Fenster\t" +newVal);
break;
case "Borderless Window":
Stage window = (Stage) comboBox.getScene().getWindow();
window.setFullScreen(true);
break;
default:
break;
}
}
});

JavaFX Spinner change is slow with click and hold of mouse button

The speed of Spinner update is slow when I click and hold the up/down arrow buttons. Is there a way to increase the change speed?
When I click, click, click with the mouse, the spinner values change as fast as I click. It also changes fast if I use the up/down arrows on the keyboard for each key press or if I hold down the up/down arrow keys. I want the values to change that fast when I click and hold on the arrow buttons.
Anyone know a way to do that?
The SpinnerBehavior of the SpinnerSkin triggers updates every 750 ms. Unfortunately there is no way to simply set/modify this behavour without using reflection to access private members. Therefore the only way to do this without reflection is using event filters to trigger the updates at a faster rate:
private static final PseudoClass PRESSED = PseudoClass.getPseudoClass("pressed");
#Override
public void start(Stage primaryStage) {
Spinner<Integer> spinner = new Spinner(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
class IncrementHandler implements EventHandler<MouseEvent> {
private Spinner spinner;
private boolean increment;
private long startTimestamp;
private static final long DELAY = 1000l * 1000L * 750L; // 0.75 sec
private Node button;
private final AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
if (now - startTimestamp >= DELAY) {
// trigger updates every frame once the initial delay is over
if (increment) {
spinner.increment();
} else {
spinner.decrement();
}
}
}
};
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
Spinner source = (Spinner) event.getSource();
Node node = event.getPickResult().getIntersectedNode();
Boolean increment = null;
// find which kind of button was pressed and if one was pressed
while (increment == null && node != source) {
if (node.getStyleClass().contains("increment-arrow-button")) {
increment = Boolean.TRUE;
} else if (node.getStyleClass().contains("decrement-arrow-button")) {
increment = Boolean.FALSE;
} else {
node = node.getParent();
}
}
if (increment != null) {
event.consume();
source.requestFocus();
spinner = source;
this.increment = increment;
// timestamp to calculate the delay
startTimestamp = System.nanoTime();
button = node;
// update for css styling
node.pseudoClassStateChanged(PRESSED, true);
// first value update
timer.handle(startTimestamp + DELAY);
// trigger timer for more updates later
timer.start();
}
}
}
public void stop() {
timer.stop();
button.pseudoClassStateChanged(PRESSED, false);
button = null;
spinner = null;
}
}
IncrementHandler handler = new IncrementHandler();
spinner.addEventFilter(MouseEvent.MOUSE_PRESSED, handler);
spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
if (evt.getButton() == MouseButton.PRIMARY) {
handler.stop();
}
});
Scene scene = new Scene(spinner);
primaryStage.setScene(scene);
primaryStage.show();
}
I modified the answer of fabian a little bit to decrease the speed of the spinner while holding mouse down:
private int currentFrame = 0;
private int previousFrame = 0;
#Override
public void handle(long now)
{
if (now - startTimestamp >= initialDelay)
{
// Single or holded mouse click
if (currentFrame == previousFrame || currentFrame % 10 == 0)
{
if (increment)
{
spinner.increment();
}
else
{
spinner.decrement();
}
}
}
++currentFrame;
}
And after stopping the timer we adjust previousFrame again:
public void stop()
{
previousFrame = currentFrame;
[...]
}
A small improvement to Fabian's answer. Making the following mod to the MOUSE_RELEASED addEventerFilter will stop a NullPointerException caused when clicking the textfield associated with the spinner. Cheers Fabian!
spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
Node node = evt.getPickResult().getIntersectedNode();
if (node.getStyleClass().contains("increment-arrow-button") ||
node.getStyleClass().contains("decrement-arrow-button")) {
if (evt.getButton() == MouseButton.PRIMARY) {
handler.stop();
}
}
});
An alternative to changing the update speed might in some cases be adjusting the amount by which the value increments/decrements per update.
SpinnerValueFactory.IntegerSpinnerValueFactory intFactory =
(SpinnerValueFactory.IntegerSpinnerValueFactory) spinner.getValueFactory();
intFactory.setAmountToStepBy(100);
Reference: http://news.kynosarges.org/2016/10/28/javafx-spinner-for-numbers/

Why is JavaFX WebEngine getLoadWorker looping?

I'm not very sure how to word this question but I'll try. My application runs commands against a website with a click of a button. The issue is during each loop the getLoadWorker increases by 1. In the load worker i set listeners. Here is how it works.
MenuItem executeToHere = new MenuItem("Execute to here");
executeToHere.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
listViewStepItem item = stepListView.getSelectionModel().getSelectedItem();
int selectedIndex = stepList.getSelectionModel().getSelectedIndex();
WebBrowser browser = new WebBrowser(item.getWebView(), item.getListView());
for(int i=0; i < selectedIndex; i++){
listViewStepItem item2 = stepList.getItems().get(i);
if(item2.comboBoxSelected.contains("http://")){
browser.loadURL();
} else if(item2.comboBoxSelected.contains("enter")){
browser.enterText();
} else if(item2.comboBoxSelected.contains("click")){
browser.click();
}
}
browser.setWorker();
}
});
public class WebBrowser{
public WebBrowser(WebView fxmlWebView, WebEngine webEngine){
this.view = fxmlWebView;
this.engine = webEngine;
}
public void loadUrl(String url){
webEngine.load(url);
}
public void enterText(){
System.out.println("ENTER TEXT");
}
public void click(){
System.out.println("click");
}
public void setWorker(){
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>(){
public void changed(ObservableValue ov, State oldState, State newState){
if(newState == javafx.concurrent.Worker.State.SUCCEEDED){
listener = new EventListener(){
public void handleEvent(org.w3c.dom.events.Event evt) {
eventListeners(evt);
}
};
setListenerByTagNames(listener, "a");
}
}
});
}
private void setListenerByTagNames(EventListener listener, String tagName){
Document doc = webEngine.getDocument();
NodeList elements = doc.getElementsByTagName(tagName);
for(int i=0; i < elements.getLength();i++){
((EventTarget) elements.item(i)).addEventListener("click", listener, false);
}
System.out.println("Listening on :"+tagName);
}
}
the first time i run it the output looks like this
ENTER TEXT
click
Listening on : a
second time
ENTER TEXT
click
Listening on : a
Listening on : a
third time
ENTER TEXT
click
Listening on : a
Listening on : a
Listening on : a
I don't see how the worker is increasing but it causes the page to reload/refresh somehow and therefore all the changes to the page DOM is reset.

Resources