Select first item shown into a treeTableView in JavaFX 8 - javafx

I have a TreeTableView control with several items. The user can move with the side scroll bar to any part of the table. How can I know which is the first item that the table shows? I mean, the first item we see. I have been going through the methods offered by this control and I have seen that scrollTo shows an item according to an order number that we specify but I have not found anything that is something like getFirstItemShown. I imagine that for a TableView control it should work the same, right? I'm using JavaFX 8.

Just for fun:
(note that if there are cut lines it can choose the next row, and the first row is sometimes hidden under the Column)
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableView;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Tree View Sample");
TreeItem<String> rootItem = new TreeItem<String> ("Inbox");
rootItem.setExpanded(true);
for (int i = 1; i < 888; i++) {
TreeItem<String> item = new TreeItem<String> ("Message" + i);
rootItem.getChildren().add(item);
}
TreeTableColumn<String, String> column = new TreeTableColumn<>("Column");
column.setPrefWidth(150);
column.setCellValueFactory((CellDataFeatures<String, String> p) -> new ReadOnlyStringWrapper(
p.getValue().getValue()));
TreeTableView<String> treeTableView = new TreeTableView<>(rootItem);
treeTableView.getColumns().add(column);
primaryStage.setScene(new Scene(treeTableView, 300, 250));
primaryStage.show();
Platform.runLater(()->{
ScrollBar verticalBar = (ScrollBar) treeTableView.lookup(".scroll-bar");
verticalBar.valueProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
int currentRow = 0;
if(newValue.doubleValue()<1){
currentRow =(int)((newValue.doubleValue()*treeTableView.getExpandedItemCount())-(newValue.doubleValue()*verticalBar.getVisibleAmount())*treeTableView.getExpandedItemCount());
}else {
currentRow =(int)(treeTableView.getExpandedItemCount()-verticalBar.getVisibleAmount()*treeTableView.getExpandedItemCount());
}
System.out.println(currentRow);
}
});
});
}
public static void main(String[] args) {
launch(args);
}
}

Related

How to create table with vertical/column header in JavaFX

Is there a way to create tableview with vertical headings ? I don't see any option in javafx to do this.
You can set the graphic to a Label which is rotated, and set the text to an empty string.
private void makeColumnHeader(TableColumn<?,?> column) {
Label label = new Label();
label.setText(column.getText());
column.setText("");
label.setRotate(90);
column.setGraphic(label);
}
Here's a complete example:
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
private void makeColumnHeader(TableColumn<?,?> column) {
Label label = new Label();
label.setText(column.getText());
column.setText("");
label.setRotate(90);
column.setGraphic(label);
}
#Override
public void start(Stage stage) throws IOException {
TableView<Item> table = new TableView<>();
TableColumn<Item, Number> idColumn = new TableColumn<>("Id");
idColumn.setCellValueFactory(data -> new SimpleIntegerProperty(data.getValue().id()));
TableColumn<Item, String> itemColumn = new TableColumn<>("Item");
itemColumn.setCellValueFactory(data -> new SimpleStringProperty(data.getValue().name()));
makeColumnHeader(idColumn);
makeColumnHeader(itemColumn);
table.getColumns().add(idColumn);
table.getColumns().add(itemColumn);
for (int i = 1 ; i <= 20; i++) table.getItems().add(new Item(i, "Item "+i));
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static record Item(int id, String name){}
public static void main(String[] args) {
launch();
}
}
Note that setting the column's text to an empty string can have undesirable side effects. For example, the tableMenuButton relies on the text in the table columns to display the menu items. Add table.setTableMenuButtonVisible(true); to the code above to see the problem.
A slightly more robust solution is to bind the text of the label in the graphic to the text in the column, and then use CSS to hide the default text:
private void makeColumnHeader(TableColumn<?,?> column) {
Label label = new Label();
label.textProperty().bind(column.textProperty());
label.setRotate(90);
column.setGraphic(label);
}
and in an external style sheet:
.table-column > .label {
-fx-content-display: graphic-only;
}
I had to adapt the solution from #James_D to properly size the label by applying a minWidth and wrapping it in a Group: (Tested with openjfx19)
private void makeColumnHeader(TableColumn<?, ?> column, String text) {
Label label = new Label();
label.setText(text);
label.setRotate(-90);
label.setMinWidth(80);
column.setGraphic(new Group(label));
column.getStyleClass().add("rotated");
}

JAVA FX: TableVIew with Map Data, update value when input lost focus

I´m trying to display a Map with a TableView, so far all works fine, but when I´m editing the value of any field, the change only is saved when and press ENTER key and I would like that the change was stored when the input field loses the focus.
I have tried to reach this behaviour with a custom render but It doesn´t work I have expected.
This is my code, and the only thing that I need is to know the way to save the changes when the user move the mouse and unselect the row, losing the focus.
import java.util.HashMap;
import java.util.Map;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.MapValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class TableViewSample extends Application {
public static final String Column1MapKey = "Key";
public static final String Column2MapKey = "Value";
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
TableColumn<Map, String> firstDataColumn = new TableColumn<>("Key");
TableColumn<Map, String> secondDataColumn = new TableColumn<>("Value");
firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey));
firstDataColumn.setMinWidth(130);
secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey));
secondDataColumn.setMinWidth(130);
TableView table_view = new TableView<>(generateDataInMap());
table_view.setEditable(true);
table_view.getSelectionModel().setCellSelectionEnabled(true);
table_view.getColumns().setAll(firstDataColumn, secondDataColumn);
Callback<TableColumn<Map, String>, TableCell<Map, String>>
cellFactoryForMap = new Callback<TableColumn<Map, String>,
TableCell<Map, String>>() {
#Override
public TableCell call(TableColumn p) {
return new TextFieldTableCell(new StringConverter() {
#Override
public String toString(Object t) {
return t.toString();
}
#Override
public Object fromString(String string) {
return string;
}
});
}
};
firstDataColumn.setCellFactory(cellFactoryForMap);
secondDataColumn.setCellFactory(cellFactoryForMap);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(table_view);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
private ObservableList<Map> generateDataInMap() {
int max = 10;
ObservableList<Map> allData = FXCollections.observableArrayList();
for (int i = 1; i < max; i++) {
Map<String, String> dataRow = new HashMap<>();
String key = "Key " + i;
String value = "Value " + i;
dataRow.put(Column1MapKey, key);
dataRow.put(Column2MapKey, value);
allData.add(dataRow);
}
return allData;
}
}
Regards
You might be able to get there adding an event listener on table_view.focusedProperty.
This would listen to the focus of the table view though, and not to the focus for each item.
table_view.focusedProperty.addListener( new ChangeListener<Boolean>(){
FocusPropertyChangeListener() { System.out.println("New FPCL instance"); }
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean oldb, Boolean newb) {
System.out.println("Focus change triggered");
}
});

is there any way to add button into dropdown list with textfields using controlfx jar

i have a textfield with autocomplete and i want to show button near each item appeared into that list
this is my code
List<String> s = ms.getUsernames(ms.getUser(7).getListamis());
TextFields.bindAutoCompletion(txtsearch, s);
this is the method getusernames
public List<String> getUsernames(String list_amis) {
List<String> ls = new ArrayList<>();
String [] idmember = list_amis.split("/");
for (String i : idmember) {
ls.add(getUser(Integer.parseInt(i)).getUsername());
}
return ls;
}
this is my output
i want to add a button near testing as a result i can get its ID
This is the solution I came up with. There certainly is a better way that is more elegant and works better, but I guess this would work for many situations. You would have to do something about the size of the ListView, though, and I didn't test this in an environment in which the appearing ListView might change something about the design of the rest of the UI. I suggest putting the whole thing into a PopOver or something.
import java.util.function.Predicate;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<String> list = FXCollections.observableArrayList("one","two","three");
FilteredList<String> filteredList = new FilteredList<>(list);
VBox box = new VBox();
TextField textField = new TextField();
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
filteredList.setPredicate(new Predicate<String>() {
#Override
public boolean test(String s){
return s.toLowerCase().contains(newValue.toLowerCase());
}
});
}
});
ListView listView = new ListView();
listView.setItems(filteredList);
listView.visibleProperty().bind(textField.textProperty().isNotEmpty());
listView.setCellFactory(new Callback() {
#Override
public Object call(Object param) {
ListCell cell = new ListCell(){
#Override
public void updateItem(Object item, boolean empty){
if(item != null && !empty){
super.updateItem(item, empty);
HBox contentBox = new HBox();
Label label = new Label(item.toString());
Button button = new Button("delete");
HBox separator = new HBox();
HBox.setHgrow(separator, Priority.ALWAYS);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println(item);
}
});
contentBox.getChildren().addAll(label, separator, button);
setGraphic(contentBox);
}else{
setText(null);
setGraphic(null);
}
}
};
return cell;
}
});
box.getChildren().addAll(textField,listView);
StackPane root = new StackPane();
root.getChildren().add(box);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Autocomplete");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX Treeview - how to create a parent node with "two" graphics

I have a node with several children and want to keep the collapse/expand arrow that is the default icon of the node, but I have another graphic that I would like to put right next to the arrow. Does the JavaFX treeview allow a way to do this?
Either just pass the graphic to the TreeItem:
TreeItem<String> root = new TreeItem<>("Root", new Rectangle(16, 16, Color.CORAL));
etc,
or use a cell factory:
TreeView<String> tree = new TreeView<>();
tree.setCellFactory(tv -> new TreeCell<String>() {
private final Node graphic = new Rectangle(16, 16, Color.CORAL);
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : graphic);
setText(empty ? null : item);
}
});
Note that for large trees, the second technique has the potential to be far more efficient, as it only creates graphics (two nodes in this case) for each cell, whereas the first technique creates graphics for every item in the tree (whether or not it is displayed). Arguably (I would strongly argue), the second technique has better separation of concerns (in the first solution the graphic is part of data, which is just plain wrong).
SSCCE for first technique:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TreeGraphicTest extends Application {
#Override
public void start(Stage primaryStage) {
TreeItem<String> root = new TreeItem<>("Root", new Rectangle(16, 16, Color.CORAL));
for (int i = 1 ; i <= 3 ; i++) {
root.getChildren().add(new TreeItem<>("Child "+i, new Rectangle(16, 16, Color.CORNFLOWERBLUE)));
}
TreeView<String> tree = new TreeView<>(root);
primaryStage.setScene(new Scene(tree));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
SSCCE for second technique:
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TreeGraphicTest extends Application {
#Override
public void start(Stage primaryStage) {
TreeItem<String> root = new TreeItem<>("Root");
for (int i = 1 ; i <= 3 ; i++) {
root.getChildren().add(new TreeItem<>("Child "+i));
}
TreeView<String> tree = new TreeView<>(root);
tree.setCellFactory(tv -> new TreeCell<String>() {
private final Node rootGraphic = new Rectangle(16, 16, Color.CORAL) ;
private final Node childGraphic = new Rectangle(16, 16, Color.CORNFLOWERBLUE) ;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : getTreeItem() == root ? rootGraphic : childGraphic);
setText(empty ? null : item);
}
});
primaryStage.setScene(new Scene(tree));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Screenshot (for either):

JavaFX fixed button size with dynamic length of text

I have a problem with my button size in JavaFX. I want to have fixed size buttons but when I change text on the buttons, changes button size aswell.
I have 5 buttons and 5 random numbers between 1 - 20. Buttons with single digit is smaller then buttons with two digits. I want both same size.
What can I do?
Here is one way to do this. The buttons go in a TilePane, the TilePane goes in a group so that everything in it remains at it's preferred size. A preferred size is set on each button.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import java.util.Random;
public class FixedSizes extends Application {
private static final int NUM_BUTTONS = 5;
private static final int MAX_BUTTON_VALUE = 20;
private static final Random random = new Random(42);
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(generateButtonLayout()));
stage.show();
}
private Parent generateButtonLayout() {
TilePane layout = new TilePane();
layout.setHgap(10);
layout.setPrefColumns(NUM_BUTTONS);
layout.getChildren().setAll(createButtons());
layout.setPadding(new Insets(10));
return new Group(layout);
}
private Button[] createButtons() {
Button[] buttons = new Button[NUM_BUTTONS];
for (int i = 0; i < buttons.length; i++) {
buttons[i] = createButton();
}
return buttons;
}
private Button createButton() {
Button button = new Button(generateButtonText());
button.setOnAction(event -> button.setText(generateButtonText()));
button.setPrefWidth(50);
return button;
}
private String generateButtonText() {
return "" + (random.nextInt(MAX_BUTTON_VALUE) + 1);
}
public static void main(String[] args) {
launch(args);
}
}

Resources