11

I wrote a basic control and its skin. A label is displayed in a HBox in the skin. This label should wrap its text if there isn't enough space.

public class LabelWrap extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {
        BasicControl basicControl = new BasicControl();
        BorderPane borderPane = new BorderPane();
        borderPane.setPrefWidth(150);
        borderPane.setCenter(basicControl);

        stage.setScene(new Scene(borderPane));
        stage.centerOnScreen();
        stage.show();
    }

    private static class BasicControl extends Control {
        @Override
        protected Skin<?> createDefaultSkin() {
            return new BasicControlSkin(this);
        }
    }

    private static class BasicControlSkin extends SkinBase<BasicControl> {
        protected BasicControlSkin(BasicControl control) {
            super(control);
            VBox box = new VBox();
            Label label = new Label("This text should wrap because it is too long");
            label.setWrapText(true);
            box.getChildren().add(label);
            getChildren().add(box);
        }
    }
}

But the label does not wrap (the ellipsis is displayed) because the preferred width of my control is not correctly computed:

actual label behavior

what i want to obtain is:

expected label behavior

How can i configure the skin to compute the skin preferred height to obtain the desired behavior (i never want an ellipsis displayed) ?

Notes:

  • i don't want to set an explicit maximum size on the label or on other skin components: label.setMaxWidth(150). The sole explicit width set should be the root BorderPane in the start method. This width (150) could be variable, the control could be used in different place.
  • this basic control is of course a simplification of the real one. The real one displays several Label with variable texts inside.
  • the label wraps correctly if i augment the window height until it has enough space
  • this code is running on java 1.8.0_40-b27 on a OSX 10.10.2
cнŝdk
  • 31,391
  • 7
  • 56
  • 78
gontard
  • 28,720
  • 11
  • 94
  • 117
  • On 8u40 Ubuntu, it is being wrapped. Try it with borderPane.setPrefHeight( 50); – Uluk Biy May 12 '15 at 11:02
  • @UlukBiy i don't want to set the BorderPane height only its width. This is a simplification of my use case. – gontard May 12 '15 at 11:15
  • 1
    I mean the text wrapping is working as expected when borderPane.setPrefHeight( 50) and if the width of the window is increased or decreased. The displayed ellipsis is due to the even the wrapped text has not enough space to render. – Uluk Biy May 12 '15 at 11:23
  • @gontard Have you tried binding the `prefWidth of Label` to the `widthProperty of VBox`? `wrapText` will only work, if text exceeds the width of the Labeled, so it needs the Label to have a width defined. Try adding `label.prefWidthProperty().bind(box.widthProperty());` in your BasicControlSkin(). – ItachiUchiha May 12 '15 at 11:55
  • @ItachUchia: on Win7 this results in a very high (almost screen size) window. I rather think he has to override the computePrefHeight method and calculate the height himself. After all the wrapping works, but as Uluk suggests it depends on the parent. – Roland May 12 '15 at 12:06
  • An alternative would be to use a Text node and setWrappingWidth, after all you know the width. – Roland May 12 '15 at 12:09
  • @Roland my control is used in other context where the width is different – gontard May 12 '15 at 12:11
  • @gontard, if we assume that wrapping is working can you update your question with a new definition of the desired result. – Uluk Biy May 12 '15 at 12:44
  • @Roland i have tried to implement: `computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset)` but the supplied `width` parameter is `-1` not `150` – gontard May 12 '15 at 12:45
  • @UlukBiy i have updated my answer, i hope it is more clear – gontard May 12 '15 at 12:51
  • @gontard, you may get a hint from com.sun.javafx.scene.control.skin.Utils to how to computeTextWidth() and computeTextHeight(). If the content consists of more complex nodes other that simple label, again you need to get their bounds and involve in the calculations. – Uluk Biy May 12 '15 at 13:30

1 Answers1

1

AFAIK, to wrap a text in a label you should define a width to this label because referring to the setWrapText(Boolean) documentation:

public final void setWrapText(boolean value)

Sets the value of the property wrapText.

Property description: If a run of text exceeds the width of the Labeled, then this variable indicates whether the text should wrap onto another line.

Here the statement exceeds the width of the Labeled induce that you have already defined a width for your label, that's why you can't use it when there's no width defined.

So your code should be:

    Label label = new Label("This text should wrap because it is too long");
    label.setMaxWidth(150);
    label.setWrapText(true);

Another alternative is to use a Text element instead of a Label and use the method setWrappingWidth() like this:

Text t = new Text("This text should wrap because it is too long"  );
t.setWrappingWidth(150);

And you will get this result:

Wrap text result

Conclusion:

To wrap a text (either in a Label or in a Text element) you have to define a width so the text will return to a new line when we exceed this width.

EDIT:

And to make it a little bit more dynamic and avoid setting a width to your label, and if you are setting a PrefWidth to your borderPaneyou can use a static double WIDTH that will get this PrefWidth and set it to the MaxWidth of the label, here's the example code:

public class LabelWrap extends Application {

    static double WIDTH;

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

    @Override
    public void start(Stage stage) throws Exception {
        BasicControl basicControl = new BasicControl();
        BorderPane borderPane = new BorderPane();
        borderPane.setPrefWidth(150);
        borderPane.setCenter(basicControl);

        //get the PrefWidth value in the WIDTH attribute
        WIDTH = borderPane.getPrefWidth();
        stage.setScene(new Scene(borderPane));
        stage.centerOnScreen();
        stage.show();
    }

    private static class BasicControl extends Control {
        @Override
        protected Skin<?> createDefaultSkin() {
                return new BasicControlSkin(this);
        }
    }

    private static class BasicControlSkin extends SkinBase<BasicControl> {
        protected BasicControlSkin(BasicControl control) {
            super(control);
            VBox box = new VBox();
            Label label = new Label("This text should wrap because it is too long");

            //set the WIDTH value to the label MaxWidth
            label.setMaxWidth(WIDTH);
            label.setWrapText(true);
            box.getChildren().add(label);
            this.getChildren().add(box);
        }
    }
}
Community
  • 1
  • 1
cнŝdk
  • 31,391
  • 7
  • 56
  • 78
  • 1
    Thanks, but i already know this. And i have also specified (first bullet in the note section) that i don't want to set an explicit width on the label. – gontard May 21 '15 at 07:15
  • @gontard Yes I know, but apparently it's the only solution here, but you can define a `static` variable width in your Application that gets the `borderPane` `prefWidth` and set it to the label see my EDIT. – cнŝdk May 21 '15 at 07:42
  • It will work if I use the control always in the same context. But my control could be use in various `Pane` in he application with different widths. – gontard May 22 '15 at 07:04