JavaFX: setting background color for Text controls - text

I'm using a TextFlow and some Text items to show a styled text, but i cant find a way to set a simple background color for the Text items.
I can set the fill color and font but it does not have a java method or css property that sets its background color.

Based on this solution, this is a quick implementation of a method to provide background coloring for all the Text nodes within a FlowPane, using CSS and the ability to set a series of paint values separated by commas (as much as Text items) and insets for each one of them:
private FlowPane flow;
private Scene scene;
#Override
public void start(Stage primaryStage) {
Text text0 = new Text("These are several ");
Text text1 = new Text("Text Nodes ");
Text text2 = new Text("wrapped in ");
Text text3 = new Text("a FlowPane");
text0.setFill(Color.WHEAT);
text0.setFont(new Font("Times New Roman", 20));
text1.setFill(Color.WHITE);
text1.setFont(new Font("Verdana", 32));
text2.setFill(Color.WHITESMOKE);
text2.setFont(new Font("Arial", 24));
text3.setFill(Color.WHITESMOKE);
text3.setFont(new Font("Arial", 18));
flow = new FlowPane(text0, text1, text2, text3);
scene = new Scene(flow, 300, 200);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
setBackgroundColors();
flow.needsLayoutProperty().addListener((obs,d,d1)->setBackgroundColors());
}
private void setBackgroundColors(){
final Bounds out = flow.getBoundsInLocal();
final StringBuilder sbColors = new StringBuilder();
final StringBuilder sbInsets = new StringBuilder();
AtomicInteger cont = new AtomicInteger();
flow.getChildrenUnmodifiable().forEach(n->{
sbColors.append("hsb(")
.append((((double)cont.get())/((double)flow.getChildren().size()))*360d)
.append(", 60%, 90%)");
Bounds b = ((Text)n).getBoundsInParent();
sbInsets.append(b.getMinY()).append(" ");
sbInsets.append(Math.min(scene.getWidth(),out.getMaxX())-b.getMaxX()).append(" ");
sbInsets.append(Math.min(scene.getHeight(),out.getMaxY())-b.getMaxY()).append(" ");
sbInsets.append(b.getMinX());
if(cont.getAndIncrement()<flow.getChildren().size()-1){
sbColors.append(", ");
sbInsets.append(", ");
}
});
flow.setStyle("-fx-background-color: "+sbColors.toString()+"; -fx-background-insets: "+sbInsets.toString()+";");
}
This will lead to this:
and after resizing the scene:
EDIT
Based on the OP request of using a TextFlow layout instead of a FlowPane, since Text nodes can be spanned over several lines within a TextFlow, the given solution will no longer be valid, as the bounding box of each text node will overlap others.
As a workaround, we can split the Text nodes in single word Text nodes, while keeping the same background color for those in the same original phrase.
I won't go into the splitting logic, but I will add a list of indices, where each index maps the text node with its index of background color.
private FlowPane flow;
private Scene scene;
private final List<Integer> indices=Arrays.asList(0,0,0,1,1,2,2,3,3);
#Override
public void start(Stage primaryStage) {
List<Text> text0 = Arrays.asList(new Text("These "), new Text("are "), new Text("several "));
List<Text> text1 = Arrays.asList(new Text("Text "), new Text("Nodes "));
List<Text> text2 = Arrays.asList(new Text("wrapped "), new Text("in "));
List<Text> text3 = Arrays.asList(new Text("a "), new Text("FlowPane"));
text0.forEach(t->t.setFill(Color.WHEAT));
text0.forEach(t->t.setFont(new Font("Times New Roman", 20)));
text1.forEach(t->t.setFill(Color.WHITE));
text1.forEach(t->t.setFont(new Font("Verdana", 32)));
text2.forEach(t->t.setFill(Color.WHITESMOKE));
text2.forEach(t->t.setFont(new Font("Arial", 24)));
text3.forEach(t->t.setFill(Color.WHITESMOKE));
text3.forEach(t->t.setFont(new Font("Arial", 18)));
flow = new FlowPane();
flow.getChildren().addAll(text0);
flow.getChildren().addAll(text1);
flow.getChildren().addAll(text2);
flow.getChildren().addAll(text3);
scene = new Scene(flow, 300, 200);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
setBackgroundColors();
flow.needsLayoutProperty().addListener((obs,d,d1)->setBackgroundColors());
}
private void setBackgroundColors(){
final Bounds out = flow.getBoundsInLocal();
final StringBuilder sbColors = new StringBuilder();
final StringBuilder sbInsets = new StringBuilder();
AtomicInteger cont = new AtomicInteger();
flow.getChildrenUnmodifiable().forEach(n->{
sbColors.append("hsb(")
.append((double)indices.get(cont.get())/(double)(indices.get(flow.getChildren().size()-1)+1)*360d)
.append(", 60%, 90%)");
Bounds b = ((Text)n).getBoundsInParent();
sbInsets.append(b.getMinY()).append(" ");
sbInsets.append(Math.min(scene.getWidth(),out.getMaxX())-b.getMaxX()-1).append(" ");
sbInsets.append(Math.min(scene.getHeight(),out.getMaxY())-b.getMaxY()).append(" ");
sbInsets.append(b.getMinX());
if(cont.getAndIncrement()<flow.getChildren().size()-1){
sbColors.append(", ");
sbInsets.append(", ");
}
});
flow.setStyle("-fx-background-color: "+sbColors.toString()+"; -fx-background-insets: "+sbInsets.toString()+";");
}
This FlowPane now behaves as a TextFlow:

There is no background for Text objects. You'd either have to group it with a shape (rectangle, ellipse, etc) and set the color of that shape, or you could put the objects inside a StackPane and set the background color of the StackPane.

Related

Control image placement on JTextPane

I am able to add ImageIcons to a JTextPane, but when I add them they show up in the center of the JTextPane. I can't find a way to control where they are placed on the JTextPane. Can someone please help me with this?
This method is making the JTextPane:
private void loadTextPanel(JPanel contentPane) {
chatLogPanel = new JPanel();
chatLogPanel.setLayout(null);
EmptyBorder eb = new EmptyBorder(new Insets(10, 10, 10, 10));
DefaultStyledDocument document = new DefaultStyledDocument();
chatLog = new JTextPane(document);
chatLog.setEditorKit(new WrapEditorKit());
chatLog.setBorder(eb);
chatLog.setMargin(new Insets(5, 5, 5, 5));
chatLogScrollPane = new JScrollPane(chatLog);
addComponent(chatLogPanel, chatLogScrollPane, 0, 0, 500, 240);
addComponent(contentPane, chatLogPanel, 0, 40, 500, 240);
}
This is the code I'm using to add a string to the Panel:
private static void appendToChatLog(JTextPane tp, String msg, Color c) {
chatLog.setEditable(true);
StyleContext sc = StyleContext.getDefaultStyleContext();
AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, c);
aset = sc.addAttribute(aset, StyleConstants.FontFamily, "Lucida Console");
aset = sc.addAttribute(aset, StyleConstants.Alignment, Integer.valueOf(3));
int len = tp.getDocument().getLength();
tp.setCaretPosition(len);
tp.setCharacterAttributes(aset, false);
tp.replaceSelection(msg);
chatLog.setEditable(false);
}
And this is what I'm currently using to add the image to the JTextPane:
BufferedImage image = generateBufferedImage(message.getImage());
Icon icon = new ImageIcon(image);
StyleContext context = new StyleContext();
StyledDocument document = (StyledDocument) chatLog.getDocument();
Style labelStyle = context.getStyle(StyleContext.DEFAULT_STYLE);
JLabel label = new JLabel(icon);
StyleConstants.setComponent(labelStyle, label);
try {
document.insertString(document.getLength(), "Ignored", labelStyle);
} catch (BadLocationException badLocationException) {
badLocationException.printStackTrace();
}
To insert a component to a JTextPane, and display it like a character, use the insertComponent method.
To insert an Icon instead, use the insertIcon method.
Quite intuitive isn't it ;)

TextureRegion cuts texture in a wrong way

I want to have a background texture with 3 rectangles and i want to create animation with them, - texture
But first rectangle cuts in a proper way and two others are cut in a dumb way
Proper way,
Dumb way #1,
Dumb way #2
Here is my code.
public class MainMenu implements Screen{
Texture background_main;
TextureRegion[] background_textures;
Animation background_animation;
SpriteBatch batch;
TextureRegion current_frame;
float stateTime;
BobDestroyer game;
OrthographicCamera camera;
public MainMenu(BobDestroyer game){
this.game = game;
}
#Override
public void show() {
camera = new OrthographicCamera();
camera.setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch = new SpriteBatch();
background_main = new Texture(Gdx.files.internal("main_menu_screen/Background.png"));
background_textures = new TextureRegion[3];
for(int i = 0; i<3; i++){
background_textures[i] = new TextureRegion(background_main,0, 0+72*i, 128, 72+72*i);
}
background_animation = new Animation(2f,background_textures);
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
current_frame = background_animation.getKeyFrame(stateTime, true);
batch.begin();
batch.draw(current_frame,0, 0,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
batch.end();
}
}
If I understand you correctly, are you trying to create 3 TextureRegions of the same width/height? If yes, your issue may be with:
new TextureRegion(background_main,0, 0+72*i, 128, 72+72*i)
I think you'd want:
new TextureRegion(background_main,0, 0+72*i, 128, 72)
As the 128x72 is the width/height (not x/y positions) of your next TextureRegions, and do you not want them all the same height (72) as opposed to varying heights (72+72*i)?

JavaFX - Create semi circle with start and end angle

How to create semi circle with adjustable start and end angle in JavaFX. I tried to use Arc or ArcTo but it never gives me what I need. Is there any easy solution to create it?
How it should look like:
A quick solution would be to create an outer circle, an inner circle, 2 lines and use Shape.subtract to create a new shape:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
double dist = 10;
double outerRadius = 100;
double innerRadius = 50;
Circle outerCircle = new Circle();
outerCircle.setRadius(outerRadius);
Circle innerCircle = new Circle();
innerCircle.setRadius(innerRadius);
Line topLeftBottomRightLine = new Line(-outerRadius, -outerRadius, outerRadius, outerRadius);
topLeftBottomRightLine.setStrokeWidth(dist);
Line bottomLeftTopRightLine = new Line(-outerRadius, outerRadius, outerRadius, -outerRadius);
bottomLeftTopRightLine.setStrokeWidth(dist);
Shape shape = Shape.subtract(outerCircle, innerCircle);
shape = Shape.subtract(shape, topLeftBottomRightLine);
shape = Shape.subtract(shape, bottomLeftTopRightLine);
shape.setStroke(Color.BLUE);
shape.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.3));
shape.relocate(300, 100);
root.getChildren().addAll(shape);
Scene scene = new Scene(root, 800, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Similarly you can create only parts of the shape by using an arc and subtracting an inner circle from it.

Android: Draw a custom view programmatically

I am developing an application in which i am showing a image followed by text followed by image again horizontally in table layout.
I am creating the table layout programmatically as:
for(i = 0; i < arrayList.size(); i++){
/* Find Tablelayout defined in main.xml */
TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
tableLayout.setStretchAllColumns(true);
/* Create a new row to be added. */
TableRow tableRow = new TableRow(this);
tableRow.setId(i);
tableRow.setClickable(true);
tableRow.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
v.setBackgroundColor(Color.GRAY);
}
});
tableRow.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.MATCH_PARENT));
/* Create a Button to be the row-content. */
ImageView imageView1 = new ImageView(this);
if(arrayList.get(i).getImage().equalsIgnoreCase("Y")){
// setImage
}
imageView1.setPadding(5, 5, 5, 5);
imageView1.setLayoutParams(new TableRow.LayoutParams(100, 100));
tableRow.addView(imageView1);
TextView textViewName = new TextView(this);
textViewName.setText(arrayList.get(i).getName());
textViewName.setPadding(5, 5, 5, 5);
textViewName.setGravity(Gravity.CENTER_VERTICAL);
textViewName.setTextSize(15);
textViewName.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.MATCH_PARENT,7));
tableRow.addView(textViewName);
ImageView imageView2 = new ImageView(this);
imageView2.setImageDrawable(getResources().getDrawable(R.drawable.icon));
imageView2.setPadding(25, 25, 25, 25);
imageView2.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.MATCH_PARENT,1));
tableRow.addView(imageView2);
tableLayout.addView(tableRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.MATCH_PARENT));
View horizontalLine = new View(this);
horizontalLine.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, 1));
horizontalLine.setBackgroundColor(Color.rgb(50, 50, 50));
tableLayout.addView(horizontalLine);
}
With the help of this i am getting the out put as image 1.
But i need the out put as image 2.
The difference between image 1 and 2 is the red line. I am getting the line with the help of drawing view but that covers whole width. I need a line which is as same as red in image 2. In center and of fix width.
Please suggest me what changes or steps i follow. Need your valuable suggestion.
You can set the color using setBackgroundColor method.
horizontalLine.setBackgroundColor(Color.RED);
Edit :
You can horizontal line of fixed length and in center.
Add the TableRow
Define layout weight for View
Add the View in TableRow
Add the tableRow in Table layout
View horizontalLine = new View(this);
// Set weight
TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, 10,0.7f);
horizontalLine.setLayoutParams(params);
TableRow tr = new TableRow(this);
TableLayout.LayoutParams tableRowParams= new TableLayout.LayoutParams
(TableLayout.LayoutParams.MATCH_PARENT,TableLayout.LayoutParams.WRAP_CONTENT);
// Set margin
int leftMargin=20;
int topMargin=2;
int rightMargin=20;
int bottomMargin=2;
tableRowParams.setMargins(leftMargin, topMargin, rightMargin, bottomMargin);
tr.setLayoutParams(tableRowParams);
// Add View in tr
tr.addView(horizontalLine);
//Add tr in Table
tableLayout.addView(tr);
You can see the output :
Hope it helps ツ

Programmatically display a message next to text field in JavaFX

I have a TextField in my JavaFX application. I want to programmatically display a message on the right side of the text (like the validation message). I thought of using Popup and setting the Label with message in that Popup. But I'm not sure how I can position this to the right side of the text field. Below is the sample code for this. Can you please help me with this?
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX Welcome");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text scenetitle = new Text("Welcome");
scenetitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20));
grid.add(scenetitle, 0, 0, 2, 1);
Label userName = new Label("User Name:");
grid.add(userName, 0, 1);
TextField userTextField = new TextField();
grid.add(userTextField, 1, 1);
Label pw = new Label("Password:");
grid.add(pw, 0, 2);
PasswordField pwBox = new PasswordField();
grid.add(pwBox, 1, 2);
Label label=new Label();
label.setText("This is an error message");
final Text actiontarget = new Text();
grid.add(actiontarget, 1, 6);
final Popup popup = new Popup();
popup.getContent().add(label);
//Want to display this popup to the right of the userTextField.
Scene scene = new Scene(grid, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
After showing the stage, do
Bounds userTextFieldBounds = userTextField.getBoundsInLocal();
Point2D popupLocation = userTextField.localToScreen(userTextFieldBounds.getMaxX(), userTextFieldBounds.getMinY());
popup.show(userTextField, popupLocation.getX(), popupLocation.getY());
The localToScreen(...) method was introduced in Java 8; if you are in an earlier version you will need
Bounds userTextFieldBounds = userTextField.getBoundsInLocal();
Point2D popupLocation = userTextField.localToScene(userTextFieldBounds.getMaxX(), userTextFieldBounds.getMinY());
popup.show(userTextField,
popupLocation.getX()+scene.getX()+primaryStage.getX(),
popupLocation.getY()+scene.getY()+primaryStage.getY());

Resources