Idiomatic way to implement context dependent MenuBar? - javafx

I have a #FXML Menu editMenu defined, and I want to populate it depending on which of my TreeViews is currently in focus, and on whether or not there are any TreeItems selected.
What would be the idiomatic way of doing this? I haven't been able to find a nice onFocus method for the TreeViews.
Thanks!

All Nodes have a focusedProperty() with which you can register a listener. Additionally, the Scene has a focusOwner property you can observe.
So you can do something like
scene.focusOwnerProperty().addListener(new ChangeListener<Node>() {
#Override
public void changed(ObservableValue<? extends Node> obs, Node oldFocusOwner, Node newFocusOwner) {
// update menu based on newFocusOwner
}
});
If you're using FXML, it can be difficult to get hold of the Scene in the controller. You may need to do something like:
ChangeListener<Node> menuUpdater = new ChangeListener<Node>() {
#Override
public void changed(ObservableValue<? extends Node> obs, Node oldFocusOwner, Node newFocusOwner) {
// update menu based on newFocusOwner
}
};
someNode.sceneProperty().addListener(new ChangeListener<Scene>() {
#Override
public void changed(ObservableValue<? extends Scene> obs, Scene oldScene, Scene newScene) {
if (oldScene != null) {
oldScene.focusOwnerProperty().removeListener(menuUpdater);
}
if (newScene != null) {
newScene.focusOwnerProperty().addListener(menuUpdater);
}
}
});
where someNode is any node in the scene graph.

Related

Tableview item dependencies javafx

Good evening,
I am working on a project and I'm at lost at how to configure my tableviews to depend on each other. I would like for the parts in one tableview to depend on the products in the other. How would I go about doing that? I prefer to not use sql at the moment just to keep everything simple as possible. The code snippet is listed below:
public class maincontroller {
private ObservableList<Part> parts = FXCollections.observableArrayList();
private ObservableList<Product> products = FXCollections.observableArrayList();
//code to swap between controllers
#Override
public void initialize(URL url, ResourceBundle rb) {
partsID.setCellValueFactory(new PropertyValueFactory<>("partID"));
partsName.setCellValueFactory(new PropertyValueFactory<>("name"));
partsinvlevel.setCellValueFactory(new PropertyValueFactory<>("instock"));
partscost.setCellValueFactory(new PropertyValueFactory<>("price"));
parttable.setItems(parts);
productsID.setCellValueFactory(new PropertyValueFactory<>("productID"));
productsName.setCellValueFactory(new PropertyValueFactory<>("name"));
productsInvlevel.setCellValueFactory(new PropertyValueFactory<>("instock"));
productsprice.setCellValueFactory(new PropertyValueFactory<>("price"));
producttable.setItems(products);
parttable.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Part> observable, Part oldValue, Part newValue) -> {
});
producttable.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Product> observable, Product oldValue, Product newValue) -> {
});
}
}
I'm not too sure about the relationship between Part and Product. From the context, I would assume each specific Product would have its own list of Parts that is related to it. I would also assume that that list of Parts is stored within Product.
Product:
public class Product
{
private final ObservableList<Part> parts = FXCollections.observableArrayList();
public final ObservableList<Part> getParts() { return parts; }
........
}
This is what you can do:
producttable.getSelectionModel().selectedItemProperty().addListener(observable, oldProduct, newProduct) ->
{
if (newProduct != null)
{
parts.clear();
parts.addAll(newProduct.getParts());
}
});

JavaFX wait till stage has been closed

I am using an MVC model where I initialize all the action listeners for the view in the controller. For example:
The view:
public void addStartDateListener(ChangeListener<Boolean>e){
startDate.focusedProperty().addListener(e);
}
public void addFinishDateListener(ChangeListener<Boolean>e){
finishDate.focusedProperty().addListener(e);
}
The controller:
this.theView.addStartDateListener(new startDateFocusListener());
this.theView.addFinishDateListener(new finishDateFocusListener());
class finishDateFocusListener implements ChangeListener<Boolean> {
#Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean newPropertyValue)
{
if (newPropertyValue)
{
System.out.println("Finish date");
}
else
{
System.out.println("Finish date");
}
}
}
//focus listener to update the date whenever the focus is lost
class startDateFocusListener implements ChangeListener<Boolean> {
#Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue, Boolean newPropertyValue)
{
if (newPropertyValue)
{
System.out.println("start date");
}
else
{
System.out.println("start date");
}
}
As the stage(which is the view) appears I need for the application to wait till it gets closed and not allow the user to perform any other action on other stages. If however I use "showAndWait()" with "initModality(Modality.APPLICATION_MODAL);" The action listeners do not get added to the view, but if I use only "initModality(Modality.APPLICATION_MODAL);" it does not allow the user to perform any actions on other stages except the one and all the action listener are fully functional, but I still need to wait till the stage gets closed and only then continue further as the rest is depending on the output of the stage that is being created. So my question is how can I do it?
I have so far tried to use Task with "setOnSucced" and etc. However no luck. I am fairly new to the MVC concept as well.

JavaFX - Update a text field through observer pattern

I am trying to update a text field through observer pattern. The update function in the observer (FXML controller) is called after clicking on a listItem in another controller class. And that works fine. The only problem is that my textfield won't update.
Here is my update function in the observer.
#Override
public void update(Observable o, final Object arg) {
System.out.println("test"); // works
firstNameTextField.setText("test"); // doesn't work (text field is still empty)
System.out.println(firstNameTextField.getText()); //works and shows me the word "test" on my console
}
The funny thing is, if I print the text from the text field on my console it's printing the word "test" on the console. It seems like the text field value is updated but it doesn't show up on the ui.
EDIT:
This is my MainController
public class MainController extends Observable implements Initializable {
private ObservableList<String> items = FXCollections.observableArrayList("item1", "item2");
private List<UserProfile> userProfiles = new ArrayList<UserProfile>();
private String[] tabTitles = { "Profile"};
#FXML
private TabPane tabPane;
#FXML
ListView<String> listView;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
for (String tabTitle : tabTitles) {
Tab tab = new Tab(tabTitle);
tab.setClosable(false);
tabPane.getTabs().add(tab);
}
tabPane.getSelectionModel().clearSelection();
for (Tab tab : tabPane.getTabs()) {
try {
String newStringValue = tab.getText();
Parent root = FXMLLoader.load(getClass().getResource("profile.fxml"));
tab.setContent(root);
FXMLLoader fxmlLoader = new FXMLLoader();
Object p = fxmlLoader.load(getClass().getResource("profile.fxml").openStream());
if (fxmlLoader.getController() instanceof ProfileController) {
ProfileController profileController = (ProfileController) fxmlLoader.getController();
this.addObserver(profileController);
}
} catch (IOException e) {
e.printStackTrace();
}
}
tabPane.getSelectionModel().selectFirst();
listView.setItems(items);
listView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
setChanged();
notifyObservers();
}
});
}
}
ProfileController
public class ProfileController implements Initializable, Observer {
#FXML
TextField firstNameTextField;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
}
#Override
public void update(Observable o, final Object arg) {
System.out.println("test"); // works
firstNameTextField.setText("test"); // doesn't work (text field is still empty)
System.out.println(firstNameTextField.getText()); //works and shows me the word "test" on my console
}
}
Can anybody help me out with this?
Thanks!
When you execute
Object p = fxmlLoader.load(getClass().getResource("profile.fxml").openStream());
if (fxmlLoader.getController() instanceof ProfileController) {
ProfileController profileController = (ProfileController) fxmlLoader.getController();
this.addObserver(profileController);
}
you load the structure represented by profile.fxml, and place that hierarchy (including firstNameTextField) in the object you called p. When you invoke update(...) on profileController, it changes the text in the text field that is part of the hierarchy of p. However, you never do anything with p: you don't display it in your UI. So when you change the text of the text field, the changes are of course invisible (because you are changing a text field that isn't displayed).
Presumably, since you said you have the text field displayed, somewhere in the code you couldn't be bothered to include you are loading profile.fxml and displaying the content in the UI. You need to get the reference to that controller, and register it as an observer. Registering an arbitrary instance of the same class will not have the desired effect.

Using a button in a tableColumn to add a song to a playlist javafx

I was want have a table column with a button labeled "Add to Playlist". The row in the column represents a song. I have the following class:
private class ButtonCell extends TableCell<Record, Boolean> {
final Button cellButton = new Button("Add to PlayList");
ButtonCell(){
cellButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t) {
// do something when button clicked
//playList.add(this.getTableRow().getItem());
}
});
}
//Display button if the row is not empty
#Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if(!empty){
setGraphic(cellButton);
}
}
}
EventHandler<ActionEvent> btnNewHandler =
new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t) {
}
};
And I want to be able to do this 'playList.add(this.getTableRow().getItem());'
Is there a way to do this?
The gist of the question is how do I get the information of a cell and add that information to an observable list?
Thanks
For some reason, getTableRow() returns a raw TableRow object, instead of a generically typed one. In other words the method signature is
public class TableCell<S,T> {
public TableRow getTableRow();
}
when I think it should be
public class TableCell<S,T> {
public TableRow<S> getTableRow();
}
Because of this, the expresison getTableRow().getItem() has a compile-time type of Object, instead of the same type as the table (Record in your example), so you need a downcast. This should work (assuming you have everything else set up as I expect you do):
playList.add((Record) this.getTableRow().getItem() );
A different technique entirely is to make the column type Record, so you can just call getItem() directly on the cell. The answer to this question uses this approach.

JAVAFX event triggered when selecting a check box

My JavaFx FXML application has an issue.
When I select a checkbox on a form, I want to execute a method based on the checkbox that was clicked. Is there any way that I can pass the name of the checkbox through to the method so I can perform some conditional work on it?
I have two checkboxes and only one can be selected. When I click on one, the other should be de-selected and vice versa. Obviously the code below will not work so I am looking to pass the name of the object that was clicked.
Any help would be appreciated,
many thanks.
#FXML private void updateRcs(){
if (chkRcsuri.isSelected()){
chkRcsuri2.setSelected(false);
}
if (chkRcsuri2.isSelected()){
chkRcsuri.setSelected(false);
}
}
You can use change tracking or use Event handling mechanism of JavaFX.
With checkboxes like this,
final CheckBox chk1 = new CheckBox("chk 1");
final CheckBox chk2 = new CheckBox("chk 2");
Change tracking
chk1.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
chk2.setSelected(!newValue);
}
});
chk2.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
chk1.setSelected(!newValue);
}
});
Using event handling
EventHandler eh = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (event.getSource() instanceof CheckBox) {
CheckBox chk = (CheckBox) event.getSource();
System.out.println("Action performed on checkbox " + chk.getText());
if ("chk 1".equals(chk.getText())) {
chk2.setSelected(!chk1.isSelected());
} else if ("chk 2".equals(chk.getText())) {
chk1.setSelected(!chk2.isSelected());
}
}
}
};
chk1.setOnAction(eh);
chk2.setOnAction(eh);
Wouldn't radio buttons give you a mutually exclusive selection? Just make sure you set the groupname to be the same - selecting one would then automatically de-select the other and you can just put additional logic in the Action event.
Better than trying to re-write same functionality around checkboxes.
So I was trying to do a similar thing, except I had multiple checkboxes and then one that would be nonsensical to have selectable in conjuction with the others. I made two seperate listeners and set one general purpose one to the main boxes, and a specialized one to the exception.
#FXML private CheckBox redCB = new CheckBox();
#FXML private CheckBox blueCB = new CheckBox();
#FXML private CheckBox greenCB = new CheckBox();
#FXML private CheckBox whiteCB = new CheckBox();
#FXML private CheckBox blackCB = new CheckBox();
#FXML private CheckBox colorlessCB = new CheckBox();
//assigning listeners
redCB.selectedProperty().addListener(colorCheckChange);
blueCB.selectedProperty().addListener(colorCheckChange);
greenCB.selectedProperty().addListener(colorCheckChange);
whiteCB.selectedProperty().addListener(colorCheckChange);
blackCB.selectedProperty().addListener(colorCheckChange);
colorlessCB.selectedProperty().addListener(colorlessCheckChange);
//note: this is the only different one^^^
//making listeners
ChangeListener colorCheckChange = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
if (new_val)
colorlessCB.setSelected(false);
}};
ChangeListener colorlessCheckChange = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean old_val, Boolean new_val) {
if (new_val)
{
redCB.setSelected(false);
blueCB.setSelected(false);
greenCB.setSelected(false);
blackCB.setSelected(false);
whiteCB.setSelected(false);
}
}
};
The first one basically just makes sure that colorlessCB isn't selected while trying to select the other colors, and vice verca. This way you also avoid the problem of de-selecting one, and the other one automatically reselecting itself.
This is my solution. But be sure about the a variable to be appropriate for you
//First in FXML file
<CheckBox fx:id="chkbxAuto" mnemonicParsing="false" onAction="#autoConfig" text="Auto" />
// in controler
public class FXMLController implements Initializable {
private static int a = 0;
//references to lables
....
#FXML
private Label lblStateValue;
#FXML
private Group grpSetting; // a group of elements which I want to be disabled and enabled
...
#FXML
private void autoConfig(ActionEvent event) {
System.out.println("Configing automatically");
a++;
if(a%2==1){
lblStateValue.setText("Auto configuration enabled"); // it change a lable to show the state
grpSetting.setDisable(true); // it disable a group of elements
}
else{
lblStateValue.setText("Auto configuration disabled");
grpSetting.setDisable(false);
}
a%=10;
}
...
I know this question is pretty old, but I found when looking for the same problem. I found a solution which
a) seems clearer (at least to me comparing to the listener definition) for reading source code and
b) Defining a changeListener on a checkBox gave me some problems.
Anyway my solution was to define an onAction function for the checkbox.
yourCheckboxName.setOnAction(this::aFunctionName);
...
void aFunctionName() {
if(yourCheckboxName.isSelected()) {
doThis()
} else {
doThat()
}
}
Attention This needs Java8 or higher.
None of the above options take advantage of the most commpact lambda expresions that can be used to add this very repetitive listeners.
Let suppose you have two checkboxes:
chk1.selectedProperty().addListener((observable, oldValue, newValue) -> chk2.setSelected(!newValue));
chk2.selectedProperty().addListener((observable, oldValue, newValue) -> chk1.setSelected(!newValue));

Resources