Well, I'm using JavaFX FXML, there's less info than just JavaFX, so I can use MVC, but I'm having trouble with adding a Confirmation Dialog so if i press alt + f4 or exit button in the window, a little confirmation dialog will show.
I found this, putting a event on setOnCloseOperation, does the job.
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("close.fxml"));
Scene scene = new Scene(root);
stage.setOnCloseRequest(e -> {
e.consume(); // stop the event to do something before quitting
closeRequest(stage); // method used to show a confirmation dialog
});
stage.setScene(scene);
stage.show();
}
private void closeRequest(Stage stage){
String msg =
"Sure to quit?";
Alert alerta = new Alert(Alert.AlertType.CONFIRMATION);
alerta.initStyle(StageStyle.DECORATED);
alerta.initModality(Modality.APPLICATION_MODAL);
alerta.initOwner(stage);
alerta.getDialogPane().setContentText(msg);
alerta.getDialogPane().setHeaderText(null);
alerta.showAndWait()
.filter(response -> response == ButtonType.OK)
.ifPresent(response -> { stage.close(); }); // then we need to call the close method for a stage, if the response is ok.
}
public static void main(String[] args) {
launch(args);
}
}
So in your main class, the one that loads the fxml resource, you need to use the method setOnCloseOperation.
Fisrt you need to consume the event so you stop the program to be finished.
Then we call a method to show a confirmation box, then we can call .close method to close the stage.
Related
When you right click on a TextField there are Undo, Redo, Cut, Copy, Paste, Delete, and Select All options.
I want to add a "Register" MenuItem to that list from my controller class, but do not know how.
Here is what I got so far:
This overwrites the existing menu items:
ContextMenu contextMenu = new ContextMenu();
MenuItem register = new MenuItem("Register");
contextMenu.getItems().add(register);
charName.setContextMenu(contextMenu);
Both of these return null:
charName.getContextMenu()
charName.contextMenuProperty().getValue()
You can replace the in-built TextField ContextMenu by setting your own (as below):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class GiveMeContext extends Application {
#Override
public void start(final Stage stage) throws Exception {
ContextMenu contextMenu = new ContextMenu();
MenuItem register = new MenuItem("Register");
contextMenu.getItems().add(register);
TextField textField = new TextField();
textField.setContextMenu(contextMenu);
stage.setScene(new Scene(textField));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
Adding to the in-built ContextMenu is a bit tricker and requires overriding non-public API.
You cannot get the in-built ContextMenu using the public textField.getContextMenu property as it is not returned (that method only returns a menu that has been set by the application code, not the internal JavaFX control skin implementation).
Be aware that the following code will almost certainly break in Java 9 as it uses deprecated com.sun APIs which will likely no longer be available. For further details on this, refer to JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization
import com.sun.javafx.scene.control.skin.TextFieldSkin;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class GiveMeContext extends Application {
#Override
public void start(final Stage stage) throws Exception {
TextField textField = new TextField();
TextFieldSkin customContextSkin = new TextFieldSkin(textField) {
#Override
public void populateContextMenu(ContextMenu contextMenu) {
super.populateContextMenu(contextMenu);
contextMenu.getItems().add(0, new SeparatorMenuItem());
contextMenu.getItems().add(0, new MenuItem("Register"));
}
};
textField.setSkin(customContextSkin);
stage.setScene(new Scene(textField));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
so I'm writing a javafx app and I need to be able to select the cells from the list view (for copy paste purposes) but I don't want to make it editable, I mean, the content cannot be changed unless I want to (allowing it through a button, for example).
So I have the following code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
ListView<String> contactsList = new ListView();
contactsList.setItems(FXCollections.observableArrayList(contacts));
//this gives me the ability to edit the row as text field but I want this text field to not be editable
contactsList.setCellFactory(TextFieldListCell.forListView());
StackPane pane = new StackPane();
pane.getChildren().add(contactsList);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show(); }
public static void main(String[] args) {
launch(args);
}
}
and if I set 'contactsList' as not editable, I'm not able to edit, neither select.
As you can see (image bellow),I'm editing the cell, but I want to be able to select the text(not the item), but I don't want to be able to delete characters (text selectable but not editable).
so after breaking my head off, lots of research and API reading, I came up with a solution. This does EXACTLY what I wanted to do. Here is the demo if someone needs it ;)
So the idea is, each time we want to select the content of a row we need to select the row, get the textField and set the editing to true or false, (every time).
So in the demo that I made, I placed a button so you can toggle the editing to true or false to be sure that's is working, and how is working.
Cheers.
I commented some of the code for better understanding, if you have any questions about this just let me know.
package sample;
import com.sun.javafx.scene.control.skin.VirtualFlow;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {
private boolean editable = false;
public static IndexedCell getCell(final Control control, final int index) {
return getVirtualFlow(control).getCell(index);
}
public static VirtualFlow<?> getVirtualFlow(Control control) {
Group group = new Group();
Scene scene = new Scene(group);
Stage stage = new Stage();
if(control.getScene() == null) {
group.getChildren().setAll(control);
stage.setScene(scene);
stage.show();
}
VirtualFlow<?>flow = (VirtualFlow<?>) control.lookup("#virtual-flow");
return flow;
}
public void setEditable(ListView contactsList){
//this needs to be done since we need to run our code after the text field was rendered
//so we need to invoke our code after this happens, if not it will throw a null pointer...
Platform.runLater(() -> {
//this is one of the most important guys because javafx api says that
//TextFieldListCell.forListView() allows editing of the cell content when the cell is double-clicked,
// or when {#link ListView#edit(int)} is called.
int rowIndex = contactsList.getSelectionModel().getSelectedIndex();
contactsList.edit(rowIndex);
ListCell rootCell = (ListCell) getCell(contactsList, rowIndex);
TextField textField = (TextField) rootCell.getGraphic();
textField.setEditable(editable);
});
}
#Override
public void start(Stage primaryStage) throws Exception{
List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
ListView<String> contactsList = new ListView();
contactsList.setItems(FXCollections.observableArrayList(contacts));
contactsList.setEditable(true);
//this gives me the ability to edit the row as text field but I want this text field to not be editable
contactsList.setCellFactory(TextFieldListCell.forListView());
contactsList.setOnEditStart(e -> {
setEditable(contactsList);
});
StackPane pane = new StackPane();
Button editBtn = new Button("Toggle edit");
editBtn.setOnAction(event -> {
editable = !editable;
editBtn.setText("Editing = " + editable);
//to cancel any editing that might be occuring
contactsList.getSelectionModel().clearSelection();
});
pane.getChildren().addAll(contactsList,editBtn);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If I understand you correctly, it is not necessary to set the listview to 'not editable', as the default behaviour should suffice for your purpose. Take a look at this code, for example:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ListView listView = new ListView();
listView.getItems().addAll("one","two","three","four");
listView.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println(listView.getSelectionModel().getSelectedItem());
}
});
StackPane root = new StackPane();
root.getChildren().add(listView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("ListView Example");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I changed nothing about the editable-property of the ListView, but I can select every item, without being able to edit it (in the sense of changing its value). You can easily add an EventHandler to the ListView to perform whatever operation you want to perform. You could also add an EventHandler to every cell of the ListView by manipulating the CellFactory, as shown in this answer: How to handle ListView item clicked action?
Here's what works for me:
TableView<DataBean> table = new TableView<>();
table.setItems(...); // list of some DataBean objects with dataBeanField proprty
table.setEditable(true);
TableColumn<DataBean, String> column = new TableColumn<>("SomeData");
column.setCellValueFactory(new PropertyValueFactory<DataBean, String>("dataBeanField"));
column.setCellFactory(new Callback<TableColumn<DataBean, String>, TableCell<DataBean, String>>() {
#Override
public TableCell<DataBean, String> call(TableColumn<DataBean, String> param) {
return new TextFieldTableCell<>(new DefaultStringConverter() {
private String defaultValue = "";
#Override
public String fromString(String newValue) {
return super.fromString(defaultValue);
}
#Override
public String toString(String value) {
return defaultValue = super.toString(value);
}
});
}
});
In JavaFX's WebView I am struggling to detect change in URL.
I have this method in a class:
public Object urlchange() {
engine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
#Override
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == Worker.State.SUCCEEDED) {
return engine.getLocation());
}
}
});
}
and I am trying to use it for an object called loginbrowser like:
System.out.print(loginbrowser.urlchange());
Can you see what I've done wrong?
(Part of) what you are doing wrong
The code you provided in your question doesn't even compile. The changed method of a ChangeListener is a void function, it can't return any value.
Anyway, loading of stuff in a web view is an asynchronous process. If you want the value of the location of the web view after the web view has loaded, you need to either wait for the load to complete (inadvisable on the JavaFX application thread, as that would hang your application until the load is complete), or be notified in a callback that the load is complete (which is what the listener you have is doing).
(Probably) what you want to do
Bind some property to the location property of the web engine. For example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.web.*;
import javafx.stage.Stage;
public class LocationViewer extends Application {
#Override
public void start(Stage stage) throws Exception {
Label location = new Label();
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
engine.load("http://www.fxexperience.com");
location.textProperty().bind(engine.locationProperty());
Scene scene = new Scene(new VBox(10, location, webView));
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The above code will update the location label whenever the location of the web view changes (try it by running the code then clicking on some links). If you wish to only update the label once a page has successfully loaded, then you need a listener based upon the WebView state, for example:
import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.web.*;
import javafx.stage.Stage;
public class LocationAfterLoadViewer extends Application {
#Override
public void start(Stage stage) throws Exception {
Label location = new Label();
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
engine.load("http://www.fxexperience.com");
engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (Worker.State.SUCCEEDED.equals(newValue)) {
location.setText(engine.getLocation());
}
});
Scene scene = new Scene(new VBox(10, location, webView));
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you run the last program and click on some links, you will notice it delays the updating of the location label until after the pages you click on completely finish loading, as opposed to the first program which updates the label as soon as the location changes, regardless of whether the load takes a while or indeed works at all.
Answers to additional questions
How can I use the url value in the label in a conditional statement? I want an action to be preformed if it changed from the original one.
location.textProperty().addListener((observable, oldValue, newValue) -> {
// perform required action.
});
i am trying to influence a UI-Element during an event in javaFX.
void buttonClicked(ActionEvent e) {
labelInfo.setText("restarting - might take a few seconds");
jBoss.restart();
labelInfo.setText("JBoss successfully restarted");
}
The action "jBoss.restart()" waits till the JBoss is restarted.
The problem:
the text "restarting - ..." is not displayed. The application waits till the JBoss is restarted and then it shows the Text "JBoss successfully restarted".
My thoughts:
the scene is refreshed AFTER the event is completed. So the first label-change will not happen.
How can i show a info message during an event?
The problem it's that the FX Thread has no safe operations. So I'm guessing that jBoss.restart() it's taking a lot of time. So you have to put this command in a Service. Also I recommend to you a progress indicator to show to the user you are making a long operation.
Here it is an example but I encourage you to go to Concurrency in JavaFX and take a deep look on it. Maybe there are other things that can help you.
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
private Label labelInfo;
private Button button;
private ProgressIndicator progressIndicator;
#Override
public void start(Stage stage) throws Exception {
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
labelInfo = new Label();
button = new Button("Restart");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
buttonClicked(event);
}
});
progressIndicator = new ProgressIndicator(-1);
progressIndicator.setVisible(false);
vbox.getChildren().addAll(labelInfo, progressIndicator, button);
Scene scene = new Scene(vbox, 300, 200);
stage.setScene(scene);
stage.show();
}
void buttonClicked(ActionEvent e) {
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("restarting - might take a few seconds");
// Here the blocking operation
// jBoss.restart();
Thread.sleep(10000);
updateMessage("JBoss successfully restarted");
return null;
}
};
}
};
// Make the progress indicator visible while running
progressIndicator.visibleProperty().bind(service.runningProperty());
// Bind the message of the service to text of the label
labelInfo.textProperty().bind(service.messageProperty());
// Disable the button, to prevent more clicks during the execution of
// the service
button.disableProperty().bind(service.runningProperty());
service.start();
}
}
I have an app in JavaFX that is getting a bit large, and I want to keep the code readable.
I have a LineChart that I want to have zoom functionality built in, that occurs on a mouseclick. I know I need to register a mouse listener to the chart. What I cannot figure out from Oracle examples, ie as written here:
http://docs.oracle.com/javafx/2/events/handlers.htm
is how to NOT have my handler defined inline to the registering. In other words, I want the body of the handler (which is many lines of code) to be in another class. Can I do that? And if so, how do I register the handler to my chart in my main Javafx controller code?
Place your handler in a new class which implements the the Mouse EventHandler and register an instance of your class with your target node via the node's setOnClicked method.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* JavaFX sample for registering a click handler defined in a separate class.
* http://stackoverflow.com/questions/12326180/registering-mouse-handler-but-handler-not-inline-in-javafx
*/
public class ClickHandlerSample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(final Stage stage) throws Exception {
stage.setTitle("Left click to zoom in, right click to zoom out");
ImageView imageView = new ImageView("http://upload.wikimedia.org/wikipedia/commons/b/b7/Idylls_of_the_King_3.jpg");
imageView.setPreserveRatio(true);
imageView.setFitWidth(150);
imageView.setOnMouseClicked(new ClickToZoomHandler());
final StackPane layout = new StackPane();
layout.getChildren().addAll(imageView);
layout.setStyle("-fx-background-color: cornsilk;");
stage.setScene(new Scene(layout, 400, 500));
stage.show();
}
private static class ClickToZoomHandler implements EventHandler<MouseEvent> {
#Override public void handle(final MouseEvent event) {
if (event.getSource() instanceof Node) {
final Node n = (Node) event.getSource();
switch (event.getButton()) {
case PRIMARY:
n.setScaleX(n.getScaleX()*1.1);
n.setScaleY(n.getScaleY()*1.1);
break;
case SECONDARY:
n.setScaleX(n.getScaleX()/1.1);
n.setScaleY(n.getScaleY()/1.1);
break;
}
}
}
}
}