4 Replies Latest reply: Feb 15, 2013 5:51 PM by bvm RSS

    minWidth = USE_COMPUTED_SIZE is ignored?

      I want to lay out a simple HBox like this:

      [label1] [label2...] [label3]

      I want label3 to be right-justified, and I am more interested in seeing the contents of label1 and label3 than label2 when the container shrinks. So naively, I'd think that I should set minWidth = USE_COMPUTED_SIZE for the important labels, and to make label3 right justified I'd set HBox.hgrow=ALWAYS for the middle label:
      <HBox xmlns:fx="http://javafx.com/fxml" spacing="5">
          <Double fx:constant="MAX_VALUE" />
        <Label fx:id="label1" text="Important Label">
            <Pane fx:constant="USE_COMPUTED_SIZE" />
        <Label fx:id="label2" text="Vastly less important message" HBox.hgrow="ALWAYS" />
        <Label fx:id="label3" text="Important Label">
            <Pane fx:constant="USE_COMPUTED_SIZE" />
      But that doesn't work in either respect! label3 is not right-justified, and labels 1 and 3 shrink to ellipses as fast, if not faster, than label2 when I make the window narrower. A little experimentation reveals that setting a large maxwidth on label2 achieves the right-justification that I want, but minwidth is still a problem.

      On closer inspection, I find that USE_COMPUTED_SIZE (-1.0) is actually the default minwidth! What good is that?

      If instead of using USE_COMPUTED_SIZE, I simply set the minwidth of labels 1 & 3 to a real value like 99, they do indeed shrink later than label2 does. But of course, I don't want to hard-code a minwidth like that. What am I supposed to do?
        • 1. Re: minWidth = USE_COMPUTED_SIZE is ignored?
          Try something like the following.
          Load it up in SceneBuilder 1.1+ and then choose Preview | Preview in Window and resize the preview window to test out the resizing behaviour:
          <?xml version="1.0" encoding="UTF-8"?>
          <?import java.lang.*?>
          <?import java.util.*?>
          <?import javafx.geometry.*?>
          <?import javafx.scene.control.*?>
          <?import javafx.scene.layout.*?>
          <?import javafx.scene.paint.*?>
          <StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="39.5" prefWidth="498.0000999999975" xmlns:fx="http://javafx.com/fxml">
              <HBox prefHeight="39.5" prefWidth="496.0" spacing="5.0" style="-fx-background-color: cornsilk" StackPane.alignment="CENTER_RIGHT">
                  <Label minWidth="-Infinity" style="-fx-background-color: slategrey" text="Important Label" HBox.hgrow="SOMETIMES" />
                  <Label style="-fx-background-color: lightgrey" text="Vastly less Important Label" HBox.hgrow="NEVER" />
                  <Label alignment="CENTER_RIGHT" maxWidth="1.7976931348623157E308" minWidth="-Infinity" style="-fx-background-color: palegreen" text="Important Label" HBox.hgrow="ALWAYS" />
                  <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
          It's probably not exactly what you want, but hopefully it is helpful.

          Oddly, in my tests, the HBox.hgrow SOMETIMES priority doesn't seem to have any preference over the NEVER priority. So when you resize the thing smaller, the first two labels start to get elided unless you start to specify minWidth values.

          So I set the minWidth of the first label to the preferred width (and same for the last label). That causes the two important labels to never get elided (which might be the behaviour you want for the important labels anyway).

          Setting the alignment of the HBox itself to CENTER_RIGHT means (in combination with the right most label's HBox.hgrow of ALWAYS and a minWidth of the preferred width) that the rightmost important label is always visible and the other labels get pushed off to the left hand side if there is not enough room.

          Setting the maxWidth of the rightmost label to MAX_VALUE and it's alignment to CENTER_RIGHT right aligns the label in the HBox.

          The styles are just in there so that it is easy to see how big each label is - normally you would stick that kind of stuff in a stylesheet.

          The values of things like minWidth="-Infinity" and maxWidth="1.7976931348623157E308" are weird constants because that is what SceneBuilder outputs rather than something sensical like Label.USE_PREF_SIZE or Double.MAX_VALUE

          And yeah, as you point out in your question "USE_COMPUTED_SIZE (-1.0) is actually the default minwidth", so there is no point in explicitly setting minWidth to that value.
          • 2. Re: minWidth = USE_COMPUTED_SIZE is ignored?
            Thanks. I guess the short answer is that I wanted USE_PREF_SIZE, not USE_COMPUTED_SIZE. I was misunderstanding their meanings. I think the latter has a somewhat misleading name. To me it ought to be called USE_DEFAULT_SIZE, and I guess the reason it exists is so that you could "undo" setting a size property to an explicit value. The name USE_COMPUTED_SIZE probably makes sense to the implementors, because it means "use the internal getComputed{Min,Max,Pref}{Width,Height} method." Meanwhile, USE_PREF_SIZE means use the preferred size, which by default is also the size the system computes for the node (and thus is in some sense its computed size).

            The alignment trick on label3 is cute, but I don't think it would generalize to node types that don't have an alignment property. So I think I'll stick with hgrow=ALWAYS on the middle node, with its maxWidth set to Double.MAX_VALUE. That also sort of gives the right answer if I wanted to style the middle region with a background color.
            • 3. Re: minWidth = USE_COMPUTED_SIZE is ignored?
              Thanks for your comments bvm.

              You might also be interested in how James Weaver solves things like this using "struts" and "springs".
              The strut provides a fixed spacing between two elements and the spring expands to fill available space.
              Because they are regions they are easily stylable via css.

              Some relevant code from the article:
              Region strut = new Region();
              Region spring = new Region();
              ToolBar toolBar = ToolBarBuilder.create()
                  currentTokenLabel = LabelBuilder.create()
              HBox.setHgrow(spring, Priority.ALWAYS);
              Note that behind the scenes a ToolBar is just a HBox, so the technique can also apply to your instance.
              One advantage of the strut and spring approach is that it also works for spacing Node types that are not resizable (such as ImageViews).

              It is arguable whether adding extra nodes to the UI just to enforce padding rules is a good idea or not, but it is an alternate implementation to consider.
              • 4. Re: minWidth = USE_COMPUTED_SIZE is ignored?
                Thanks. One of the earliest JFX classes I wrote was this one:
                public class HSpring extends Region {
                     public HSpring() {
                          HBox.setHgrow(this, Priority.ALWAYS);
                Very handy for lots of layouts. Not sure I've had occasion to do something like a strut, since that's fixed size, rather than adapting to the container.