DatePicker getContextMenu() is null [duplicate] - java

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);
}
}

Related

How to make a ListView selectable but not editable

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);
}
});
}
});

Detect URL change in JavaFX WebView

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.
});

Adding a change listener to JavaFX ToggleSwitch

My stage contains a ToggleSwitch and two StackPanes which we'll call A and B. Both StackPanes are located in the same space within the parent StackPane. This means that if both A and B are visible and set to managed, they each take up half the allocated space like this:
I'm trying to hide StackPane B upon initalization so that StackPane A takes up the full space... and then when I click the toggle button, it should hide StackPane A and show StackPane B, making B take up the full space.
The initial hiding of StackPane B works fine, but I'm having trouble writing a change listener for the ToggleSwitch in my controller class. Here is my code, and where I'm having trouble:
application class:
public class showPanes extends Application {
Stage stage = new Stage();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
StackPane root = (StackPane) FXMLLoader.load(Drag.class.getResource("twoPanes.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Pane Switcher");
scene.getStylesheets().add("styleMain.css");
stage.setScene(scene);
stage.show();
}
}
The answer found here uses Toggle Groups, and James' answer here uses Buttons. I can't find a solution for ToggleSwitch. I tried to adapt the first answer for use with ToggleSwitch but it's producing an error like:
and says
Cannot resolve method 'addListener(anonymous
javafx.beans.value.ChangeListener)'
How do I fix the listener?
controller class:
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import java.net.URL;
import java.util.ResourceBundle;
import org.controlsfx.control.ToggleSwitch;
public class compsController implements Initializable {
#FXML
private StackPane paneA, paneB;
#FXML
private ToggleSwitch toggleSwitch;
#Override
public void initialize(URL location, ResourceBundle resources) {
paneB.setManaged(false);
paneB.setVisible(false);
toggleSwitch.selectedProperty().addListener(new ChangeListener < ToggleSwitch > () {
#Override
public void changed(ObservableValue < ? extends ToggleSwitch > ov, ToggleSwitch t, ToggleSwitch t1) {
paneA.setManaged(false);
paneA.setVisible(false);
paneB.setManaged(true);
paneB.setVisible(true);
}
});
}
}
You could also use the Binding API of JavaFx like below,
#Override
public void initialize(URL location, ResourceBundle resources) {
paneA.managedProperty().bind(toggleSwitch.selectedProperty());
paneA.visibleProperty().bind(toggleSwitch.selectedProperty());
paneB.managedProperty().bind(toggleSwitch.selectedProperty().not());
paneB.visibleProperty().bind(toggleSwitch.selectedProperty().not());
}
}

JavaFX connecting components to model

I have a JavaFX user interface with several controls; the values should be stored inside fields of a Model class; the UI class has a reference to Model.
Say the Model class is the basic:
public static class Model{String myText; /*javabeans getters and setters provided too*/}
The JavaFX Application is the following.
public class T08 extends Application {
Model model;
#Override
public void start(Stage primaryStage) throws Exception {
model = new Model();
BorderPane bp = new BorderPane();
primaryStage.setScene(new Scene(bp));
//this is the component that should be connected to model.myText
TextField textField = new TextField();
bp.setCenter(textField);
primaryStage.show();
}
Question
The user can write text in textField control and the text should be saved into model.myText.
During application startup i need to load the data into the Model and have it rendered to the controls.
I've tried with JavaFX 2.x bindings, but they seem to focus on unidirectional connections.
What are my options to accomplish this in a neat way?
One way is to use myTextProperty.bindBidirectional() instead of myTextProperty.bind(), AFAIK.
Ok I wrote some SSCCE sample code, since code explains the things more precisely :)
First example is for this question:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class JavaFXApplication10 extends Application {
private Model model = new Model();
#Override
public void start(Stage primaryStage) {
final TextField textField = new TextField();
textField.setText(model.getMyText());
Button btn = new Button();
btn.setText("Done");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
model.setMyText(textField.getText());
System.out.println("Done.");
System.out.println("New value: " + model.getMyText());
}
});
BorderPane root = new BorderPane();
root.setTop(textField);
root.setBottom(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// Data Model
public static class Model {
private String myText = "model myText default";
public String getMyText() {
return myText;
}
public void setMyText(String myText) {
this.myText = myText;
}
}
}
The second modified version (uses bidirectional binding) of this example is here.
In both examples try to click the button.
Maybe you take a look at https://github.com/laubfall/modelfx. I wrote this library exactly for this reason. It supports bidirectional Bindings for JavaFX-Components (that are composed in a hierarchy) and Properties that resides in a Bean. For further Documentation refer to the Wiki-Pages of the Github-Project modelfx. Hope you find it helpful!
ps: the core functionality uses Bindings.bindBidrectional of JavaFX.

How to disable column-reordering in a JavaFX2 TableView?

JavaFX2's TableView features
"Column reordering by the user at runtime".
I'd like to disable this feature for one specific table in my Application.
Looking at the API doc, there is no obvious API hook for this.
There is, however, the columns-property. According to the doc, it represents
The TableColumns that are part of this TableView. As the user reorders the TableView columns, this list will be updated to reflect the current visual ordering.
Hoping that I'd at least be able to reset a change after it occurred, I tried adding a listener to reset changes after the fact.
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class TableTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
TableView tableView = new TableView();
tableView.getColumns().setAll(new TableColumn(), new TableColumn());
tableView.getColumns().addListener(new ListChangeListener() {
#Override
public void onChanged(Change change) {
if (change.wasPermutated()){
change.reset();
}
}
});
stage.setScene(new Scene(tableView));
stage.show();
}
}
However, the listener aborts with an IllegalStateException when I ask for wasPermutated.
Is there a way to prevent reordering, or at least revert it programatically?
See below a SSCCE that shows that the listener gets called - but the flag is set to added when moving columns. Note that you need to call next() before using the change or you will get an IllegalStateException. See the javadoc of ListChangeListener.Change for a simple canonical example.
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class TableTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final TableView tableView = new TableView();
final TableColumn[] columns = {new TableColumn("1"), new TableColumn("2")};
tableView.getColumns().setAll(columns);
tableView.getColumns().addListener(new ListChangeListener() {
public boolean suspended;
#Override
public void onChanged(Change change) {
change.next();
if (change.wasReplaced() && !suspended) {
this.suspended = true;
tableView.getColumns().setAll(columns);
this.suspended = false;
}
}
});
stage.setScene(new Scene(tableView));
stage.show();
}
}

Categories

Resources