Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Label take same height as font size in JavaFX?

I set the font size of upper label to 24px. I don't know where I can check the real height of element (if it's possible in the Scene builder), but there is the extra space in upper (designated in the image) and lower part of box.

  • The line spacing is 0.
  • The parent VBox has no top padding.
  • All label sizes are computed.

enter image description here

I need the height of label equals to font size. I mean FontSize = Gray space surrounded by blue markers height.

How to reach it?

I have faced with similar problem HTML/CSS. There, not simple but working solution for most cases is negative margins and :before and :after pseudo elements.

Now which solutions are exists for JavaFX?

The repro

🌎 Repository

Open the resources/static/TasksOverview.fxml by Scene Builder.

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>


<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="640.0" prefWidth="320.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <HBox alignment="CENTER_LEFT">
         <children>
            <CheckBox mnemonicParsing="false" />
            <VBox>
               <children>
                  <Label style="-fx-font-size: 24px;" text="Wake up">
                     <VBox.margin>
                        <Insets />
                     </VBox.margin>
                  </Label>
                  <Label style="-fx-font-size: 14px;" text="... and fly. I need more text to test multiline mode." wrapText="true" />
               </children>
               <HBox.margin>
                  <Insets left="12.0" />
               </HBox.margin>
            </VBox>
         </children>
         <padding>
            <Insets left="12.0" right="12.0" />
         </padding>
      </HBox>
      <HBox alignment="CENTER_LEFT" layoutX="10.0" layoutY="10.0">
         <children>
            <CheckBox mnemonicParsing="false" />
            <VBox>
               <children>
                  <Label style="-fx-font-size: 24px;" text="Wash face">
                     <VBox.margin>
                        <Insets />
                     </VBox.margin>
                  </Label>
                  <Label style="-fx-font-size: 14px;" text="... with cold water." wrapText="true" />
               </children>
               <HBox.margin>
                  <Insets left="12.0" />
               </HBox.margin>
            </VBox>
         </children>
         <padding>
            <Insets left="12.0" right="12.0" />
         </padding>
      </HBox>
   </children>
</VBox>
like image 712
Takeshi Tokugawa YD Avatar asked Dec 30 '22 20:12

Takeshi Tokugawa YD


1 Answers

Labels are "smart" controls that include, within the "skin" code, stuff like some in-built padding, as well as various alignment and spacing options for text and graphics, and support for resizing by dynamically eliding text.

If you want some more control over how text layout happens, then you can use raw Text nodes instead of Labels and then do some of the additional layout yourself if needed. Of course you are going to lose a lot of in-built functionality that way (buyer beware...), but sometimes that might be what you want.

For example, a Text node with the following attributes:

  1. A font size of 24.
  2. No spacing on the top, left or right of the visual area of the text.
  3. A margin spacing of 3 pixels below the visual area of the text when placed in a VBox.

can be defined in FXML as below:

<Text boundsType="VISUAL" text="Visual bounds != Logical bounds">
    <font>
        <Font size="24.0" />
    </font>
    <VBox.margin>
        <Insets bottom="3.0" />
    </VBox.margin>
</Text>

If you use this stuff on a lot of text, then you can set a lot of the values in a style sheet rather than FXML (which would be preferable). I won't describe how to do that here other than to say there is an undocumented -fx-bounds-type css attribute for text.

Sometimes rather than using a Text node, it might be better to just "hack" a Label similar to Matt's answer by using stuff like negative insets (that is usually my preferred option...).

Be careful, esoteric discussion follows, get ready to ignore it ;-)

A key part is setting the boundsType to VISUAL. By default the bounds of the text are usually a bit more than what you actually see, which eases alignment issues, because that means the bounds of the letter "a" and the letter "A" are the same. That kind of bounds is the default and is called LOGICAL bounds. VISUAL bounds makes the bounds of those two letters different, so "a"'s bounds is smaller than "A"'s bounds, which is what allows stuff to not take up any more space than it absolutely needs to.

For example, using Text with VISUAL bounds and instead of a Label, gets rid of two sets of the extra whitespace around the text which appears on your example screen shot. One set of whitespace is allocated by the Label control's internal layout algorithm, another set of whitespace is allocated for the LOGICAL bounds.

Setting bounds to VISUAL is possible within a label by applying an appropriate CSS stylesheet, but if you do that, then the label still won't shrink to fit just the VISUAL bounds because it seems to be coded to work based off of LOGICAL bounds, at least that is what some quick testing I tried seemed to show. So using VISUAL bounds in text in a label just seems to make it display incorrectly (which may even be classified as bug by some people). It might work if you set a graphic with Text inside the label, but if you do that, the label doesn't do much for you and may as well just use a Text node directly (unless you are using a control like a TitledPane which includes a Label in the title, in which case you must use a Label).

like image 148
jewelsea Avatar answered Jan 25 '23 22:01

jewelsea