TableView tableColumn width is not considering header when autosizing javafx - javafx

Need help to fix the display issue on table view JavaFX.
I can't paste full code. but, will try to include maximum.
TextField headerTextField = new TextField();
Label label = new Label((String) allColumns[i]);
VBox headerGraphic = new VBox();
headerGraphic.setAlignment(Pos.CENTER);
headerGraphic.getChildren().addAll(label, headerTextField);
TableColumn tableColumn = new TableColumn<>();
tableColumn.setGraphic(headerGraphic);
Output is:
if I don't set graphics and directly create a table column with a column name, it looks good.
TableColumn tableColumn = new TableColumn<>((String) allColumns[i]);
Output is:
Updates:
I resolved it by using Text instead of Label. Seems that Label's width is calculated only after Scene is loaded. Hence, the table column pref width was not set.
With the code below, it worked.
TextField headerTextField = new TextField();
Text label = new Text((String) allColumns[i]);
VBox headerGraphic = new VBox();
headerGraphic.setAlignment(Pos.CENTER);
headerGraphic.getChildren().addAll(label, headerTextField);
TableColumn tableColumn = new TableColumn<>();
tableColumn.setGraphic(headerGraphic);
Output is:

I resolved it by using Text instead of Label. Seems that Label's width is calculated only after Scene is loaded. Hence, the table column pref width was not set. Edited the above post.
Updates:
I had to use resize custom method as above approach didn't work when the table doesn't have records.
So, I called the below function and it worked for both when table with records and table without records.
public static void autoResizeColumns( TableView<?> table )
{
//Set the right policy
table.getColumns().stream().forEach( (column) ->
{
Text t = new Text( column.getText() );
double max = 0.0f;
if("".equals(t.getText()))
{
VBox vBox = (VBox) column.getGraphic();
ObservableList<Node> vBoxChild = vBox.getChildren();
max = vBoxChild.get(0).getLayoutBounds().getWidth();
}
else
{
max = t.getLayoutBounds().getWidth();
}
for ( int i = 0; i < table.getItems().size(); i++ )
{
//cell must not be empty
if ( column.getCellData( i ) != null )
{
t = new Text( column.getCellData( i ).toString() );
double calcwidth = t.getLayoutBounds().getWidth();
//remember new max-width
if ( calcwidth > max )
{
max = calcwidth;
}
}
}
//set the new max-widht with some extra space
column.setPrefWidth( max + 15.0d );
} );
}

Related

ScrollPane.setVvalue() does not update scrollbar in javafx

I have a program where I can insert something in a textfield and then after pressing the enter button, it will be displayed as a label in a VBox.
My layout looks like this:
A tab with inside a borderpane with on the bottom a hbox containing a textfield and a button and at the top a scrollpane containing a vbox full of labels.
This is the code:
Tab consoleTab = new Tab("Console");
consoleTab.setClosable(false);
BorderPane consoleContent = new BorderPane();
TextField commandEntry = new TextField();
commandEntry.setPromptText("Enter command...");
Button exe = new Button("Enter");
HBox input = new HBox(5, commandEntry, exe);
VBox outputL = new VBox();
ScrollPane output = new ScrollPane();
output.setMinHeight(365);
output.setMaxHeight(365);
output.setContent(outputL);
EventHandler<ActionEvent> customEvent = e -> {
String in = commandEntry.getText();
if (in.equals("")) return;
Label inserted = new Label("> "+in);
inserted.setStyle("-fx-font-weight: bold");
outputL.getChildren().add(inserted);
commandEntry.setText("");
Command cmd = new Command(in, outputL);
cmd.execute(true);
output.setVvalue(1); // This does not work
};
commandEntry.setOnAction(customEvent);
exe.setOnAction(customEvent);
consoleContent.setTop(output);
consoleContent.setBottom(input);
consoleContent.setPadding(new Insets(5, 5, 5, 5));
consoleTab.setContent(consoleContent);
And this is the Command.java class:
public class Command {
private String command;
private VBox vbox;
public static final String NEW_FILE = "new_file";
public static final String OPEN_FILE = "open";
public static final String SAVE_FILE = "save";
public static final String LIST_FILES = "list";
public static final String HELP = "help";
public Command(String command, VBox v){
this.command = command;
this.vbox = v;
}
public void execute(boolean layout){
String[] args = this.command.split(" ");
String cmd = args[0];
String outputText = "";
switch (cmd){
case NEW_FILE:
break;
case OPEN_FILE:
outputText = "File opened";
break;
case SAVE_FILE:
break;
case LIST_FILES:
outputText = "Files listed";
break;
case HELP:
outputText = "Available commands:\nOPEN: open <file-name>\nLIST: list";
break;
default:
outputText = "Command not found, type help to get the list of available commands";
break;
}
if (layout){
makeLayout(outputText);
}
}
private void makeLayout(String outputText){
this.vbox.getChildren().add(new Label(outputText));
}
}
The problem is that when I call the setVvalue(1.0) method of the scrollpane, this is not setting the scrollbar at the bottom.
I have tried with using output.setContent(outputL) before output.setVvalue(1.0) but nothing changes.
Thanks for any help
Generate a layout pass before setting the scroll value. To generate a layout pass see:
Get the height of a node in JavaFX (generate a layout pass)
// change the content of the scroll pane
// . . .
// generate a layout pass on the scroll pane.
scrollPane.applyCss();
scrollPane.layout();
// scroll to the bottom of the scroll pane.
scrollPane.setVvalue(scrollPane.getVmax());
Why this works
When the layout pass occurs, the vValue of the scroll pane will change to keep the currently visible area displayed rather than the new area. If you then set the vValue to the maximum value, it will change from the value calculated in the layout pass to the maximum value, scrolling the pane to the bottom of the visible content.
Sample code
This is just a code snippet to demonstrate the approach, not an executable application.
I did test the approach with the example code in the original question, and it worked fine.
public void start(Stage stage) {
VBox content = new VBox();
final ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(content);
Button append = new Button("Append");
append.setOnAction(e -> appendToScrollPane(scrollPane));
VBox layout = new VBox(scrollPane, append);
stage.setScene(new Scene(layout));
stage.show();
}
public void appendToScrollPane(ScrollPane scrollPane) {
// ... actions which add content to the scroll pane ...
// generate a layout pass on the scroll pane.
scrollPane.applyCss();
scrollPane.layout();
// scroll to the bottom of the scroll pane.
scrollPane.setVvalue(scrollPane.getVmax());
}

TextFields inside VBox

I have a list of records and 2 types of TextFields: filled and empty. I am putting these in a VBox.
for (int i = 0; i < records.size(); i++) {
if (records.get(i).contains("NEW")) {
TextField fillField = new TextField();
vbox.getChildren().add(fillField);
} else {
TextField filledField = new TextField();
filledField.setEditable(false);
filledField.setText(records.get(i));
vbox.getChildren().add(filledField);
}
}
After this the user can fill in the free TextFields. How can I update them inside the VBox?
Then I want to check if any of them are empty(how?), in which case I will fill them with "true".
EDIT:
So I am doing this:
for (int i = 0; i < vbox.getChildren().size(); i++) {
if (((TextField) vbox.getChildren().get(i)).getText()==null) {
TextField filledField = new TextField("true");
((TextField) vbox.getChildren().get(i)).setText("true");
//System.out.println(((TextField)vbox.getChildren().get(i)).getText());
}
}
My problem is that when I am printing in the console, I do see true when the field is empty. But in my application, the field remains empty.
Do I need to update vbox or something, after I update all the fields or?
The text of a TextField only becomes null, if you set the property to this value. This is a bad idea though, since you'd need to check for null and the empty string (the latter being the result of adding some chars and deleting them from the TextField).
In this case the simplest solution would be not to do this and use String.isEmpty for the checks:
for (String record : records) {
TextField textField = new TextField();
if (!record.contains("NEW")) {
textField.setEditable(false);
textField.setText(record);
}
vbox.getChildren().add(textField);
}
for (Node child : vbox.getChildren()) {
TextField tf = (TextField) child;
if (tf.getText().isEmpty()) {
tf.setText("true");
}
}

How do I get the fit-to-content width of a JavaFX TableColumn?

When I create a JavaFX TableView, by default the columns are sized to the content. However, when I get the preferred size property of each column, it's a fixed value. How is the "content size" calculated, and is there a property that exposes it?
For example, the code below:
public class TableExample extends Application {
private static <R, V> TableColumn<R, V> makeColumn(String name, Function<R, V> value) {
TableColumn<R, V> col = new TableColumn<>(name);
col.setCellValueFactory((param) ->
new ReadOnlyObjectWrapper<>(value.apply(param.getValue())));
return col;
}
#Override
public void start(Stage stage) {
TableView<String> table = new TableView<>();
ObservableList<String> items = table.getItems();
items.addAll("Foo", "Bar", "Baz", "Qux", "Quux", "Corge", "Grault");
ObservableList<TableColumn<String, ?>> columns = table.getColumns();
columns.add(makeColumn("#", items::indexOf));
columns.add(makeColumn("value", Function.identity()));
columns.add(makeColumn("hash", Object::hashCode));
BorderPane root = new BorderPane();
root.setCenter(table);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
for (TableColumn<String, ?> col : columns) {
System.out.printf("%s: pref = %f, actual = %f%n", col.getText(), col.getPrefWidth(), col.getWidth());
}
}
}
This generates:
And outputs:
#: pref = 80.000000, actual = 28.226563
value: pref = 80.000000, actual = 55.419434
hash: pref = 80.000000, actual = 97.002930
Note that the preferred widths are all the same, whereas the actual widths are appropriate to the content.
It's not enough for me to get the actual values at a given point in time, because the columns could be resized manually or the content could change. I want to know how those values are calculated, and how I can get at the result of that calculation.

How to display number of lines textarea javafx

I´m trying to create a list with the number of lines of a textarea like in a text editor. I have done it with a VBox item and adding TextField ListCell but when I scroll in the textarea, the VBox doesn´t it . How can I do it?. This is part of code:
TextArea areaNueva = new TextArea();
areas.add(numeroTab, areaNueva);
areas.get(numeroTab).setStyle("-fx-font:15pt \"Times New Roman\";" + "-fx-focus-color: transparent;");
BorderPane bor = new BorderPane();
ObservableList<TextFieldListCell> tf = FXCollections.observableArrayList();
TextFieldListCell cell = new TextFieldListCell();
VBox b = new VBox();
cell.setPrefSize(20,1);
cell.setFont(Font.font("Times New Roman",11.35));
cell.setText("1");
tf.add(0,cell);
b.getChildren().addAll(tf);
b.setSpacing(-2);
b.setPadding(new Insets(3,0,0,0));
bor.setLeft(b);
bor.setCenter(areaNueva);
Tab tabNuevo = new Tab("Sin Titulo");
tabs.add(numeroTab, tabNuevo);
tabs.get(numeroTab).setClosable(true);
tabs.get(numeroTab).setContent(bor);
An with this I add new number of lines:
private ArrayList<ObservableList<TextFieldListCell>> lineas = new ArrayList<ObservableList<TextFieldListCell>>();
String parte = null;
int i = 1;
while ((parte = br.readLine()) != null) {
areaAUtilizar.appendText(parte + "\n");
if(i!=1){
TextFieldListCell c = new TextFieldListCell();
c.setText(Integer.toString(i));
c.setFont(Font.font("Times New Roman",11.35));
c.setPrefSize(20, 13);
lineas.get(a).add(i-1,c);
boxes.get(a).getChildren().setAll(lineas.get(a));
}
i++;
}
I solved it by removing the scroll of the textarea and the listview and put its opacity to 0, and putting both in a borderpane. After I put a scrollbar and the borderpane in a scrollpane. And for it to appear the scrollbar of the scrollpane I increased the height of the textarea and the listview when the lines of the textarea are more great than the prefheight.

Make portion of a text bold in a JavaFx Label or Text

In my JavaFx application I need to have a word or two rendered in boldface in the whole sentence. Currently the sentence is rendered as a JavaFx Label but upgrading component also would not allow me set the text as so that I can have the words "Sample" displayed in bold.
String s = "This is a <b>Sample</b> sentence"
Label label = new Label(s);
output
This is a Sample sentence
JavaFx Text also does not allow this. Is there any component where I can have a portion of the text in boldface?
I am not sure if JavaFx WebView is a good idea for rendering many small sentences in a window.
It is possible to use TextFlow container from JavaFX8.
Then you can easily add differently styled Text nodes inside it.
TextFlow flow = new TextFlow();
Text text1=new Text("Some Text");
text1.setStyle("-fx-font-weight: bold");
Text text2=new Text("Some Text");
text2.setStyle("-fx-font-weight: regular");
flow.getChildren().addAll(text1, text2);
TextFlow container will automatically wrap content Text nodes.
Since the previous answers did not include FXML code, I'll post an additional one.
As suggested by #Ernisto, you can use a TextFlow that contains Text parts, where each part can be styled differently.
Example FXML file content:
<TextFlow>
<Text text="Normal text and "/>
<Text text="bold text and " style="-fx-font-weight: bold"/>
<Text text="italic text and " style="-fx-font-style: italic"/>
<Text text="red text." style="-fx-stroke: red"/>
</TextFlow>
Output:
Update: JavaFX 8 provides new control for the rich text: TextFlow
Unfortunately there is no such feature in 2.2, although it may be included into next release.
For now you can try to use next approaches:
HBox with several Label or Text components
WebView
Canvas with several Text components drawn
public class UtilsDialog {
private static final String TAG = "UtilsDialog";
private static boolean sIsShowing = false;
public static void showDialogShowError(String title, String msg, String defaultStyle,
#Nullable String customStyle, String... styledWords) {
if (sIsShowing) return;
Stage dialogStage = new Stage(StageStyle.UTILITY);
dialogStage.initModality(Modality.APPLICATION_MODAL);
dialogStage.setWidth(400);
dialogStage.setHeight(220);
BorderPane borderPane = new BorderPane();
borderPane.setPadding(new Insets(15));
borderPane.setPrefWidth(Integer.MAX_VALUE);
borderPane.setPrefHeight(Integer.MAX_VALUE);
Scene scene = new Scene(borderPane);
dialogStage.setScene(scene);
sIsShowing = true;
dialogStage.show();
UtilsGui.closeOnEsc(borderPane, scene);
scene.addEventHandler(KeyEvent.KEY_PRESSED, t -> {
if (t.getCode() == KeyCode.ESCAPE) {
sIsShowing = false;
}
});
// Top
Text textTitle = new Text(title);
textTitle.setStyle("-fx-font-size: 18px;");
HBox hBoxTop = new HBox(10);
hBoxTop.getChildren().addAll(textTitle);
borderPane.setTop(hBoxTop);
// Center
TextFlow textFlow = new TextFlow();
List<String> words = Arrays.asList(msg.split(" "));
List<String> styledWordsList = Arrays.asList(styledWords);
for (String word : words) {
Text tmpWord = new Text(word);
if (styledWordsList.contains(word
.replace(".", "")
.replace(",", "")
.replace("?", "")
.replace("!", "")
.replace(";", "")
.replace("\n", "")
)) {
tmpWord.setStyle(customStyle);
} else {
if (defaultStyle == null) {
tmpWord.setStyle("");
} else {
tmpWord.setStyle(defaultStyle);
}
}
tmpWord.setText(tmpWord.getText());
textFlow.getChildren().add(tmpWord);
textFlow.getChildren().add(new Text(" "));
}
Text textMsg = new Text(msg);
textMsg.setStyle("-fx-font-size: 14px;");
HBox hBoxInputPane = new HBox(10);
hBoxInputPane.setAlignment(Pos.CENTER);
VBox vBoxCenter = new VBox(10);
vBoxCenter.setPadding(new Insets(25, 0, 15, 0));
vBoxCenter.getChildren().addAll(textFlow);
borderPane.setCenter(vBoxCenter);
JFXButton btnOk = new JFXButton("OK");
btnOk.setAlignment(Pos.CENTER_RIGHT);
btnOk.setStyle("-fx-text-fill: WHITE; -fx-background-color: #5264AE; -fx-font-size: 14px;");
btnOk.setOnAction(event -> {
sIsShowing = false;
dialogStage.close();
});
// Bottom
HBox hBoxBottom = new HBox();
final Pane spacer = new Pane();
HBox.setHgrow(spacer, Priority.ALWAYS);
hBoxBottom.getChildren().addAll(spacer, btnOk);
borderPane.setBottom(hBoxBottom);
// store on close
dialogStage.setOnCloseRequest(event -> sIsShowing = false);
}
}
call:
UtilsDialog.showDialogShowError("Test", "This is the message to show. Does it work?",
null, "-fx-font-weight: bold", "This", "message", "show");

Resources