JavaFX - StackPane with Pane as EventDispatcher - javafx

In the event dispatcher pane occurs an mouse pressed event.
The pane one should show the context menu of it's combobox when a event occurs.
That works fine if the event is only dipatched to pane one.
When the event is dipatched to pane one and pane two the context menu of pane one doesn't show up.
I suppose it has something to do with the event tail and event consuming.
Until now i doesn't had a look at the EventDispatcher Class of the JDK itself.
Here is what i got so far:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
*
* #author Robert
*/
public class EventDispatcherExample extends Application {
private Group root;
private StackPane cStackPane;
private Pane cPaneEventDispatcher;
private Pane cPaneOne;
private ComboBox cComboBox;
private Pane cPaneTwo;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Group();
cStackPane = new StackPane();
cStackPane.setPrefHeight(200.0);
cStackPane.setPrefWidth(200.0);
cPaneEventDispatcher = new Pane();
cPaneEventDispatcher.setPrefHeight(200.0);
cPaneEventDispatcher.setPrefWidth(200.0);
cPaneEventDispatcher.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane ED.");
cPaneOne.fireEvent(event);
cPaneTwo.fireEvent(event);
}
});
cPaneOne = new Pane();
cPaneOne.setPrefHeight(200.0);
cPaneOne.setPrefWidth(200.0);
cPaneOne.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane One.");
cComboBox.show();
}
});
ObservableList<String> observableList = FXCollections.observableArrayList();
observableList.add("1");
observableList.add("2");
observableList.add("3");
cComboBox = new ComboBox();
cComboBox.setLayoutX(50.0);
cComboBox.setLayoutY(50.0);
cComboBox.setPrefHeight(30.0);
cComboBox.setPrefWidth(100.0);
cComboBox.setItems(observableList);
cPaneTwo = new Pane();
cPaneTwo.setPrefHeight(200.0);
cPaneTwo.setPrefWidth(200.0);
cPaneTwo.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane Two.");
//Something will happen because of selected item in Combo Box of pane one...
}
});
cPaneOne.getChildren().add(cComboBox);
// add the nodes in reverse order
cStackPane.getChildren().add(cPaneTwo);
cStackPane.getChildren().add(cPaneOne);
cStackPane.getChildren().add(cPaneEventDispatcher);
root.getChildren().add(cStackPane);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Any ideas how to handle this?

After some ideas I got a solution that at least works:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author Robert
*/
public class EventDispatcherExample extends Application {
private Group root;
private StackPane cStackPane;
private Pane cPaneEventDispatcher;
private Pane cPaneOne;
private ComboBox cComboBox;
private boolean cComboBoxClicked = false;
private Pane cPaneTwo;
public static void main(String[] args) {
launch(args);
}
public boolean isComboBoxClicked() {
if (cComboBoxClicked == true) {
cComboBox.show();
} else {
cComboBox.hide();
}
return cComboBoxClicked;
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Group();
cStackPane = new StackPane();
cStackPane.setPrefHeight(200.0);
cStackPane.setPrefWidth(200.0);
cPaneEventDispatcher = new Pane();
cPaneEventDispatcher.setPrefHeight(200.0);
cPaneEventDispatcher.setPrefWidth(200.0);
cPaneEventDispatcher.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane ED.");
cPaneOne.fireEvent(event);
cPaneTwo.fireEvent(event);
}
});
cPaneOne = new Pane();
cPaneOne.setPrefHeight(200.0);
cPaneOne.setPrefWidth(200.0);
cPaneOne.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane One.");
Rectangle rect = new Rectangle(cComboBox.getLayoutX(), cComboBox.getLayoutY(),
cComboBox.getPrefWidth(), cComboBox.getPrefHeight());
cComboBoxClicked = rect.contains(event.getX(), event.getY());
}
});
ObservableList<String> observableList = FXCollections.observableArrayList();
observableList.add("1");
observableList.add("2");
observableList.add("3");
cComboBox = new ComboBox();
cComboBox.setLayoutX(50.0);
cComboBox.setLayoutY(50.0);
cComboBox.setPrefHeight(30.0);
cComboBox.setPrefWidth(100.0);
cComboBox.setItems(observableList);
cComboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
// if cComboBoxSelectedIndex == 1 do Color on pane two
// if cComboBoxSelectedIndex == 2 do Size on pane two
// if cComboBoxSelectedIndex == 3 do ...
//System.out.println("newValue " + newValue);
}
});
cPaneTwo = new Pane();
cPaneTwo.setPrefHeight(200.0);
cPaneTwo.setPrefWidth(200.0);
cPaneTwo.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//System.out.println("Mouse pressed in Pane Two.");
boolean cComboBoxClicked = isComboBoxClicked();
if (cComboBoxClicked){
//System.out.println("Skip code internally managed by pane two.");
return;
}
// Internal code of pane two
//...
}
});
cPaneOne.getChildren().add(cComboBox);
// add the nodes in reverse order
cStackPane.getChildren().add(cPaneTwo);
cStackPane.getChildren().add(cPaneOne);
cStackPane.getChildren().add(cPaneEventDispatcher);
root.getChildren().add(cStackPane);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Perhaps a better approach comes up during the time of someone else.
Am looking forward...

Related

How to force showing the popup part of a ComboBox in JavaFX

I want to be able to always show the popup part of a combobox regardless of the fact it has been clicked or not, or even without the combo being focused. I tried to use the show() method of the combo, but in my case the popup part never shows.
My code is:
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class ComBoBoxTest {
public static final void main(String[] args) {
ComBoBoxTest test = new ComBoBoxTest();
test.setup();
}
private void setup() {
new JFXPanel();
Platform.runLater(new Runnable() {
#Override
public void run() {
createUI();
}
});
}
private void createUI() {
Stage stage = new Stage();
Pane pane = new Pane();
ComboBox<String> combo = new ComboBox();
ObservableList<String> values = combo.getItems();
values.add("ONE");
values.add("TWO");
values.add("THREE");
combo.setVisibleRowCount(3);
combo.show();
pane.getChildren().add(combo);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show(); // the important part
}
}
In that case, I thought that my show() method would force to open the popup, but the result is that the popup is never shown
Per the excellent comment of kleopatra, the solution is to call show() on the Combo after the Stage is shown. This example works:
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class ComBoBoxTest {
public static final void main(String[] args) {
ComBoBoxTest test = new ComBoBoxTest();
test.setup();
}
private void setup() {
new JFXPanel();
Platform.runLater(new Runnable() {
#Override
public void run() {
createUI();
}
});
}
private void createUI() {
Stage stage = new Stage();
Pane pane = new Pane();
ComboBox<String> combo = new ComboBox();
ObservableList<String> values = combo.getItems();
values.add("ONE");
values.add("TWO");
values.add("THREE");
combo.setVisibleRowCount(3);
pane.getChildren().add(combo);
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.show();
combo.show(); // call show() on the Combo after the stage is shown
}
}

Why does JavaFX only receive mouse events once?

I'm working on an application where I can drag and drop an ImageView anywhere on to the scene. When I run the application it works fine the first time I drag the ImageView, but is unresponsive after I release it.
Here is my FXML Controller:
#FXML
private ImageView card;
#FXML
private void handleCardMousePressed(MouseEvent event) {
System.out.println("Drag Entered");
DropShadow dropShadow=new DropShadow();
dropShadow.setColor(Color.rgb(18,139,237));
dropShadow.setSpread(.48);
card.setEffect(dropShadow);
card.setMouseTransparent(true);
event.consume();
}
#FXML
private void handleCardMouseDragged(MouseEvent event){
System.out.println("In Drag");
card.setLayoutX(event.getSceneX());
card.setLayoutY(event.getSceneY());
event.consume();
}
#FXML
private void handleCardMouseReleased(MouseEvent event){
System.out.println("Exit Drag");
card.setEffect(null);
event.consume();
}
Video of What is Happening
Don't set mouseTransparent to true in the handleMouseCardPressed method.
If you need to do this for some other reason (I can't see why you would), then in handleCardMouseReleased(...) you need to set mouseTransparent back to false:
card.setMouseTransparent(false);
Complete example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DragImageViewExample extends Application {
private ImageView card;
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(30, 30, Color.CORAL);
card = new ImageView(rect.snapshot(null, null));
card.setOnMousePressed(this::handleCardMousePressed);
card.setOnMouseDragged(this::handleCardMouseDragged);
card.setOnMouseReleased(this::handleCardMouseReleased);
Pane pane = new Pane(card);
Scene scene = new Scene(pane, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private void handleCardMousePressed(MouseEvent event) {
System.out.println("Drag Entered");
DropShadow dropShadow=new DropShadow();
dropShadow.setColor(Color.rgb(18,139,237));
dropShadow.setSpread(.48);
card.setEffect(dropShadow);
event.consume();
}
private void handleCardMouseDragged(MouseEvent event){
System.out.println("In Drag");
card.setLayoutX(event.getSceneX());
card.setLayoutY(event.getSceneY());
event.consume();
}
private void handleCardMouseReleased(MouseEvent event){
System.out.println("Exit Drag");
card.setEffect(null);
event.consume();
}
public static void main(String[] args) {
launch(args);
}
}

Jfxtra window bring it forward / internal window

I have an internal jfxtra window. On clicking a button, I want to bring it forward.
The code that I have tried :
window w = new window("mdi win");
private Stage primaryStage;
private BorderPane rootLayout;
...
public void win() {
Parent bla = FXMLLoader.load(getClass().getResource("bla.fxml"));
w.getContentPane().getChildren().add(bla);
rootLayout.getChildren().add(w);
}
private void wfront(ActionEvent event) throws Exception {
w.isMoveToFront(); // is not?
}
How to make it possible?
So you made me curious and I went through the JFXtras docs. I came to know that Window in Jfxtras extends Control. So there is a method called toFront which can be fired on it. To show this I have created a sample for you.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import jfxtras.scene.control.window.Window;
public class NewWindow extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane stackPane = new StackPane();
Button button = new Button("Click Me to show Window !");
Window window = new Window("Cick Me to bring me to front");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
window.toFront();
window.setTitle("I am on the Front");
}
});
window.setPrefSize(200, 200);
stackPane.getChildren().addAll(window, button);
Scene scene = new Scene(stackPane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Let me know, if you are looking for something else !

Back button that will open main Stage in Javafx 2

I have 2 screens in application. First screen(MainController class) which opens with running application from Eclipse.
And second screen(SecondController class) which opens on button located in first screen.
How can I make some kind of 'Back' button in second screen which will show back first screen?
I'm creating the visual part of the application in JavaFX Scene Builder if it matters.
Here is a small example, containing to screens, to show how you can achieve what you are looking for
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class TwoScreensWithInterchange extends Application {
#Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(loadScreenOne(), 200, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public VBox loadScreenOne()
{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
final Button button = new Button("Switch Screen");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
button.getScene().setRoot(loadScreenTwo());
}
});
Text text = new Text("Screen One");
vBox.getChildren().addAll(text, button);
vBox.setStyle("-fx-background-color: #8fbc8f;");
return vBox;
}
public VBox loadScreenTwo()
{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
final Button button = new Button("Back");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
button.getScene().setRoot(loadScreenOne());
}
});
Text text = new Text("Screen Two");
vBox.getChildren().addAll(text, button);
return vBox;
}
}

JavaFX: Window closing event prevents button action

I did implement a popup window in a new stage and I'm now trying to close it, no matter where I click (excluding the popup itself). This works just fine. Although the popup windows disappears when I click on another element (e.g. a button) on the background, I'd still like to get the event for the button. Any suggestions on how to achieve this? I put together a short example of the situation.
package application;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
// the popup
Pane p = new Pane();
p.setPrefSize(100, 100);
p.setStyle("-fx-background-color: #660066");
final Stage popUp = new Stage();
Scene popUpScene = new Scene(p);
popUp.setScene(popUpScene);
Button btnShow = new Button("Show popUp");
root.setCenter(btnShow);
btnShow.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
ChangeListener stageFocusListener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean newPropertyValue) {
if (!newPropertyValue) {
popUp.hide();
}
}
};
popUp.focusedProperty().addListener(stageFocusListener);
popUp.show();
}
});
Button btnTest = new Button("test");
root.setRight(btnTest);
btnTest.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event) {
System.out.println("Button test clicked");
}
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
You can put the hiding event to the queue without disturbing the event handling procedure of the button
if (!newPropertyValue) {
Platform.runLater(new Runnable() {
#Override
public void run() {
popUp.hide();
}
});
}

Resources