JavaFX TextArea Limit - javafx

How do I set a limit on text area. I already made a counter that keeps track of the amount of characters in the text area, now I just need something to put in my if statement to make it impossible to put anymore text in the text area. How do I do that?

There's no point in creating a counter: the number of characters in the text area is already always available just from textArea.getText().length(), or, if you need an observable value, Bindings.length(textArea.textProperty()).
To limit the number of characters in a text area, set a TextFormatter which uses a filter that vetoes changes to the text if they would cause the text to exceed the maximum:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.stage.Stage;
public class LimitedTextArea extends Application {
#Override
public void start(Stage primaryStage) {
final int MAX_CHARS = 15 ;
TextArea textArea = new TextArea();
textArea.setTextFormatter(new TextFormatter<String>(change ->
change.getControlNewText().length() <= MAX_CHARS ? change : null));
Scene scene = new Scene(textArea, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Related

JavaFX Alert with multiple colors

I have a program that at some point (may) displays two warnings - one about errors - those are in red, and one about warnings - those are in orange.
I wonder however if there is a way - using css - to have just one warning with some text red and some text orange.
Here is an example of what I want to achieve (the two can be separated into "sections"):
RED ERROR1
RED ERROR2
RED ERROR3
ORANGE WARNING1
ORANGE WARNING2
I've seen some answers pointing to RichTextFX like this one, however I don't see (or don't know) how that could apply to generic Alerts. Is that even possible, without writing some custom ExpandedAlert class?
The Alert class inherits from Dialog, which provides a pretty rich API and allows arbitrarily complex scene graphs to be set via the content property.
If you just want static text with different colors, the simplest approach is probably to add labels to a VBox; though you could also use more complex structures such as TextFlow or the third-party RichTextFX mentioned in the question if you need.
A simple example is:
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class App extends Application {
private final Random rng = new Random();
private void showErrorAlert(Stage stage) {
Alert alert = new Alert(Alert.AlertType.ERROR);
int numErrors = 2 + rng.nextInt(3);
int numWarnings = 2 + rng.nextInt(3);
VBox errorList = new VBox();
for (int i = 1 ; i <= numErrors ; i++) {
Label label = new Label("Error "+i);
label.setStyle("-fx-text-fill: red; ");
errorList.getChildren().add(label);
}
for (int i = 1 ; i <= numWarnings ; i++) {
Label label = new Label("Warning "+i);
label.setStyle("-fx-text-fill: orange; ");
errorList.getChildren().add(label);
}
alert.getDialogPane().setContent(errorList);
alert.initOwner(stage);
alert.show();
}
#Override
public void start(Stage stage) {
Button showErrors = new Button("Show Errors");
showErrors.setOnAction(e -> showErrorAlert(stage));
BorderPane root = new BorderPane(showErrors);
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
which gives this result:

Java FX TextField blur

Can anyone tell me why sometimes JavaFX displays the content of a TextField with a blur effect on it? It seems to be random and occurs in any of my TextFields. Please see the image attached.
Focusing on the intermittent rendering artifact mentioned here, the 2 glyph looks like it's been rendered twice, with one copy shifted horizontally relative to the other. Such apparently random anomalies are notoriously difficult to identify. Myriad causes may include incorrect synchronization, improper layout, defects in the host platform's rendering pipeline, etc. For reference, the example below may allow you to test on disparate platforms.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/53989899/230513
*/
public class TextFieldTest extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("TextFieldTest");
BorderPane root = new BorderPane();
root.setCenter(createContent());
root.setBottom(createVersion());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node createContent() {
HBox row1 = new HBox(4);
Label channelsLabel = new Label("Channels:");
TextField channelsText = new TextField("2");
channelsText.setPrefWidth(32);
Label separatorLabel = new Label("Separator:");
TextField separatorText = new TextField("!");
separatorText.setPrefWidth(32);
row1.setPadding(new Insets(8));
row1.getChildren().addAll(
channelsLabel, channelsText, separatorLabel, separatorText);
HBox row2 = new HBox(4, new Label("Label:"), new TextField());
row2.setPadding(new Insets(8));
return new VBox(row1, row2);
}
private Label createVersion() {
Label label = new Label(
System.getProperty("os.name") + " v"
+ System.getProperty("os.version") + "; Java v"
+ System.getProperty("java.version"));
label.setPadding(new Insets(8));
return label;
}
public static void main(String[] args) {
launch(args);
}
}
As shown in the Modena example, an intentional blur effect indicates that the text field is focused:
The detail that gives rise to the blurred effect in your image is a compound border, seen below at 2x:
Comparable effects are seen here for buttons (top row) and default buttons (bottom row):

What's a good observable appendable base for a TextArea?

I have a StringBuffer that is occasionally appended with new information.
In a separate module, I have a JavaFX TextArea that displays that StringBuffer.
Right now, I have to manually update the TextArea every time the underlying data is modified.
Is there something like an ObservableList (which I use for TableViews) that I can use as the back-end data for the TextArea instead, so I don't have to manually manage pushing the changes to the display?
I am not attached to using a StringBuffer. I'm glad to use any appendable data structure to hold text.
You can consider something simple like this:
import javafx.beans.binding.StringBinding;
public class ObservableStringBuffer extends StringBinding {
private final StringBuffer buffer = new StringBuffer() ;
#Override
protected String computeValue() {
return buffer.toString();
}
public void set(String content) {
buffer.replace(0, buffer.length(), content);
invalidate();
}
public void append(String text) {
buffer.append(text);
invalidate();
}
// wrap other StringBuffer methods as needed...
}
This enables easy coding for binding to a text area. You can simply do
TextArea textArea = new TextArea();
ObservableStringBuffer buffer = new ObservableStringBuffer();
textArea.textProperty().bind(buffer);
// ...
buffer.append("Hello world");
However, it's important to note here that you don't transfer the efficiency of the buffer API to the text area: the text area simply has a textProperty() representing its text, which can still only really be modified by set(...) and setValue(...). In other words, when you append to the buffer, you essentially end up with textArea.setText(textArea.getText() + "Hello world") (not textArea.appendText("Hello world"). If you're just looking for a clean API, then this should work for you; if you're looking for something efficient, you would have to "wire" the calls to appendText yourself, since that is simply not supported by the text area's textProperty().
Here's a SSCCE using the above class:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ObservableStringBufferTest extends Application {
private int counter ;
#Override
public void start(Stage primaryStage) {
ObservableStringBuffer buffer = new ObservableStringBuffer();
TextArea textArea = new TextArea();
textArea.setEditable(false);
textArea.textProperty().bind(buffer);
buffer.set("Item 0");
Timeline timeline = new Timeline(new KeyFrame(
Duration.seconds(1),
e -> buffer.append("\nItem "+(++counter))));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
primaryStage.setScene(new Scene(new StackPane(textArea)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX GridPane: Shrink if content is disabled and invisible

Is it possible to shrink a GridPane row if the content of that row is both disabled and invisible?
When I set a Node to disable=true and visible=false, the cell still takes up space.
If I have 8 rows and only the first and last is visible I don't want the empty rows to take up much space. As if there was only two rows.
The only "solution" I could find was to set the size to zero. However I do not consider that a good solution. I would have to store the min/max size to set it back when/if the node becomes enabled/visible again.
Could perhaps CSS help with a better solution?
package com.company;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
/* (non-Javadoc)
* #see javafx.application.Application#start(javafx.stage.Stage)
*/
#Override
public void start(Stage stage) throws Exception {
Label label1 = new Label("Text");
Label label2 = new Label("Text");
Label label3 = new Label("Text");
label2.setDisable(true);
label2.setVisible(false);
GridPane root = new GridPane();
root.setGridLinesVisible(true);
root.add(label1, 0, 0);
GridPane.setVgrow(label1, Priority.NEVER);
root.add(label2, 0, 1);
GridPane.setVgrow(label2, Priority.NEVER);
root.add(label3, 0, 2);
GridPane.setVgrow(label3, Priority.NEVER);
stage.setScene(new Scene(root));
stage.setWidth(300);
stage.setHeight(300);
stage.setTitle("JavaFX 8 app");
stage.show();
}
}
If you do not want the parent to layout a node, you can set the managed property of the node to false.
Here is an image showing the difference in layout when I used the following line in your code:
label2.setManaged(false);

Adding value from textfield

I a m creating a java application and I have a problem.
Here is the code.
package javastackoverflow;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Javastackoverflow extends Application {
TextField deduct2;
Label text;
double ammount = 0.0;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Apply");
text = new Label(Double.toString(ammount));
btn.setOnAction((e->{
double getamount = Double.parseDouble(deduct2.getText());
text.setText(Double.toString(getamount)+ ammount);
//this is where the program is suppose to get the amount and add it to amount, notice the + sign.
}))
;
deduct2 = new TextField();
FlowPane root = new FlowPane();
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(btn,deduct2,text);
Scene scene = new Scene(root, 400, 450);
primaryStage.setTitle("Yo Stack");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
What it is suppose to do
When the user enters a number into the textfield the program is suppose to
to take that number and add it to ammount = 0;
My problem
But when ever the using enters a new number the text changes to That number. Remember I would like it to add to the currant number like (currant number = 23; user enters new Number) new number = 3; I would like results to equal = 26 but right now the program shows the result 3;
What I think
I think the problem is in the onAction() method.
I think that the text.setText() method is displaying the currant text entered into the textfield, rather then adding it to ammount.
I alse don't think I am using the correct operator from this line of code. That may be part of the problem.
text.setText(Double.toString(getamount)+ ammount);
notice how I use the + sign, that + adds the getamount to ammount..or its supposed to. But when I change that plus sign to - or * I get this error
===============================
bad operand types for binary operator '*'
first type: String
second type: TextField
===============================
As you can probably tell I really would like this code to be correct so if you don't understand something please comment before you report me. Then I can change it fast. Thank you!
You are adding the amount to the getAmount variable when amount = 0.0
Try adding the new value to getAmount
package javastackoverflow;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Javastackoverflow extends Application {
TextField deduct2;
Label text;
double getamount = 0.0; //Edit 1
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Apply");
text = new Label(Double.toString(ammount));
btn.setOnAction((e->{
getamount += Double.parseDouble(deduct2.getText()); //Edit 2
text.setText(Double.toString(getamount));
//this is where the program is suppose to get the amount and add it to amount, notice the + sign.
}))
;
deduct2 = new TextField();
FlowPane root = new FlowPane();
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(btn,deduct2,text);
Scene scene = new Scene(root, 400, 450);
primaryStage.setTitle("Yo Stack");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

Resources