Reputation: 2277
I am developing a application where I need some widgets to wrap up inside a panel. And I want to put a border around it. I am a swt programmer where in composite I can pass swt.border to put border. But how to do that in JavaFX. Any help on this is appreciated.
CODE:
Label Center=new Label();
Center.setText("Center Frequency");
GridPane.setConstraints(Center, 0, 0);
tb1[i].getChildren().add(Center);
TextField text=new TextField();
GridPane.setConstraints(text, 1, 0);
tb1[i].getChildren().add(text);
Label chiprate=new Label();
chiprate.setText("Chiprate");
GridPane.setConstraints(chiprate, 0, 1);
tb1[i].getChildren().add(chiprate);
TextField chip=new TextField();
GridPane.setConstraints(chip, 1, 1);
tb1[i].getChildren().add(chip);
Label frequency=new Label();
frequency.setText("Frequency deviation");
GridPane.setConstraints(frequency, 0, 2);
tb1[i].getChildren().add(frequency);
TextField frequencydeviation=new TextField();
GridPane.setConstraints(frequencydeviation, 1, 2);
tb1[i].getChildren().add(frequencydeviation);
Label outputLabel=new Label();
outputLabel.setText("Output Power");
GridPane.setConstraints(outputLabel, 0, 3);
tb1[i].getChildren().add(outputLabel);
TextField output=new TextField();
GridPane.setConstraints(output, 1, 3);
tb1[i].getChildren().add(output);
Upvotes: 10
Views: 44405
Reputation: 87
I have created TitledBorderedPane class that extends StackPane. I have used Shape objects to create Rectangle and Text. The class contains a container StackPane object that holds the contents. The following code is self-explanatory.
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Text;
public class TitledBorderedPane extends StackPane{
private final StackPane container;
private final Group shapeGroup = new Group();
private final Rectangle rect;
private final Rectangle rectRemove;
private final Text title;
public TitledBorderedPane(String titleText) {
container = new StackPane();
//Rectangle to create border
//Initial size of the rectangle is not important
//as it is set with action listener.
rect = new Rectangle(0, 0, 200, 200);
rect.setStroke(Color.BLACK);
rect.setFill(null);
rect.setStrokeWidth(1);
rect.setArcWidth(10.0);
rect.setArcHeight(10.0);
//Border title text
title = new Text(20.0, 4.0, titleText);
//Rectangle to remove part of the border to fit the text
rectRemove = new Rectangle(15, -2, title.getBoundsInLocal().getWidth() + 10, 5);
//Set padding of the container, so the content of the container
//does not overlap the border
container.setPadding(new Insets(title.getBoundsInLocal().getHeight() + 5));
super.getChildren().addAll(shapeGroup, container);
//Add action listeners to width and height properties
widthProperty().addListener(
(observable, oldvalue, newvalue) -> {
rect.setWidth((double)newvalue - title.getBoundsInLocal().getHeight());
setShape();
}
);
heightProperty().addListener(
(observable, oldvalue, newvalue) -> {
rect.setHeight((double)newvalue - title.getBoundsInLocal().getHeight());
setShape();
}
);
//The style should not be used here. I have used it only for the example.
setStyle("-fx-background-color: rgba(0, 100, 100, 0.5); -fx-background-radius: 0;");
}
private void setShape() {
Shape shape = Shape.subtract(rect, rectRemove);
shape.setSmooth(true);
shapeGroup.getChildren().clear();
shapeGroup.getChildren().addAll(shape, title);
}
/**
* Returns ObservableList of the container pane
*
* @return
*/
@Override
public ObservableList<Node> getChildren() {
return container.getChildren();
}
}
The class can be used as a normal StackPane. It gives the following result.
Upvotes: 1
Reputation: 159576
I create a BorderedTitledPane
class which places a titled border around content.
If you don't need to have a title placing a border around things is even easier - just set the the css border parameters on a region (e.g. -fx-border-color: black;
).
Here is a complete executable sample.
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
/** Places content in a bordered pane with a title. */
public class BorderedTitledPane extends StackPane {
private StringProperty title = new SimpleStringProperty();
private ObjectProperty<Node> graphic = new SimpleObjectProperty<>();
private ObjectProperty<Node> content = new SimpleObjectProperty<>();
private ObjectProperty<Pos> titleAlignment = new SimpleObjectProperty<>();
// todo other than TOP_LEFT other alignments aren't really supported correctly, due to translation fudge for indentation of the title label in css => best to implement layoutChildren and handle layout there.
// todo work out how to make content the default node for fxml so you don't need to write a <content></content> tag.
public BorderedTitledPane() {
this("", null);
}
public BorderedTitledPane(String titleString, Node contentNode) {
final Label titleLabel = new Label();
titleLabel.textProperty().bind(Bindings.concat(title, " "));
titleLabel.getStyleClass().add("bordered-titled-title");
titleLabel.graphicProperty().bind(graphic);
titleAlignment.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
StackPane.setAlignment(titleLabel, titleAlignment.get());
}
});
final StackPane contentPane = new StackPane();
getStyleClass().add("bordered-titled-border");
getChildren().addAll(titleLabel, contentPane);
content.addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (content.get() == null) {
contentPane.getChildren().clear();
} else {
if (!content.get().getStyleClass().contains("bordered-titled-content")) {
content.get().getStyleClass().add("bordered-titled-content"); // todo would be nice to remove this style class when it is no longer required.
}
contentPane.getChildren().setAll(content.get());
}
}
});
titleAlignment.set(Pos.TOP_LEFT);
this.title.set(titleString);
this.content.set(contentNode);
}
public String getTitle() {
return title.get();
}
public StringProperty getTitleStringProperty() {
return title;
}
public void setTitle(String title) {
this.title.set(title);
}
public Pos getTitleAlignment() {
return titleAlignment.get();
}
public ObjectProperty<Pos> titleAlignmentProperty() {
return titleAlignment;
}
public void setTitleAlignment(Pos titleAlignment) {
this.titleAlignment.set(titleAlignment);
}
public Node getContent() {
return content.get();
}
public ObjectProperty<Node> contentProperty() {
return content;
}
public void setContent(Node content) {
this.content.set(content);
}
public Node getGraphic() {
return graphic.get();
}
public ObjectProperty<Node> graphicProperty() {
return graphic;
}
public void setGraphic(Node graphic) {
this.graphic.set(graphic);
}
}
Related CSS.
.bordered-titled-title {
-fx-translate-x: 8;
-fx-translate-y: -10;
-fx-padding: 0 0 0 4;
-fx-background-color: -fx-background;
}
.bordered-titled-border {
-fx-content-display: top;
-fx-border-insets: 2 0 0 0;
-fx-border-color: -fx-text-box-border;
-fx-border-width: 2;
}
.bordered-titled-content {
-fx-padding: 18 5 5 5;
}
Upvotes: 20