The question is in the title: is it possible to catch the resize of column (TreeTableColumn) to apply a new column size?
Add a Listener to the columns width property. Something like this:
col.widthProperty().addListener(e->{
if(!running.get()) {
running.set(true);
Runnable task = () -> {
try {Thread.sleep(2000);} catch (InterruptedException exc) { }
Platform.runLater( () -> {
col.setPrefWidth(79);
col.setPrefWidth(80);
});
running.set(false);
System.out.println("done");
};
new Thread(task).start();
}
});
If the prefWidth is just set to the previous value nothing happens. It has to actually change before the column resizes.
Related
I have a TableView that I am populating with 2 columns (Key and Value) from a database. When I click on a table row, I open a new scene which has a text area that shows the Value column and allows one to edit it. I choose to go with a separate UI for editing the contents of value column as it contains a prettyfied JSON document and having an in place edit in the table would have been cumbersome.
dataTable.setRowFactory(tv -> {
TableRow<Map.Entry<String, String>> row = new TableRow<>();
row.setOnMouseClicked(event -> {
showDataPopup(dataValue.getKey(), dataValue.getValue());
});
return row;
});
private void showDataPopup(String key, String value) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("tableDataPopup.fxml"));
Parent dataRoot = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Data Viewer");
stage.setScene(new Scene(dataRoot, 800, 500));
DataPopupController dataPopupController = fxmlLoader.getController();
dataPopupController.loadDataTextArea(key, value, this);
stage.show();
} catch (Exception e) {
logger.error("Error loading tableDataPopup.fxml", e);
}
}
Now, in the tableDataPopup scene, I allow the value to be edited. I have a save button which should save the edited document back to the table and close the scene. Here's my save method
public void saveEditedDocument(ActionEvent event) {
//code to save document to db
mainController.refreshTable(docIdLabel.getText(), dataTextArea.getText());
Stage stage = (Stage) editCancelButton.getScene().getWindow();
stage.close();
}
...
I have a refreshTable method in my main controller. Main controller has all the TableView components and logic.
public void refreshTable(String docId, String docVal) {
logger.info(": {}", dataTable.getItems());
}
I need help figuring out how to update the cell value that was changed in the popup dialog. I'd rather avoid having to stream the whole table and look for the key column and update the value. I am looking for a way to pass the cell index to the data popup and have it pass it back to the refreshTable method. Then use it to directly update the cell and then call dataTable.refresh() method to refresh the data table.
I am struggling with where to even start on this. Any pointers would really help...
I guess i figured it out. May not be an elegant solution, but this works for me.
I am passing the index of the row that was clicked on to my dataPopupController
dataTable.setRowFactory(tv -> {
TableRow<Map.Entry<String, String>> row = new TableRow<>();
row.setOnMouseClicked(event -> {
showDataPopup(dataValue.getKey(), dataValue.getValue(),row.getIndex());
});
return row;
});
private void showDataPopup(String key, String value, int index) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("tableDataPopup.fxml"));
Parent dataRoot = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Data Viewer");
stage.setScene(new Scene(dataRoot, 800, 500));
DataPopupController dataPopupController = fxmlLoader.getController();
dataPopupController.loadDataTextArea(key, value, this, **index**);
stage.show();
} catch (Exception e) {
logger.error("Error loading tableDataPopup.fxml", e);
}
}
And I updated the refreshTable method in main controller to update the item based on the index
public void refreshTable(String docVal, int tableIndex) {
logger.info(": {}", dataTable.getItems());
logger.info("Table value is: {}", dataTable.getItems().get(tableIndex).setValue(docVal));
dataTable.refresh();
}
And in my popup controller, I just pass back the index when I call refreshTable method
public void saveEditedDocument(ActionEvent event) {
// Todo - refresh table after save
try {
mainController.refreshTable(minifyData(dataTextArea.getText()), tableIndex);
Stage stage = (Stage) editCancelButton.getScene().getWindow();
stage.close();
} catch (JsonSyntaxException exception) {
saveStatusLabel.setText("Malformed JSON, please correct");
logger.error("Malformed JSON - {}", exception.getMessage());
}
}
May not be the most elegant way to do this, but in a pinch, it works for me.
I'm trying to remove the event handler from a FontAwesomeIconView object but it's not working. The event handler keeps working and I don't know why.
EventHandler<MouseEvent> glowIcon = (e) -> {
FontAwesomeIconView icon = (FontAwesomeIconView) e.getSource();
icon.setFill(Color.web("#ffb521"));
scene.setCursor(Cursor.HAND);
};
EventHandler<MouseEvent> unglowIcon = (e) -> {
FontAwesomeIconView icon = (FontAwesomeIconView) e.getSource();
icon.setFill(Color.web("#000000"));
scene.setCursor(Cursor.DEFAULT);
};
if (Session.getSession().isProjectCreator()) {
newIcon.setFill(Color.web("#000000"));
newIcon.addEventHandler(MouseEvent.MOUSE_ENTERED, glowIcon);
newIcon.addEventHandler(MouseEvent.MOUSE_EXITED, unglowIcon);
}else {
newIcon.setFill(Color.web("#e8e8e8")); //It's changed to this color
newIcon.removeEventHandler(MouseEvent.MOUSE_ENTERED, glowIcon);
newIcon.removeEventHandler(MouseEvent.MOUSE_EXITED, unglowIcon);
}
In the beginning the event handlers are ADDED, but when I remove them they keep working, when they shouldn't.
Without seeing a minimal reproducible example it is hard to tell. The most likely scenario from the code that you have posted is that glowIcon/unglowIncon are pointing at different lambdas when you add the handler vs remove it.
Every time this code runs it will assign a different lambda reference to those variables, even if everything is exactly the same.
EventHandler<MouseEvent> glowIcon = (e) -> {
FontAwesomeIconView icon = (FontAwesomeIconView) e.getSource();
icon.setFill(Color.web("#ffb521"));
scene.setCursor(Cursor.HAND);
};
The solution is to make sure they are only called once and the reference to them is kept in your controller for as long as they are needed. Using final is a good defensive technique to make sure you don't accidentally reassign it somewhere and lose the reference you need to remove the handler.
Something like:
public class GlowController {
private final EventHandler<MouseEvent> glowIcon = (e) -> {
FontAwesomeIconView icon = (FontAwesomeIconView) e.getSource();
icon.setFill(Color.web("#ffb521"));
scene.setCursor(Cursor.HAND);
};
private final EventHandler<MouseEvent> unglowIcon = (e) -> {
FontAwesomeIconView icon = (FontAwesomeIconView) e.getSource();
icon.setFill(Color.web("#000000"));
scene.setCursor(Cursor.DEFAULT);
};
public void doSomething() {
if (Session.getSession().isProjectCreator()) {
newIcon.setFill(Color.web("#000000"));
newIcon.addEventHandler(MouseEvent.MOUSE_ENTERED, glowIcon);
newIcon.addEventHandler(MouseEvent.MOUSE_EXITED, unglowIcon);
}else {
newIcon.setFill(Color.web("#e8e8e8")); //It's changed to this color
newIcon.removeEventHandler(MouseEvent.MOUSE_ENTERED, glowIcon);
newIcon.removeEventHandler(MouseEvent.MOUSE_EXITED, unglowIcon);
}
}
}
In case someone stumbles upon this when trying to figure out why removing event listeners add with:
vbox.setOnMouseClicked(eventHandler);
do not get removed with removeEventHandler. It turns out that you need to add your event handler using:
vbox.addEventHandler(MouseEvent.MOUSE_CLICKED, eventHandler);
for the remove call to work.
I would have thought that it would work regardless of the way you added your handler, but it doesn't :|
In my GUI I create some dices (ImageView) and I can drag and drop them in one specific GridPane. When I drop a dice into the specific GridPane, the dice disappears from the initial location and it moves to the right position. This works fine only if I choose the right drop location.
The problem is how can I manage the wrong drop location?
Actually if I drop a dice in a wrong location (like outside the Gridpane) the dice disappears like it was moved to the right position.
I want to restore the dice to the original location if the dice isn't placed to the GridPane.
Is there a method can help me to check if I drop into the right location? Or something can prevent to drop into the wrong location?
You can check the transferMode property of the DragEvent passed to the onDragDone event:
dragSource.setOnDragDone(evt -> {
if (evt.getTransferMode() == null) {
System.out.println("drag aborted");
} else {
System.out.println("drag successfully completed");
}
});
Note: this requires you to mark the drag gesture as completed in the onDragDropped event handler using setDropCompleted. Example:
#Override
public void start(Stage primaryStage) {
Button source = new Button("Not dragged yet");
Button target = new Button("target");
HBox root = new HBox(20, source, target);
source.setOnDragDetected(evt -> {
Dragboard db = source.startDragAndDrop(TransferMode.COPY);
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
});
source.setOnDragDone(evt -> {
source.setText(evt.getTransferMode() == null ? "failure" : "success");
});
target.setOnDragOver(evt -> {
if (evt.getDragboard().hasString()) {
evt.acceptTransferModes(TransferMode.COPY);
evt.consume();
}
});
target.setOnDragDropped(evt -> {
String value = evt.getDragboard().getString();
target.setText(value);
evt.setDropCompleted(true);
evt.consume();
});
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
I have made treeview. I want to make treeview like this.
when I enter mouse to item, that item should change image.
when I clcick mouse to item, that item should chnage image.
I know how the way getSelectionMode()... but I don't know hover event.
Please help me.
Not sure if I understand you correct.
But to change your image as soon as you click an image use a selectedItemProperty listener:
treeView.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<String> selectedItem = (TreeItem<String>) newValue;
// do something
}
});
If you want it as soon as you hover over the item you can use a hoverProperty on the row:
treeView.setRowFactory(tableView -> {
final TableRow<Person> row = new TableRow<>();
row.hoverProperty().addListener((observable) -> {
final YourItem yourItem = row.getItem();
if (yourItem.isHover() ) {
// do something
} else {
// do something
}
});
return row;
});
(this code is from the answer here)
I missread, its about a TreeView. You can try an onMouseEntered or similiar within a cellfactory:
treeView.setCellFactory(tv -> {
final Tooltip tooltip = new Tooltip();
TreeCell<Path> cell = new TreeCell<Path>() {
#Override
public void updateItem(Path item, boolean empty) {
super.updateItem(item, empty);
}
};
cell.setOnMouseClicked(e -> {
// do something
});
cell.setOnMouseEntered(e -> {
// do something
});
return cell ;
});
I'm trying to get the progress value of my Picture Viewer when another picture is loading. I've tried two simple ways to do it, but it didn't work out for me.
First I was using the progressProperty from the Image class:
public boolean nextImageClicked()
{
if(PICTURE_INDEX < picturePaths.size() - 1)
{
String path = picturePaths.get(PICTURE_INDEX + 1).toURI().toString();
Image newImage = new Image(path);
newImage.progressProperty().addListener((observable, oldValue, newValue) -> System.out.println("Current progress: "+newValue));
GUI.getImageView().setImage(newImage);
adjustImageViewBounds();
PICTURE_INDEX += 1;
return true;
}
else return false;
}
It didn't print out anything on the console because the progress value doesn't change bizarrely. So I've tried to put all the work stuff in a Task<Void> and getting the progress value through the Task:
public boolean nextClicked()
{
if(PICTURE_INDEX < picturePaths.size() - 1)
{
Task<Void> task = new Task<Void>()
{
#Override protected Void call() throws Exception
{
String path = picturePaths.get(PICTURE_INDEX + 1).toURI().toString();
Image newImage = new Image(path);
GUI.getImageView().setImage(newImage);
adjustImageViewBounds();
PICTURE_INDEX += 1;
return null;
}
};
task.setOnRunning(e -> System.out.println(task.getProgress()));
task.progressProperty().addListener((observable, oldValue, newValue) ->
{
System.out.println(newValue);
});
task.run();
return true;
}
else return false;
}
Also didn't work out as hoped.
task.setOnRunning(e -> System.out.println(task.getProgress()));
I implemented this to see the default value, it printed out "-1".
What have I to change to let the console return single progress values like "0.1", "0.14" ?
You need the Image to load in the background, so that the call to the constructor returns before the image is completely loaded. By default, it will block until it is loaded (so the progress property will be 1 by the time you add the listener to it):
public boolean nextImageClicked()
{
if(PICTURE_INDEX < picturePaths.size() - 1)
{
String path = picturePaths.get(PICTURE_INDEX + 1).toURI().toString();
// note additional parameter:
Image newImage = new Image(path, true);
newImage.progressProperty().addListener((observable, oldValue, newValue) -> System.out.println("Current progress: "+newValue));
GUI.getImageView().setImage(newImage);
adjustImageViewBounds();
PICTURE_INDEX += 1;
return true;
}
else return false;
}
For a Task's progress to change, you need to explicitly call updateProgress(...) on the task. The only way to know what to pass in would be to observe the image's progress and pass it to the task's progress, so you would just have a more convoluted version of the code above. This is not a good use case for a task, since Image already supports background loading out of the box.
Don't try to do this on your own. You've got no idea, how much of the image has been loaded, unless you find the size of the image before loading and load the image from a steam observing the progress of the stream, which would be unnecessarily complicated. BTW: The Image constructor you use returns when the image is completely loaded. You can specify the image to be loaded asynchronically by using the right constructor however. Image provides a progress property to observe the loading progress:
#Override
public void start(Stage primaryStage) {
ImageView iv = new ImageView();
ProgressBar pb = new ProgressBar();
Button btn = new Button("Load Image");
btn.setOnAction((ActionEvent event) -> {
// ca. 6 MB image loaded from web
Image image = new Image("http://eoimages.gsfc.nasa.gov/images/imagerecords/79000/79793/city_lights_africa_8k.jpg", true);
pb.progressProperty().bind(image.progressProperty());
iv.setImage(image);
});
ScrollPane sp = new ScrollPane(iv);
VBox.setVgrow(sp, Priority.ALWAYS);
VBox root = new VBox(btn, pb, sp);
root.setFillWidth(true);
Scene scene = new Scene(root);
primaryStage.setMaximized(true);
primaryStage.setScene(scene);
primaryStage.show();
}