How to listen focusing child event - java

here is my code:
#Override
void start(Stage stage) throws Exception {
def root = new VBox() {
{
children.add(new TextArea() {
{
setId("ta1")
}
})
children.add(new TextArea() {
{
setId("ta2")
}
})
}
}
root.setOnFocus(new OnFocus() {
void onFocus(Node focusedTarget) {
// handle focusedTarget
}
})
def scene = new Scene(root, 800, 600)
stage.setScene(scene)
stage.show()
}
I hope implement following code to handle focusing child events
root.setOnFocus(new OnFocus() {
void onFocus(Node focusedTarget) {
// handle focusedTarget
}
})
if i set #ta1 and #ta2's focusedProperty, if child are large, it hard to do it, so I hope directly listen the parent, how to do it?

The standard event dispatching can be used to fire a custom event on the Scene. A listener atached to the focusOwner property of the Scene can be used to trigger the event.
Example (java)
public class FocusEvent extends Event {
public static final EventType FOCUS_EVENT_TYPE = new EventType(EventType.ROOT);
public FocusEvent(Object source, EventTarget target) {
super(source, target, FOCUS_EVENT_TYPE);
}
}
#Override
public void start(Stage primaryStage) {
TextArea ta1 = new TextArea();
ta1.setId("ta1");
TextArea ta2 = new TextArea();
ta2.setId("ta2");
VBox root = new VBox(ta1, ta2);
root.addEventHandler(FocusEvent.FOCUS_EVENT_TYPE, evt -> {
System.out.println("focused "+ evt.getTarget());
});
ta1.addEventHandler(FocusEvent.FOCUS_EVENT_TYPE, evt -> {
System.out.println("You focused the first TextArea");
evt.consume();
});
Scene scene = new Scene(root);
scene.focusOwnerProperty().addListener((o, old, newValue) -> {
if (newValue != null) {
FocusEvent event = new FocusEvent(scene, newValue);
Event.fireEvent(newValue, event);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}

Related

javafx reopen minimized window via stage(?) method

SO, I have a JavaFX application with an already implemented Tray that needs 1 option to reopen the window/stage minimized by this GuiController function, which is called by the fxml:
`public void minimizeImageViewClick(MouseEvent mouseEvent) {
if (mouseEvent.getSource() == minimizeImageView) {
stage = (Stage) ((ImageView) mouseEvent.getSource()).getScene().getWindow();
stage.setIconified(true);
}
if(mouseEvent.getSource() == minimizePane){
stage = (Stage) ((Pane) mouseEvent.getSource()).getScene().getWindow();
stage.setIconified(true);
}
}`
There is also one to maximize, implemented by the same means:
public void maximizeImageViewClick(MouseEvent mouseEvent) {
if (mouseEvent.getSource() == maximizeImageView) {
if (ExamAlertStateController.getInstance().getWindowSizeState() == WindowSizeState.INIT) {
stage = (Stage) ((ImageView) mouseEvent.getSource()).getScene().getWindow();
stage.setMaximized(true);
update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.MAXIMIZED);
} else {
stage = (Stage) ((ImageView) mouseEvent.getSource()).getScene().getWindow();
stage.setMaximized(false);
update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.INIT);
}
}
}`
A piece of the Tray, with the Open (Abrir) option I need to implement and another which only change the screen and the state, as all the other after that:
# SpringBootApplication
public class Application {
//private final MenuItem menuItemSelectedFile;
//Stage stage;
private SystemTray systemTray;
public static final URL MULTI_LOGO = Application.class.getResource("source.png");
private Timer evaluatorTimer;
public static void main(String[] args) {
* code *
}
public Application() {
Log.i(this.getClass().getName(),"Text");
SystemTray.FORCE_GTK2=true;
SystemTray.DEBUG = true;
CacheUtil.clear();
this.systemTray = SystemTray.get();
if (systemTray == null) {
throw new RuntimeException("text!");
}
systemTray.setTooltip("text");
systemTray.setImage(MULTI_LOGO);
systemTray.setStatus("TEXT");
Menu mainMenu = systemTray.getMenu();
MenuItem openMenuItem = new MenuItem("Abrir", new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
}
});
openMenuItem.setTooltip("Abrir programa");
mainMenu.add(openMenuItem);
mainMenu.add(new Separator());
MenuItem loginMenuItem = new MenuItem("Login", new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
ExamAlertStateController.getInstance().setWindowState(WindowState.LOGIN);
}
});
loginMenuItem.setTooltip("Login Text");
mainMenu.add(loginMenuItem);
mainMenu.add(new Separator());
The stage creation in the View file:
public class eaView extends Application {
private static final long ONE_SECOND = 1000;
private static double xOffset = 0;
private static double yOffset = 0;
private boolean ignore;
private Timer updaterTimer;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
URL urlLoadingScreen = getClass().getResource("/fxml/source.fxml");
loader.setLocation(urlLoadingScreen);
GridPane gridLoadingScreen = loader.load();
Scene scene = new Scene(gridLoadingScreen);
stage.initStyle(StageStyle.UTILITY);
stage.setOpacity(0);
stage.setHeight(0);
stage.setWidth(0);
stage.show();
Stage mainStage = new Stage();
//stage.setResizable(false);
//mainStage.setResizable(false);
mainStage.initOwner(stage);
mainStage.initStyle(StageStyle.UNDECORATED);
mainStage.setScene(scene);
mainStage.show();
So, I wasn't able to to call the maximizeImageViewClick function to it - I can by making it static, but it calls an update() method which contains a lot of FXML objects which cannot be altered, couldn't found the proper stage way of doing it (the attempts:
stage.setScene(scene);
stage.setMaxHeight(0);
stage.setMaxWidth(0);
stage.setOpacity(0);
stage.setIconified(true);
//ExamAlertView.
System.out.println("foi");
//ExamAlertView.
//System.out.println(stage.getProperties());
stage.show();
//if (ExamAlertStateController.getInstance().getWindowSizeState() == WindowSizeState.INIT) {
//stage = (Stage) ((ImageView) e.getSource()).getScene().getWindow();
//stage.setIconified(false);
//stage.setMaximized(true);
//stage.show();
//ImageView
//ExamAlertGuiController.setMinimizeImageView(ExamAlertGuiController.maximizeImageView);
System.out.println("foi2");
//ExamAlertGuiController.update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.MAXIMIZED);
/* } else {
stage = (Stage) ((ImageView) e.getSource()).getScene().getWindow();
stage.setMaximized(false);
stage.setIconified(false);
//ExamAlertGuiController.update();
ExamAlertStateController.getInstance().setWindowSizeState(WindowSizeState.INIT);
}*/
//System.out.println(stage.isIconified());
) which was only possible by making
public static Stage stage;
public static Scene scene;
in the GuiController file.
Really out of ideas here. Already did read the javafx.stage class the best I could.
Maybe I'm trying to access the wrong stage - stage UTILITY to remove taskbar button, mainStage UNDECORATED is the real deal, yet can't reach it - but I dont know....
How do I do this?
Thanks in advance.

Auto close JavaFX application due to innactivity

I want to close my JavaFX application if the user is inactive for a period of time. I have this code in Swing and I want to do the same in JavaFX. This class redirect the user to the login panel if no event happens during the time indicated.
import javax.swing.Timer;
public class AutoClose {
private Timer timer;
public AutoClose(JFrame centralpanel) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
Object source = event.getSource();
if (source instanceof Component) {
Component comp = (Component) source;
Window win = null;
if (comp instanceof Window) {
win = (Window) comp;
} else {
win = SwingUtilities.windowForComponent(comp);
}
if (win == centralpanel) {
timer.restart();
}
}
}
}, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK);
timer = new Timer(3600000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
centralpanel.dispose();
//Redirect to the login panel.
Login login = new Login();
login.setVisible(true);
timer.stop();
JOptionPane.showMessageDialog(null,"Connection closed due to inactivity");
}
});
timer.start();
}
});
}
}
Create a PauseTransition to trigger a delayed exit and add a event filter for InputEvents to all your scenes that restart the transition:
#Override
public void start(Stage primaryStage) throws IOException {
Button button = new Button("abcde");
StackPane root = new StackPane(button);
Scene scene = new Scene(root, 400, 400);
// create transition for logout
Duration delay = Duration.seconds(10);
PauseTransition transition = new PauseTransition(delay);
transition.setOnFinished(evt -> logout());
// restart transition on user interaction
scene.addEventFilter(InputEvent.ANY, evt -> transition.playFromStart());
primaryStage.setScene(scene);
primaryStage.show();
transition.play();
}
private void logout() {
// TODO: replace with logout code
Platform.exit();
}
Note: It's important to use an event filter since events can be consumed before they reach an event handler.
I have done this code and now it works correctly as I wanted.
public class AutoClose {
private Timeline timer;
public AutoClose(VBox mainPanel) {
timer = new Timeline(new KeyFrame(Duration.seconds(3600), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Inactivity");
alert.setHeaderText("Connection closed due to inactivity");
alert.show();
try {
Stage mainWindowStage = Login.getPrimaryStage();
Parent root = FXMLLoader.load(getClass().getResource("/view/Login.fxml"));
Scene scene = new Scene(root);
mainWindowStage.setScene(scene);
mainWindowStage.show();
timer.stop();
} catch (IOException ex) {
}
}
}));
timer.setCycleCount(Timeline.INDEFINITE);
timer.play();
mainPanel.addEventFilter(MouseEvent.ANY, new EventHandler<Event>() {
#Override
public void handle(Event event) {
timer.playFromStart();
}
});
}
}

Passing parameter from a popup Scene with its own controllers to a main controller JavaFX

User clicks on a button that opens a popup second scene that allows the user to select some values, then close and pass the selection to the first scene.
First Controller
Set<String> set;
public void initialize(URL url, ResourceBundle rb){
set = new TreeSet<String>():
}
#FXML
public Set<String> addValue (MouseEvent e) throws IOException {
Stage stage = new Stage ();
root = FXMLoader.load(getClass).getResources(2ndFXML.fxml);
stage.initModality(Modality.APPLICATION_MODAL);
stage.iniOwner(clickedButton.getScene().getWindow();
stage.showAndWait():
return set;
}
2nd Controller
#FXML
public void addSelection (MouseEvent e) throws IOException {
if (event.getSource() == button){
stage = (Stage) button.getScene().getWindow();
set.addAll(listSelection)
stage.close
}
But the value never makes it back to the first controller.
Since you're using showAndWait(), all you need to do is define an accessor method for the data in the second controller:
public class SecondController {
private final Set<String> selectedData = new TreeSet<>();
public Set<String> getSelectedData() {
return selectedData ;
}
#FXML
private void addSelection (MouseEvent e) {
// it almost never makes sense to define an event handler on a button, btw
// and it rarely makes sense to test the source of the event
if (event.getSource() == button){
stage = (Stage) button.getScene().getWindow();
selectedData.addAll(listSelection)
stage.close();
}
}
}
Then retrieve it in the first controller when the window has been closed:
#FXML
public void addValue(MouseEvent e) throws IOException {
Stage stage = new Stage ();
FXMLLoader loader = new FXMLLoader(getClass().getResource(2ndFXML.fxml));
Parent root = loader.load();
// I guess you forgot this line????
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.iniOwner(clickedButton.getScene().getWindow();
stage.showAndWait();
SecondController secondController = loader.getController();
Set<String> selectedData = secondController.getSelectedData();
// do whatever you need to do with the data...
}

JavaFX: Disable all components while a process is running and show progress indicator

I have a method that read values from the the database and returns a Map<Integer,String>. This method takes some time to return the map.
Till the time values are getting read I want a progress indicator(only loading ring like indicator will be enough,no need for progress bar) to be displayed on screen and all other components should be disabled till the time progress bar is shown.
public void scanDevice() {
ObservableList<TextField> list = FXCollections.observableArrayList(vehicleId, vehicleName, deviceType,
offboardBroker1, offboardBroker2, postfixQueue, pKIServer);
editedValuesMap.clear();
// devicePlugged = true;
if (cbChooseProject.getSelectionModel().getSelectedItem() != null) {
try {
devicePlugged = dsAdapter.getAdapter();
if (devicePlugged) {
if (bScanDevice.isFocused()) {
readMap = new HashMap<Integer, String>();
//Process Start
readMap = dsAdapter.initScan();
//Process End
if (!readMap.isEmpty() && readMap != null) {
isWritten = true;
isDeviceSideEnabled();
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
}
}
}
You could disabled all nodes with a method like the following but if you are also wanting to wait while something is happening an overlay using StackPanes may be the preferred choice.
public void setNodesDiabled(boolean disable, Node... nodes) {
for(Node node : nodes) {
node.setDisable(disable);
}
}
With an arbitrary node count, you can disable and re-enable as many nodes that are relevant to the process. It also helps to clean up as you won't have several node.setDisable(true); node2.setDisable(true); and so on.
Here in this example you won't need setNodesDisabled() because the StackPane overlay prevents clicking anything other than what's inside it. The background color is gray with 70% alpha so that you can tell it's an overlay.
public class ProgressExample extends Application {
public StackPane layout, main, progress;
public StackPane createProgressPane() {
ProgressIndicator indicator = new ProgressIndicator();
indicator.setMaxHeight(50);
indicator.setMaxWidth(50);
StackPane pane = new StackPane();
pane.setAlignment(Pos.CENTER);
pane.setStyle("-fx-background-color: rgba(160,160,160,0.7)");
pane.getChildren().add(indicator);
Task<Void> task = new Task<Void>(){
protected Void call() throws Exception {
// Your process here.
// Any changes to UI components must be inside Platform.runLater() or else it will hang.
Thread.sleep(2000);
Platform.runLater(() -> {
layout.getChildren().remove(pane);
});
return null;
}
};
new Thread(task).start();
return pane;
}
public StackPane createMainPane() {
Label label = new Label("Hello World!");
label.setFont(Font.font("Tahoma", FontWeight.SEMI_BOLD, 16));
Button start = new Button("Start Process");
start.setOnAction(action -> {
progress = createProgressPane();
layout.getChildren().add(progress);
});
VBox vbox = new VBox(10);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(label, start);
vbox.setPadding(new Insets(10,10,10,10));
StackPane pane = new StackPane();
pane.getChildren().add(vbox);
return pane;
}
public void start(Stage stage) throws Exception {
main = createMainPane();
layout = new StackPane();
layout.getChildren().add(main);
Scene scene = new Scene(layout, 900, 550);
stage.setScene(scene);
stage.setTitle("Progress Example");
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I believe the problem is that you are trying to change the values of TextFields inside the Task which is not the FX application thread which is why you are getting Not on FX application thread. To fix this you need to put your lines that modify nodes inside a Platform.runLater() like the following to your if statement.
if (readMap != null && !readMap.isEmpty()) { // Swap the order, can't check empty if it's null.
isWritten = true;
isDeviceSideEnabled();
Platform.runLater(() -> {
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
});
}
Here is an SSCCE:
It uses a Service that can be started more than once. It is not completebut something to start with.
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Service<Void> serv = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
int maxWork = 10;
for (int i = 0; i < maxWork; i++) {
Thread.sleep(1000);
updateProgress(i + 1, maxWork);
}
return null;
}
#Override
protected void succeeded() {
super.succeeded();
updateProgress(1, 1);
}
#Override
protected void cancelled() {
super.cancelled();
updateProgress(1, 1);
}
#Override
protected void failed() {
super.failed();
updateProgress(1, 1);
}
};
}
};
ProgressIndicator pi = new ProgressIndicator();
pi.progressProperty().bind(serv.progressProperty());
Button bStart = new Button("Start");
bStart.setOnAction(e -> {
serv.reset();
serv.start();
});
root.setCenter(bStart);
root.setBottom(pi);
primaryStage.setScene(scene);
primaryStage.show();
pi.getScene().getRoot().disableProperty().bind(serv.runningProperty());
}
public static void main(String[] args) {
launch(args);
}
}
In CSS I added:
.progress-indicator:disabled {
-fx-opacity: 1;
}

JavaFX fireEvent StackOverflowError

I'm currently working on a little game with JavaFX, and i had some problems to catch keyEvents.
Now i can catch them but the program throw a java.lang.StackOverflowError and it didn't do what I expected when a key is pressed.
Here is the main class:
public class WarbladeFX extends Application {
Game root;
public void start(Stage primaryStage) {
root = new Game();
Scene scene = new Scene(root, 800, 600);
scene.setFill(new Color(0,0,0,1));
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
Event.fireEvent(root, event);
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
Event.fireEvent(root, event);
}
});
primaryStage.setTitle("WarbladeFX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And the Game class:
public class Game extends Group {
Entity ship;
long delta;
HashSet<String> input = new HashSet<>();
public Game() {
File f = new File("src/ressources/ship.gif");
ship = new Entity(new Image("file:///" + f.getAbsolutePath().replace("\\", "/")) ,300, 300);
getChildren().add(ship);
setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
//System.out.println(".handle()");
String code = event.getCode().toString();
if(!input.contains(code))
input.add(code);
}
});
setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String code = event.getCode().toString();
input.remove(code);
}
});
new AnimationTimer(){
#Override
public void handle(long now) {
if(input.contains("LEFT"))
ship.setVelX(-1);
if(input.contains("RIGHT"))
ship.setVelX(1);
ship.move(now);
getChildren().clear();
getChildren().add(ship);
}
}.start();
}
}
Some help would be very great.
Events fired on nodes in the scene graph will "bubble up" to the parent, and then the parent of the parent, and eventually all the way up to the scene. So the event handlers that you defined on the scene "refire" the event to the root, which will then bubble up to the scene and get handled again, being refired again to the root, and so on...
If you want to catch the events anywhere in the scene, and then handle them in the Game class, define some methods in Game to process the events and invoke those methods. Don't "refire" the events.
For example:
public class Game extends Group {
Entity ship;
long delta;
HashSet<String> input = new HashSet<>();
public Game() {
File f = new File("src/ressources/ship.gif");
ship = new Entity(new Image("file:///" + f.getAbsolutePath().replace("\\", "/")) ,300, 300);
getChildren().add(ship);
new AnimationTimer(){
#Override
public void handle(long now) {
if(input.contains("LEFT"))
ship.setVelX(-1);
if(input.contains("RIGHT"))
ship.setVelX(1);
ship.move(now);
getChildren().clear();
getChildren().add(ship);
}
}.start();
}
public void keyDown(String key) {
if(!input.contains(key))
input.add(key);
}
public void keyUp(String key) {
input.remove(code);
}
}
and then you can do
public class WarbladeFX extends Application {
Game root;
public void start(Stage primaryStage) {
root = new Game();
Scene scene = new Scene(root, 800, 600);
scene.setFill(new Color(0,0,0,1));
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
game.keyDown(event.getCode().toString());
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
game.keyUp(event.getCode().toString());
}
});
primaryStage.setTitle("WarbladeFX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
you are generating an infinite loop when you fire the event again in the event-handle method. Try to handle the event in this method, i.e. react to the users input.

Categories