Reputation: 383
Swing has a feature to enlarge tablecells on mouse over. For example, a cell without mouse-over ( see second row ):
shows on mouse-over like this:
Is it possible to implement something similar in JavaFx TableView TableCell?
Update: This code is something which may work. What I need is to find out the real string width, something like in this example, but for TableCell: sample
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import java.util.List;
public class ExpandingLabelApp2 extends Application {
@Override
public void start(Stage stage) {
TableView<Lines> tableView = new TableView<>(
createTestData()
);
TableColumn<Lines, String> col1 = new TableColumn<>(
"Instruction 1"
);
col1.setCellValueFactory(p ->
readOnlyStringProperty(p.getValue().line1)
);
col1.setCellFactory(p ->
new TooltipTableCell<>()
);
col1.setMaxWidth(120);
TableColumn<Lines, String> col2 = new TableColumn<>(
"Instruction 2"
);
col2.setCellValueFactory(p ->
readOnlyStringProperty(p.getValue().line2)
);
tableView.getColumns().setAll(
List.of(col1, col2)
);
tableView.setPrefSize(500, 200);
stage.setScene(new Scene(tableView));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private static ReadOnlyStringProperty readOnlyStringProperty(String string) {
return new ReadOnlyStringWrapper(string).getReadOnlyProperty();
}
private static ObservableList<Lines> createTestData() {
ObservableList<Lines> lines = FXCollections.observableArrayList();
for (int i = 0; i < TROUBLE.size(); i += 2) {
String line1 = TROUBLE.get(i);
String line2 = i+1 < TROUBLE.size() ? TROUBLE.get(i+1) : "";
lines.add(new Lines(line1, line2));
}
return lines;
}
private static final List<String> TROUBLE = """
Double, double toil and trouble;
Fire burn and caldron bubble.
Fillet of a fenny snake,
In the caldron boil and bake;
Eye of newt and toe of frog,
Wool of bat and tongue of dog,
Adder's fork and blind-worm's sting,
Lizard's leg and howlet's wing,
For a charm of powerful trouble,
Like a hell-broth boil and bubble.
Double, double toil and trouble;
Fire burn and caldron bubble.
Cool it with a baboon's blood,
Then the charm is firm and good.
""".lines().toList();
record Lines(String line1, String line2) {}
public class TooltipTableCell<S> extends TableCell<S, String> {
public TooltipTableCell() {
super();
setOnMouseEntered(ev ->{
//Node text = lookup(".text");
//setPrefWidth(text != null ? text.getBoundsInLocal().getHeight() : 1200);
setManaged(false);
setPrefWidth( 1200);
});
setOnMouseExited(e -> {
if ( !isManaged()){
setManaged( true );
setPrefWidth( USE_COMPUTED_SIZE );
}
});
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setTooltip(null);
} else {
setText(item);
}
}
}
}
Upvotes: 1
Views: 81
Reputation: 383
I found a solution for this problem. Is not the perfect one, but close to my request. The solution with the tooltip is also fine, but this has the advantage for not duplicating the text on the screen and being more consistent. Here is the code:
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.util.List;
public class ExpandingLabelApp2 extends Application {
@Override
public void start(Stage stage) {
TableView<Lines> tableView = new TableView<>(
createTestData()
);
TableColumn<Lines, String> col1 = new TableColumn<>(
"Instruction 1"
);
col1.setCellValueFactory(p ->
readOnlyStringProperty(p.getValue().line1)
);
col1.setCellFactory(p ->
new TooltipTableCell<>()
);
TableColumn<Lines, String> col2 = new TableColumn<>(
"Instruction 2"
);
col2.setCellValueFactory(p ->
readOnlyStringProperty(p.getValue().line2)
);
tableView.getColumns().setAll(
List.of(col1, col2)
);
tableView.setPrefSize(500, 200);
stage.setScene(new Scene(tableView));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private static ReadOnlyStringProperty readOnlyStringProperty(String string) {
return new ReadOnlyStringWrapper(string).getReadOnlyProperty();
}
private static ObservableList<Lines> createTestData() {
ObservableList<Lines> lines = FXCollections.observableArrayList();
for (int i = 0; i < TROUBLE.size(); i += 2) {
String line1 = TROUBLE.get(i);
String line2 = i+1 < TROUBLE.size() ? TROUBLE.get(i+1) : "";
lines.add(new Lines(line1, line2));
}
return lines;
}
private static final List<String> TROUBLE = """
Double, double toil and trouble;
Fire burn and caldron bubble.
Fillet of a fenny snake,
In the caldron boil and bake;
Eye of newt and toe of frog,
Wool of bat and tongue of dog,
Adder's fork and blind-worm's sting,
Lizard's leg and howlet's wing,
For a charm of powerful trouble,
Like a hell-broth boil and bubble.
Double, double toil and trouble;
Fire burn and caldron bubble.
Cool it with a baboon's blood,
Then the charm is firm and good.
""".lines().toList();
record Lines(String line1, String line2) {}
public static class TooltipTableCell<S> extends TableCell<S, String> {
public TooltipTableCell() {
super();
setOnMouseEntered(ev ->{
Text text = new Text(getText());
text.setFont( getFont() );
double textNodeWidth = text.getLayoutBounds().getWidth() + 10;
if ( textNodeWidth > getWidth() ) {
setManaged(false);
setPrefWidth( textNodeWidth );
}
});
setOnMouseExited(e -> {
if ( !isManaged()){
setManaged( true );
setPrefWidth( USE_COMPUTED_SIZE );
}
});
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setTooltip(null);
} else {
setText(item);
}
}
}
}
Upvotes: 2
Reputation: 159291
A solution using a ToolTip
, it is not exactly what you want, but for a lot of people wanting similar functionality it might be fine.
This solution creates a custom cell factory. In the cell factory, a Tooltip is installed on the cell. The Tooltip shows the full text on hover.
TooltipTableCell.java
import javafx.scene.control.TableCell;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;
public class TooltipTableCell<S> extends TableCell<S, String> {
private final Tooltip tooltip = new Tooltip();
public TooltipTableCell() {
super();
tooltip.setShowDelay(Duration.ZERO);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
tooltip.setText(null);
setTooltip(null);
} else {
setText(item);
tooltip.setText(item);
setTooltip(tooltip);
}
}
}
Potential customizations
There might be some additional customizations you may wish to make such as:
I don't provide any potential modifications in this answer.
Example Usage
Mouse over the text in the first column of the table to see a tooltip displaying the full text of the cell.
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import java.util.List;
public class ExpandingLabelApp extends Application {
@Override
public void start(Stage stage) {
TableView<Lines> tableView = new TableView<>(
createTestData()
);
TableColumn<Lines, String> col1 = new TableColumn<>(
"Instruction 1"
);
col1.setCellValueFactory(p ->
readOnlyStringProperty(p.getValue().line1)
);
col1.setCellFactory(p ->
new TooltipTableCell<>()
);
col1.setMaxWidth(120);
TableColumn<Lines, String> col2 = new TableColumn<>(
"Instruction 2"
);
col2.setCellValueFactory(p ->
readOnlyStringProperty(p.getValue().line2)
);
tableView.getColumns().setAll(
List.of(col1, col2)
);
tableView.setPrefSize(500, 200);
stage.setScene(new Scene(tableView));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private static ReadOnlyStringProperty readOnlyStringProperty(String string) {
return new ReadOnlyStringWrapper(string).getReadOnlyProperty();
}
private static ObservableList<Lines> createTestData() {
ObservableList<Lines> lines = FXCollections.observableArrayList();
for (int i = 0; i < TROUBLE.size(); i += 2) {
String line1 = TROUBLE.get(i);
String line2 = i+1 < TROUBLE.size() ? TROUBLE.get(i+1) : "";
lines.add(new Lines(line1, line2));
}
return lines;
}
private static final List<String> TROUBLE = """
Double, double toil and trouble;
Fire burn and caldron bubble.
Fillet of a fenny snake,
In the caldron boil and bake;
Eye of newt and toe of frog,
Wool of bat and tongue of dog,
Adder's fork and blind-worm's sting,
Lizard's leg and howlet's wing,
For a charm of powerful trouble,
Like a hell-broth boil and bubble.
Double, double toil and trouble;
Fire burn and caldron bubble.
Cool it with a baboon's blood,
Then the charm is firm and good.
""".lines().toList();
record Lines(String line1, String line2) {}
}
Alternate Implementation
Create a custom skin for a label which handles a new popover behavior or overrun style.
The label skin code is in LabeledSkinBase
. I think I created a feature request for similar behavior sometime in the past or mentioned it on a developer forum. The controls developer at the time responded that it sounded like a good feature. Still, I don't think it was ever implemented.
This is not a trivial implementation option. It is something I would only recommend for experienced JavaFX developers and I do not attempt to do it here.
Upvotes: 4