0
votes

I'm using some TextFields in JavaFX, and I want to limit their area of shown text. I already limit their maximum number of characters, but in some cases, the maximum char number typed is larger than the Textfield's width. As you see, the text is overlapping a custom Erase-text button I added.

I want to know if I can move the text's right margin a little bit to the left (without changing the TextField's properties - size, coordinates), so the button and the text won't overlap anymore.

I already tried margins, padding, but they don't do what I need.

Example

How do I limit my TextField's maximum length (from stackoverflow):

public class LimitedJFXTextField extends JFXTextField {

    private final IntegerProperty maxLength;

    public LimitedJFXTextField() {
        super();
        this.maxLength = new SimpleIntegerProperty(-1);
    }

    public IntegerProperty maxLengthProperty() {
        return this.maxLength;
    }

    public final Integer getMaxLength() {
        return this.maxLength.getValue();
    }

    public final void setMaxLength(Integer maxLength) {
        Objects.requireNonNull(maxLength,
                "Max length cannot be null, -1 for no limit");
        this.maxLength.setValue(maxLength);
    }

    @Override
    public void replaceText(int start, int end, String insertedText) {
        if (this.getMaxLength() <= 0) {
            // Default behavior, in case of no max length
            super.replaceText(start, end, insertedText);
        } else {
            // Get the text in the textfield, before the user enters something
            String currentText = this.getText() == null ? "" : this.getText();

            // Compute the text that should normally be in the textfield now
            String finalText = currentText
                    .substring(0, start) + insertedText + currentText
                    .substring(end);

            // If the max length is not excedeed
            int numberOfexceedingCharacters = finalText.length() - this
                    .getMaxLength();
            if (numberOfexceedingCharacters <= 0) {
                // Normal behavior
                super.replaceText(start, end, insertedText);
            } else {
                // Otherwise, cut the the text that was going to be inserted
                String cutInsertedText = insertedText.substring(
                        0,
                        insertedText.length() - numberOfexceedingCharacters
                );

                // And replace this text
                super.replaceText(start, end, cutInsertedText);
            }
        }
    }
}
2
the layout is the task of the skin: to add children, subclass TextFieldSkin, add what's needed and override computeXX and layoutChildren to accomodate for them. Unrelated: why this manual hack for limiting the # of chars? That's possible but belongs into stone age, the current way to implement it is using a TextFormatter - kleopatra
anyway, whenever asking for debug help a minimal reproducible example is required - not just a snippet of (copied?) code. Actually, when having problems with layout there's no need at all for other specialities like char-limitation (repeating: in a very out-dated way!) - kleopatra
@DevilsHnd can't be really certain but: the question is about layout, not about limiting the the # of chars in the field (which is answered well in your referenced QA :) - kleopatra
@kleopatra yes, you're right, the question was about layout. Also, I was using that manual hack because, to be honest, I wasn't aware of the TextFormatter. I first tried using TextProperty, but at least in my way of implementing this, it was buggy. Then, I switched to that manual stone-aged hack. Thanks for your help! I didn't solve my problem yet, even though you gave me a solution, but I will keep trying. At least, I modified my way of limiting the # of chars :) - Alex
@kleopatra can you be more specific, again, please? I'm trying what you said, and I haven't got even a small progress, in the last few hours (plus google-ing this for 2 days; without finding something relevant, other than what you suggested). If I'm annoying, or breaking some rules with my reply, please delete my post. Thanks! - Alex

2 Answers

0
votes

Here is a solution using ControlsFX.

import java.io.IOException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.controlsfx.control.textfield.CustomTextField;

/**
 * JavaFX App
 */
public class App extends Application
{

    private static Scene scene;

    @Override
    public void start(Stage stage) throws IOException
    {
        CustomTextField customTextField = new CustomTextField();
        customTextField.setText("Hello World!");

        Label labelX = new Label("x");
        labelX.setTextFill(Color.RED);
        customTextField.setRight(labelX);
        customTextField.setMaxWidth(200);

        scene = new Scene(new StackPane(customTextField), 500, 500);//loadFXML("primary"), 640, 480);
        stage.setScene(scene);
        stage.show();
    }
}

enter image description here

0
votes

I have never used JFXTextField, so take this answer with a grain of salt.

However, since JFXTextField extends TextField, you should be able to change properties of TextField. One of these is called padding:

So using just the "normal" TextField, you can use this:

public class ExampleApp extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        var textField = new TextField();
        textField.setPadding(new Insets(0, 50, 0, 0)); // This adds 50px right padding
        textField.setText("Hello World!");

        var label = new Label("x");

        var pane = new StackPane(textField, label);
        StackPane.setAlignment(label, Pos.CENTER_RIGHT);

        var scene = new Scene(pane, 500, 500);
        stage.setScene(scene);
        stage.show();
    }
}

Or if you prefer FXML, use this:

<TextField>
    <padding>
        <Insets right="50"/>
    </padding>
</TextField>

The StackPane as well as the Label are just for the sake of demonstrating some overlaying item, same as in Sedricks answer. The important part which actually restricts the text inside of the textfield is the padding.