I hava 3 tabs in a TabPane that each one has a text area with different texts and different length.
I want to autosize text area according to it's length in each tab.
I don't understand what should I do ? using scene builder ? css ?javaFX methods ?
Thank's in Advance ...
I think you are asking that the text areas grow or shrink according to the text that is displayed in them?
If so, see if this code helps:
import java.util.concurrent.Callable;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class AutosizingTextArea extends Application {
#Override
public void start(Stage primaryStage) {
TextArea textArea = new TextArea();
textArea.setMinHeight(24);
textArea.setWrapText(true);
VBox root = new VBox(textArea);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
// This code can only be executed after the window is shown:
// Perform a lookup for an element with a css class of "text"
// This will give the Node that actually renders the text inside the
// TextArea
Node text = textArea.lookup(".text");
// Bind the preferred height of the text area to the actual height of the text
// This will make the text area the height of the text, plus some padding
// of 20 pixels, as long as that height is between the text area's minHeight
// and maxHeight. The minHeight we set to 24 pixels, the max height will be
// the height of its parent (usually).
textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(new Callable<Double>(){
#Override
public Double call() throws Exception {
return text.getBoundsInLocal().getHeight();
}
}, text.boundsInLocalProperty()).add(20));
}
public static void main(String[] args) {
launch(args);
}
}
If you want to make this reusable, then you could consider subclassing TextArea. (In general, I dislike subclassing control classes.) The tricky part here would be to execute the code that makes the TextArea expand once it has been added to a live scene graph (this is necessary for the lookup to work). One way to do this (which is a bit of a hack, imho) is to use an AnimationTimer to do the lookup, which you can stop once the lookup is successful. I mocked this up here.
Related
I got quite simple question but I can't find answer to my case.
My goal is to "allow" only visual overflow of node and prevent resize of parent node like this:
I want to allow visual overflow of child (right) but I want to prevent resize of parent and clip it as black rectangle (left).
I am aware of setClip method but this way I get situation (#1) where child is clipped visually.
Is it possible in JavaFX to allow visual overflow?
I need it becauase I can't deal with centering of node in StackPane when nested children overflow but would be good to avoid clipping them. I want to get first situation but now i get second one:
Is there other solution to my problem?
If you set the absulute position and size of the children yourself, you can make the parent layout ignore a child by setting the managed property to false. Unmanaged nodes are not repositioned/resized by the parent and are ignored when calculating the layout bounds of the parent.
child.setManaged(false);
This is standard behavior for JavaFX Regions:
Every Region has its layout bounds, which are specified to be (0, 0, width, height). A Region might draw outside these bounds. The content area of a Region is the area which is occupied for the layout of its children. This area is, by default, the same as the layout bounds of the Region, but can be modified by either the properties of a border (either with BorderStrokes or BorderImages), and by padding. The padding can be negative, such that the content area of a Region might extend beyond the layout bounds of the Region, but does not affect the layout bounds.
Here's a short example:
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
pane.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
pane.setMaxSize(500, 300);
Rectangle rect = new Rectangle(100, 100, Color.FIREBRICK);
rect.setOnMousePressed(this::handleMousePressed);
rect.setOnMouseDragged(this::handleMouseDragged);
pane.getChildren().add(rect);
StackPane root = new StackPane(pane);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
private Point2D origin;
private void handleMousePressed(MouseEvent event) {
origin = new Point2D(event.getX(), event.getY());
event.consume();
}
private void handleMouseDragged(MouseEvent event) {
Rectangle rect = (Rectangle) event.getSource();
rect.setTranslateX(rect.getTranslateX() + event.getX() - origin.getX());
rect.setTranslateY(rect.getTranslateY() + event.getY() - origin.getY());
event.consume();
}
}
This has a Rectangle whose parent is a Pane but allows you to drag the Rectangle anywhere, even outside the bounds of the Pane.
So, I've been working on a dynamical UI, which consists of TextAreas, but the thing is that the inputs to TextAreas come from the database and therefore are with different lengths. And I must also make the TextAreas dynamic depending on the length of the strings from database. And this is a difficult task because the length of the strings doesn't automatically tell its length in pixels.
So, for example strings:
a)"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
b)"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
Those two strings consist of 70 letters but their length in pixels is completely different.
And I need to make sure that the TextArea gets its width based on the string's length in pixels.
I have tried to use something like this:
int textwidth = (int) font.getStringBounds(ta.getText(), frc).getWidth();
But it gives me errors, because the font is the following:
textLabel.getFont()
-> Font[name=System Regular, family=System, style=Regular, size=12.0]
But using this font in the previous getStringBounds method it gives me errors:
Cannot resolve method 'getStringBounds(java.lang.String, java.awt.font.FontRenderContext)'
Any help would be highly appriciated. I can provide more information if required.
Thanks in advance!
You can measure the size of some text by creating a Text object, placing it in a pane (e.g. a StackPane) and calling layout() on the pane, then get the layout bounds of the text. Set the font to the same font as you want to use in the text area.
The only remaining issue is that the text area needs some padding for its border, etc, the following code example just uses a fixed padding (established via trial-and-error) but works well enough. You can probably improve on this if needed.
Type something in the text field and press enter; it will update the text and size of the text area:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class SizeTextAreaToString extends Application {
#Override
public void start(Stage primaryStage) {
TextField enterField = new TextField();
TextArea textArea = new TextArea();
textArea.setPrefRowCount(1);
enterField.setOnAction(e -> sizeTextAreaToText(textArea, enterField.getText()));
VBox root = new VBox(5, enterField, textArea);
VBox.setVgrow(textArea, Priority.NEVER);
root.setPadding(new Insets(5));
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
private void sizeTextAreaToText(TextArea textArea, String text) {
Text t = new Text(text);
t.setFont(textArea.getFont());
StackPane pane = new StackPane(t);
pane.layout();
double width = t.getLayoutBounds().getWidth();
double padding = 20 ;
textArea.setMaxWidth(width+padding);
textArea.setText(text);
}
public static void main(String[] args) {
launch(args);
}
}
In my application I have a BorderPane which contains a ScrollPane in the center. The ScrollPane has two logical sections top and bottom.
When I create a stage to show the BorderPane I want the stage to be sized so that only the top section of ScrollPane is shown and the user can scroll down to see the bottom section if they need it.
So I try to bind the prefHeight property of the ScrollPane to the prefHeight property of the top section. Hoping that the ScrollPane will get the right height and the stage will look "Just Rightâ„¢". For some reason this fails miserably. But when I attach a listener to the prefHeight property it does indeed return the correct height. So I'm flabbergasted as to what is going on.
Interestingly, if I remove the BorderPane it works in the SSCCE but this is not an option in the real case.
EDIT: SSCCE updated
Here is a SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SSCCE extends Application {
#Override
public void start(Stage aStage) throws Exception {
VBox top = new VBox(new ComboBox<>(), new ComboBox<>(), new ComboBox<>());
top.setStyle("-fx-background-color: pink");
top.heightProperty().addListener((aObs, aOld, aNew)->{
System.out.println(aNew.doubleValue());
});
Region regionBottom = new Region();
regionBottom.setPrefHeight(100);
regionBottom.setPrefWidth(200);
regionBottom.setStyle("-fx-background-color: red");
VBox content = new VBox(top, regionBottom);
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setFitToWidth(true);
scrollPane.prefHeightProperty().bind(top.prefHeightProperty());
BorderPane root = new BorderPane(scrollPane);
root.setTop(new MenuBar(new Menu("Foo")));
Scene scene = new Scene(root);
aStage.setScene(scene);
aStage.sizeToScene();
aStage.show();
aStage.toFront();
}
public static void main(String[] args) {
launch();
}
}
Actual result:
Expected result:
The prefHeightProperty defaults to a sentinel value, indicating that the pref height should be computed. (Region.USE_COMPUTED_SIZE). Thus when you bind the prefHeight of the scroll pane, you effectively instruct it to do the same, which basically means it will get its default size.
Persuading the layout to be performed and measured in order to get the correct size for the scroll pane's preferred height is a bit of a hack. The correct layout sizes depend on CSS, so you must first force the CSS to be applied, and then you can ask for the computed pref height of the top area. You can do the latter with a call to
top.prefHeight(-1);
So the following works:
ScrollPane scrollPane = new ScrollPane(content);
scrollPane.setFitToWidth(true);
// scrollPane.prefHeightProperty().bind(top.heightProperty());
BorderPane root = new BorderPane(scrollPane);
root.setTop(new MenuBar(new Menu("Foo")));
Scene scene = new Scene(root);
aStage.setScene(scene);
root.applyCss();
scrollPane.setPrefHeight(top.prefHeight(-1));
root.requestLayout();
aStage.sizeToScene();
aStage.show();
aStage.toFront();
(Note the call to aStage.sizeToScene(); is not needed: this is the default behavior anyway.)
Is there any way to get the current size of a TableView column? I've haven't even been able to find the question online which makes me think I've missed something since I can't be the first one to need this functionality.
If that isn't possible, is there any way to set the size of a TableView column? That would also solve my issue although I'd prefer getting the size. setFixedCellSize(double) looked promising, but I couldn't get it to work.
I want to have a TextField above each column in my TableView with the same size as the column it's above. If there's a better way to accomplish that, I'm open to suggestions.
You can use Property-Bindings. But the width-Property of a TableColumn or TextField is read-only. This is correct, since the width and height is part of the layout process in rendering the whole window.
So you need to set the three sizes, min - pref - max width, for the TextField with the currently width from the TableColumn. It seems to me the prefered way to take the TableColumns width as the master for the TextFields width.
Now, even on manually resizing, your TextField stays the same width as the "bound" TableColumns width.
A little Minimal, Complete, and Verifiable example is below:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableTest extends Application {
#Override
public void start(Stage primaryStage) {
TextField field = new TextField();
TableView<String> table = new TableView<>();
TableColumn<String, String> column = new TableColumn("Header Text");
table.getColumns().add(column);
field.prefWidthProperty().bind(column.widthProperty());
field.minWidthProperty().bind(column.widthProperty());
field.maxWidthProperty().bind(column.widthProperty());
VBox root = new VBox();
root.getChildren().addAll(field, table);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Is it possible so set the origin of a Pane to the bottom-left corner (instead of top-left)? I'm adding many shapes to my canvas which are defined within the "mathematical coordinate system".
I thought there is perhaps an easier way than always substract the height from the y-coordinate. Another advantage would be that I don't have to take care of resizing the pane.
If all you are doing is using shapes, you can apply a reflection to the pane. You can represent a reflection as a Scale with x=1 and y=-1. The only tricky part is that you must keep the pivot of the scale at the vertical center, which needs a binding in case the pane changes size.
If you're putting controls, or text, in the pane, then those will also be reflected, so they won't look correct. But this will work if all you are doing is using shapes.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;
public class ReflectedPaneTest extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Scale scale = new Scale();
scale.setX(1);
scale.setY(-1);
scale.pivotYProperty().bind(Bindings.createDoubleBinding(() ->
pane.getBoundsInLocal().getMinY() + pane.getBoundsInLocal().getHeight() /2,
pane.boundsInLocalProperty()));
pane.getTransforms().add(scale);
pane.setOnMouseClicked(e ->
System.out.printf("Mouse clicked at [%.1f, %.1f]%n", e.getX(), e.getY()));
primaryStage.setScene(new Scene(pane, 600, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}