Class Label

java.lang.Object
limn.scene.Widget<Label>
limn.components.Label

public final class Label extends Widget<Label>
Single- or multi-line text. Without wrap, text that does not fit is cut with a real-measured ellipsis (…), never a blind character count. With wrap, lines break greedily at the opportunities BreakIterator finds under the UI language, so unspaced CJK and dictionary-segmented Thai wrap through the same walk as prose. Between the two, setMaxLines(int) breaks the text the same way across a set number of lines and puts the ellipsis at the end of the last. Horizontal and vertical alignment supported. Colors come from the Theme; typography comes from the resolved ControlSize step, picked by Label.Role.

Geometry comes from one shaping of the paragraph, never from prefix widths. The width of the first N characters of a string is not the width of anything on screen once a shaper joins, ligates or reorders, so every question this class asks about where to cut is asked of a ShapedText: ShapedText.fitEnd(int, float) for the budget and ShapedText.advanceTo(int) for what a candidate costs. The paragraph shaping does not depend on the box, so a measure and the layout that follows share it; only the per-line shapings are width-dependent, and each painted line is re-shaped because advanceTo is a budget and not a promise about a substring — the forms on both sides of a cut change when the cut is made.

Content-tight at every step: the measured box is exactly the line box: no minimum height, no padding, and no contribution from the icon. A Label's job beside a Button or a TextField is to let the parent's alignment decide the row height; a floor here would double-count against the container's control height.

The icon overhangs rather than growing the row. Its square is Strokes.ICON_OPTICAL_BUMP larger than the line box, so measuring max(lineHeight, iconBox) would let it win unconditionally and leave an icon Label taller than a bare one; a form column mixing the two goes visibly ragged. The row is the line box either way and the bump hangs out of the top and bottom, which is what it is for: icon glyphs carry no ascender/descender slack and must be drawn slightly larger than the text box to read at the same weight. The overhang is declared to partial rendering by paintOutset() and drawn outside the text clip.

Reading right to left, the icon and the text swap ends and nothing else moves. Label.HAlign.START and Label.HAlign.END name where reading starts and ends, so they turn around with the paragraph; Label.HAlign.CENTER is a centre in either direction and is the one arm of that decision that is the same number. The icon square is leading, which is the left edge in one direction and the right edge in the other, and the glyph inside it turns around only when the call that placed it said it should — see Icon.Mirroring.

  • Constructor Details

    • Label

      public Label(String text)
      A label showing a fixed string; see the I18nString constructor for localized text.
    • Label

      public Label(I18nString text)
      A label whose text follows the UI language; see I18nString.
  • Method Details

    • text

      public String text()
      The text as it currently reads; see textSource() for the key behind it. A label that names a control (setLabelFor(limn.scene.Widget<?>)) reads its access letter's & mark out of it: "&Name" reads "Name". Any other label shows its text as written, an ampersand included.
    • textSource

      public I18nString textSource()
      The localizable value this label holds, which a language change re-resolves.
    • setLabelFor

      public Label setLabelFor(@Nullable Widget<?> target)
      Makes this label the accessible name of another widget, the way a form's caption names the field it sits beside. UI thread only.

      Nothing is inferred from where the two sit: a caption beside a field names it only when this is called, because proximity is a layout accident and a tree built from it says confident wrong things. One widget at a time; calling it again moves the link, and passing null removes it.

      The named widget takes this label's text as its name and carries a LABELLED_BY relation back to it, so a client may read either. Changing this label's text renames what it labels, because the text is read at publish rather than copied here.

      Parameters:
      target - the widget this label names, or null to remove the link
      Returns:
      this label, for chaining
    • labelFor

      public @Nullable Widget<?> labelFor()
      The widget this label names, or null; see setLabelFor(Widget).
    • setDescriptionFor

      public Label setDescriptionFor(@Nullable Widget<?> target)
      Makes this label the accessible description of another widget, the way the message beneath a field says why the field is invalid. UI thread only.

      The same rule as setLabelFor(limn.scene.Widget<?>): a message that merely sits under a field describes nothing until this is called, one widget at a time, and null removes the link. The described widget takes this label's text as its description, read at publish so a message that changes follows, and carries a DESCRIBED_BY relation back to this label; a reader then speaks the message after the field's name and state rather than finding it as a sentence somewhere else on the screen.

      A label may name one widget and describe another at once; a caption and a message are two different labels in a form, and this is what keeps them from having to be the same one.

      Parameters:
      target - the widget this label describes, or null to remove the link
      Returns:
      this label, for chaining
    • descriptionFor

      public @Nullable Widget<?> descriptionFor()
      The widget this label describes, or null; see setDescriptionFor(Widget).
    • accessibleLabelText

      protected I18nString accessibleLabelText()
      Description copied from class: Widget
      The text this widget offers when it is named as another's label, or null when it is not the kind of widget that captions anything.

      Overridden by the widgets that are captions in their own right; everything else declines, which is why a link to one names nothing rather than inventing a string from its children.

      Overrides:
      accessibleLabelText in class Widget<Label>
      Returns:
      the caption text, or null
    • accessibleLabelAccessKey

      protected @Nullable String accessibleLabelAccessKey()
      The chord of the access letter this label gives the control it names, only while the letter would reach it: a caption kept hidden as a field's name, or one in a collapsed or disabled section, is skipped by the scene, and a reader told "Alt+N" would press a key that does nothing.
      Overrides:
      accessibleLabelAccessKey in class Widget<Label>
      Returns:
      the access key's display form, or null
    • onAccessibility

      protected void onAccessibility(Accessibility a)
      What a label is to an assistive technology: static text, or a heading when its typographic role is Label.Role.TITLE, named by the whole paragraph it holds.

      The role is decided on role() alone and never on the font actually drawn with: setFont(limn.graphics.Font) pins a face as a size escape hatch and says nothing about what the text means, so a title drawn at fourteen points is still the title. The name is text handed over by reference, which is what keeps a damaged frame that changed nothing free; it is never the painted lines, because an ellipsis and a wrap are how the text fits a box and a reader must hear what the box cut off. An empty text still publishes, so a status label that empties between messages keeps its identity and an icon-only label keeps the tooltip the walk then names it by. The one relation declared is the half nothing else can supply: the walk writes LABELLED_BY onto the widget this label names, and only this label knows to point back.

      Overrides:
      onAccessibility in class Widget<Label>
      Parameters:
      a - the node being described; write into it, and never keep it
    • setText

      public Label setText(String newText)
      Replaces the text with a fixed string. Repaints without a layout pass when the box is unchanged.
    • setText

      public Label setText(I18nString newText)
      Replaces the text with a value that follows the UI language. UI thread only.
    • setFont

      public Label setFont(@Nullable Font newFont)
      Pins an explicit font, overriding both the Label.Role and the size step: the escape hatch, and the only thing that beats them. While it is set, the icon box falls back to the font-derived formula, so an explicitly 14 pt label inside an XLARGE subtree does not get a 24 pt icon. null restores the role.
    • role

      public Label.Role role()
      The typographic role this label draws with.
    • setRole

      public Label setRole(Label.Role newRole)
      Selects the typographic token; the resolved step decides its size. Ignored while an explicit setFont(limn.graphics.Font) is in force.
    • setColor

      public Label setColor(@Nullable Color newColor)
      Pins the text colour; null restores the theme's, resolved at paint time. A pinned colour is the palette of the moment it was read and stays when the palette changes; a text that should follow it says what it means with setTone(limn.components.Tone) instead.
    • setTone

      public Label setTone(@Nullable Tone newTone)
      Draws the text in one of the palette's colours, said by what it means: Tone.DANGER for a line that reports a failure, Tone.SUCCESS for one that confirms. The colour is asked of the theme on every paint, so a change of palette re-colours the text with no code of the application's, which a colour read once and passed to setColor(limn.graphics.Color) does not do. An explicit setColor(limn.graphics.Color) still wins over the tone, and a disabled label is drawn disabled whatever its tone. UI thread only.
      Parameters:
      newTone - the tone, or null for none: the theme's text colour, or its muted one while setMuted(boolean) says so
      Returns:
      this label
    • tone

      public @Nullable Tone tone()
      The tone this label draws its text in, or null; see setTone(limn.components.Tone).
    • setMuted

      public Label setMuted(boolean newMuted)
      Uses the theme's muted text color, resolved at paint time; unlike an explicit setColor(limn.graphics.Color), it follows runtime theme switches. A tone wins over it.
    • setStrong

      public Label setStrong(boolean newStrong)
      Bold weight on top of the effective font; follows runtime theme switches.
    • setItalic

      public Label setItalic(boolean newItalic)
      Italic style on top of the effective font; follows runtime theme switches.
    • setOverflow

      public Label setOverflow(Label.Overflow newOverflow)
      What happens to text that does not fit: on the one line of a label that does not wrap, and on the last line of one given more lines, where Label.Overflow.ELLIPSIS ends that line with an ellipsis when more text remains and Label.Overflow.CLIP lets the rest run on and cuts it at the box's edge.
    • setAlign

      public Label setAlign(Label.HAlign horizontal, Label.VAlign vertical)
      Alignment of the text block inside the laid-out box.
    • setWrap

      public Label setWrap(boolean newWrap)
      Breaks text across lines at the opportunities BreakIterator reports for the UI language, greedily; a run that offers no opportunity at all — a word longer than the box, or a script this locale has no rule for — is cut at the last cluster that fits. The measured height then depends on the width constraint. As many lines as the text needs, unless setMaxLines(int) sets a limit above one. UI thread only.
    • setMaxLines

      public Label setMaxLines(int lines)
      Lets the text break across up to lines lines, at the same opportunities and by the same walk as wrap, and ends the last of them with a measured ellipsis when more text remains (or lets it run on to the box's edge, under Label.Overflow.CLIP). 1, the default, leaves the label as setWrap has it: one line without wrap, as many as the text needs with it. Above 1 the limit holds whether or not wrap is on, so a title of two lines says setMaxLines(2) and nothing else. UI thread only.

      The lines are counted, not fitted: a label given less height than its lines take is clipped, as a wrapped one is. A reader still hears the whole text, which is the label's name however it is cut.

      Parameters:
      lines - the most lines, 1 or more
      Returns:
      this label
      Throws:
      IllegalArgumentException - when lines is below 1
    • maxLines

      public int maxLines()
      Returns:
      the most lines the text breaks across; see setMaxLines(int)
    • setIcon

      public Label setIcon(@Nullable Icon newIcon)
      Sets a leading icon, tinted to the text color, drawn as authored whichever way this label reads. null clears it. Leading is the side reading starts on: the left edge left to right, the right edge right to left.
    • setIcon

      public Label setIcon(@Nullable Icon newIcon, Icon.Mirroring mirroring)
      Sets a leading icon and says whether its glyph turns around when the interface does. The icon's position is leading either way and is this label's decision; whether the drawing inside that square is a back arrow (which turns around) or a logo, a download arrow or a photograph (which must not) is knowable only at the call that placed it, which is why it is a parameter here and not a table anywhere.
      Parameters:
      newIcon - the icon, or null to clear it
      mirroring - Icon.Mirroring.NEVER unless this glyph is directional; a wrong NEVER is one arrow pointing the wrong way, and a wrong IN_RTL is a flipped brand mark
    • baselineOffset

      protected float baselineOffset()
      Description copied from class: Widget
      Distance from this widget's top edge to its first text baseline, in logical points: the alignment reference for Flex.CrossAlignment.BASELINE. Default height(): align on the bottom edge, the correct fallback for a widget with no text. Text-bearing components override with the expression they already paint with; a single-line control's is textRuler().centredBaseline(font, height()), which stands the line's capitals in the middle of the box.

      Valid only once this widget has been given a box. Flex guarantees that: it lays every child of a BASELINE line out at cross position 0 first, reads the baselines, then repositions with Widget.moveChild(limn.scene.Widget<?>, int), which moves without re-running layout.

      Overrides:
      baselineOffset in class Widget<Label>
    • paintOutset

      protected float paintOutset()
      The icon's optical bump, the only ink a Label puts outside its bounds. Undeclared, partial rendering would both under-damage the bump on Widget.invalidate() and cull the Label out of a repaint pass that covers only the overhanging sliver, either way leaving a stale crescent of the old glyph on screen.
      Overrides:
      paintOutset in class Widget<Label>
    • onMeasure

      protected Size onMeasure(Constraints constraints)
      Description copied from class: Widget
      Reports the size this widget wants within constraints. Called once per layout pass, and the result is cached against the constraints and the resolved axes (size step, layout direction, locale), so it must be a pure function of them and of this widget's own state.

      Resolve the ControlSize and the LayoutDirection once each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, and I18n.locale() answers it wherever text is resolved.

      Specified by:
      onMeasure in class Widget<Label>
    • onLayout

      protected void onLayout()
      Description copied from class: Widget
      Containers position children here (measure + Widget.layoutBox(float, float, float, float) per child).
      Overrides:
      onLayout in class Widget<Label>
    • onPaint

      protected void onPaint(Canvas canvas)
      Description copied from class: Widget
      Widget's own background/content, in local coordinates.
      Overrides:
      onPaint in class Widget<Label>