Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display text progressively with libgdx

Tags:

java

label

libgdx

I am looking for a way to display text progressively with libgdx, but I can't find a way to do it exactly the way I want. Here is what I did:

  • I have a text label that is being updated periodically to display a different text. The label is set to setWrap(true); and setAlignment(Align.center);
  • Every time I change the text of the label I use a custom Action which I built like this

    public class DisplayTextAction extends TemporalAction{
        private CharSequence completeText;
    
        @Override
        protected void update(float percent) {
            ((Label)actor).setText(
                completeText.subSequence(
                    0,
                    (int)Math.round(completeText.length()*percent));
        }
    
        public void setText(String newText){
            completeText = newText;
        }
    }
    
  • Every text update, I call the action from a pool, change the text and add the action to the label.

Here is my problem: This doesn't work the way I want with a centered and wrapped text.

This happens when text isn't centered (dots represent space):

|h........|
|hel......|
|hello....|

(Works as intended)

This is what happens when the text is centered:

|....h....|
|...hel...|
|..hello..|

And this is how I want it to behave:

|..h......|
|..hel....|
|..hello..|

My original idea to fix this was to use 2 sets of strings, one that is the visible text, and one invisible that acts as "padding". I came up with something like this:

CharSequence visibleText = completeText.subSequence(
    0,
    (int)Math.round(completeText.length()*percent));
CharSequence invisibleText = completeText.subSequence(
    (int)Math.round(completeText.length()*percent),
    completeText.length());

So I have my two sets of strings, but I can't find a way to display two different fonts (one visible, and another one which is the same but with an alpha of 0) or styles in the same label with Libgdx.

I'm stuck, I don't know if my approach is the right one or if I should look into something completely different, and if my approach is correct, I don't know how to follow it up using libgdx tools.

EDIT:

I followed Jyro117's instructions and I could make great progress, but I couldn't make it work with centred text on multiple lines.

imagine this text:

|all those lines are|
|..for a very long..|
|........text.......|

And it has to be displayed like this

|all th.............|
|...................|
|...................|

|all those line.....|
|...................|
|...................|

|all those lines are|
|..for a ve.........|
|...................|

|all those lines are|
|..for a very long..|
|........text.......|

Jyro117's solution give either

|all those lines are|
|for a very long....|
|text...............|

displayed correctly.

or

|...................|
|......all tho......|
|...................|

|...................|
|...all those lin...|
|...................|

|all those lines are|
|......for a v......|
|...................|
like image 388
vaati Avatar asked Oct 03 '22 05:10

vaati


1 Answers

You are over-complicating the solution. All you really need is to determine the size of the label when all the text is added. Once you have determined that, lock the label size to those dimensions, put it inside of a table that expands to fill up the area around it, and then update your label with the action. (You can use a pool and such as needed, but for simplicity I left that out of the code below).

You will have to obviously adapt the code to yours, but this gives you a code reference to what I mean.

Here is a code snippet on one way to do it:

stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
Gdx.input.setInputProcessor(stage);

uiSkin = new Skin(Gdx.files.internal("skin/uiskin.json"));

Table fullScreenTable = new Table();
fullScreenTable.setFillParent(true);

final String message = "hello";
final Label progressLabel = new Label(message, this.uiSkin);
final TextBounds bounds = progressLabel.getTextBounds(); // Get libgdx to calc the bounds
final float width = bounds.width;
final float height = bounds.height;
progressLabel.setText(""); // clear the text since we want to fill it later
progressLabel.setAlignment(Align.CENTER | Align.TOP); // Center the text

Table progressTable = new Table();
progressTable.add(progressLabel).expand().size(width, height).pad(10);

final float duration = 3.0f;

final TextButton button = new TextButton("Go!", this.uiSkin);
button.addListener(new ClickListener() {
    @Override public void clicked(InputEvent event, float x, float y) {
        progressLabel.addAction(new TemporalAction(duration){
            LabelFormatter formatter = new LabelFormatter(message);                 
            @Override protected void update(float percent) {
                progressLabel.setText(formatter.getText(percent));
            }
        }); 
    }
});
stage.addActor(button);

fullScreenTable.add(progressTable);
fullScreenTable.row();
fullScreenTable.add(button);

stage.addActor(fullScreenTable);

Edit:

Added code to center and top align text in label. Also added code to fill spaces on the end to allow for proper alignment. Note: Only useful for mono-spaced fonts.

class LabelFormatter {
    private final int textLength;
    private final String[] data;
    private final StringBuilder textBuilder;

    LabelFormatter(String text) {
        this.textBuilder = new StringBuilder();
        this.data = text.split("\n");
        int temp = 0;
        for (int i = 0 ; i < data.length; i++) {
            temp += data[i].length();
        }
        textLength = temp;
    }

    String getText(float percent) {
        textBuilder.delete(0, textBuilder.length());
        int current = Math.round(percent * textLength);

        for (final String row : data) {
            current -= row.length();
            if (current >= 0) {
                textBuilder.append(row);
                if (current != 0) {
                    textBuilder.append('\n');
                }
            } else {
                textBuilder.append(row.substring(0, row.length() + current));

                // Back fill spaces for partial line
                for (int i = 0; i < -current; i++) {
                    textBuilder.append(' ');
                }
            }

            if (current <= 0) {
                break;
            }
        }

        return textBuilder.toString();
    }
}
like image 80
Jyro117 Avatar answered Oct 10 '22 03:10

Jyro117