Class TextField

java.lang.Object
limn.scene.Widget
limn.components.TextField
Direct Known Subclasses:
PasswordField, SearchField

public class TextField extends Widget
Single-line text input: blinking cursor (frame-clock ticker), mouse and Shift+arrow selection, Home/End, Ctrl/Cmd+A/C/V/X through the system clipboard, placeholder, and horizontal scrolling that keeps the cursor visible when the text overflows. Colors come from the Theme, metrics from the SizeTokens row of the step resolved on this widget, and every stroke from Strokes, which is why a focus ring is the same weight at XSMALL and XLARGE.

The reference implementation for the text cluster: PasswordField and SearchField inherit all of this geometry and declare none of their own.

Every horizontal coordinate here comes from one shaped line, held in a field and re-shaped only when its inputs change. Caret x is ShapedText.caretX(limn.graphics.ShapedText.Position), a click is ShapedText.hitTest(float), the selection band is the N boxes selection returns, and the arrow keys step through ShapedText.caretLeft(limn.graphics.ShapedText.Position)/ShapedText.caretRight(limn.graphics.ShapedText.Position). None of it is the width of a prefix of the string, because with a shaper in the pipeline that width is not a thing that exists: inside their line characters join, ligate, kern and reorder differently than they do alone, so a prefix measurement is not a slower way to place a caret but a wrong one. shapeDisplay(java.lang.String, limn.graphics.Font) is where a subclass substitutes a display form, and its index space is the model's, which is what lets the substitution happen without any index arithmetic at all.

  • Field Details

  • Constructor Details

    • TextField

      public TextField()
      An empty single-line field.
  • Method Details

    • text

      public String text()
      The current contents.
    • setText

      public TextField setText(String text)
      Replaces the contents, clearing the selection and undo history. UI thread only.
    • setPlaceholder

      public TextField setPlaceholder(String newPlaceholder)
      Sets a fixed placeholder, shown only while the field is empty.
    • setPlaceholder

      public TextField setPlaceholder(I18nString newPlaceholder)
      Sets a placeholder that follows the UI language. A subclass shipping a default one (see SearchField) simply calls this in its constructor: an application's own setPlaceholder replaces the value, so there is no "did the app override it" state to track.
    • placeholder

      public String placeholder()
      The placeholder as it currently reads.
    • onChange

      public TextField onChange(Consumer<String> listener)
      Called with the full text after every edit, typed or programmatic.
    • insertText

      public TextField insertText(String text)
      Inserts text at the cursor (replacing any selection), as if typed. UI thread.
    • setPreferredWidth

      public TextField setPreferredWidth(float width)
      Overrides the step's fieldWidth; a negative value restores it.
    • setLeadingIcon

      public TextField setLeadingIcon(Icon icon)
      A leading icon inside the field, tinted to the muted text color (null clears). Drawn as authored whichever way the field reads; use setLeadingIcon(Icon, Icon.Mirroring) for an icon that means a direction.
    • setLeadingIcon

      public TextField setLeadingIcon(Icon icon, Icon.Mirroring mirroring)
      A leading icon that says whether it turns around when the interface does. Only the code that placed an icon knows whether its arrow means "back" or "download", which is why this is a flag here and never a classification inside the toolkit.
    • setTrailingButton

      public TextField setTrailingButton(Icon icon, Runnable action)
      A trailing coupled button (icon + action) inside the field, the caret/arrow region idiom of ComboBox. icon == null removes it.
    • setTrailingButton

      public TextField setTrailingButton(Icon icon, Runnable action, Icon.Mirroring mirroring)
      A trailing coupled button whose icon says whether it turns around when the interface does; see setLeadingIcon(Icon, Icon.Mirroring) for why the toolkit does not decide that.
    • setValidation

      public TextField setValidation(TextField.Validation state)
      Sets the validation state; colors the border danger/warning/success.
    • setError

      public TextField setError(boolean error)
      Convenience: ERROR when true, NONE when false.
    • validation

      public TextField.Validation validation()
      The current validation state, which drives the border and helper colours.
    • model

      public TextEditModel model()
      Returns:
      the editing model (cursor/selection state), for tests and subclasses
    • allowClipboardCopy

      protected boolean allowClipboardCopy()
      Whether copy/cut may export the content (password fields say no).
    • shapeDisplay

      protected ShapedText shapeDisplay(String text, Font font)
      The display form of this field's content as a shaped line: the one place a subclass changes what is drawn and how wide it is.

      Its index space is the model's. ShapedText.caretX(limn.graphics.ShapedText.Position), ShapedText.hitTest(float) and selection on the returned value take and return offsets into text, with no translation anywhere in this component. That is what deletes the prefix arithmetic the caret used to rest on rather than moving it one layer down, and it is the whole contract: an override that returns a line shaped from some other string, with its own boundaries, puts the caret on a neighbour of the character it edits and a click one mark away from the pointer. An override that substitutes marks keeps text as the returned value's ShapedText.text() and changes only the geometry — ShapedText.uniform(java.lang.String, limn.graphics.Font, float, limn.graphics.TextMetrics, long) builds exactly that, from one multiplication, with no glyphs and without the content reaching a shaper at all.

      font is the font this line must be shaped for, and it is half the key the held value is refreshed against, so an override that shapes in some other font makes that value lie about when it is stale. There is deliberately no SizeTokens parameter: the font is the only thing in that row a display line can legitimately depend on, and two ways to reach it is one too many.

      An override whose display form depends on its own state — a reveal toggle — must call invalidateDisplayLine() when that state changes: the key is text, font and ruler epoch, and it cannot see a field it does not know about.

      Parameters:
      text - the model's text, which is the index space of the result
      font - the font the line is drawn in
      Returns:
      the shaped display line; never null
    • paintDisplayText

      protected void paintDisplayText(Canvas canvas, ShapedText display, float x, float baseline, TextMetrics metrics, SizeTokens t, Color ink)
      Draws a display line with its left edge at x: the paint-side twin of shapeDisplay(java.lang.String, limn.graphics.Font). metrics is passed rather than only the baseline so an override can place ink against the same band the caret and the selection fill use: the ink box starts at baseline - metrics.ascent() and is metrics.height() tall.
      Parameters:
      canvas - where to draw
      display - the shaped display line, as shapeDisplay(java.lang.String, limn.graphics.Font) produced it
      x - left edge of the line, in this widget's coordinates
      baseline - the text baseline, in this widget's coordinates
      metrics - the line's vertical band
      t - the size row resolved for this pass
      ink - the colour to draw in
    • invalidateDisplayLine

      protected final void invalidateDisplayLine()
      Drops the held display line, so the next paint rebuilds it through shapeDisplay(java.lang.String, limn.graphics.Font).

      For a subclass whose display form depends on state of its own: the held value is refreshed against the text, the font and the ruler's epoch, and none of those changes when a reveal toggle does.

    • 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
    • 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, (height() - metrics.height()) / 2 + metrics.ascent().

      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, float, float), which moves without re-running layout.

      Overrides:
      baselineOffset in class Widget
    • onPaint

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

      protected void onMouseEvent(MouseEvent event)
      Description copied from class: Widget
      Mouse events (bubbling); call event.consume() when handled.
      Overrides:
      onMouseEvent in class Widget
    • onKeyEvent

      protected void onKeyEvent(KeyEvent event)
      Description copied from class: Widget
      Key events (focused widget first, then ancestors).
      Overrides:
      onKeyEvent in class Widget
    • onCharTyped

      protected void onCharTyped(CharEvent event)
      Description copied from class: Widget
      Committed text input (focused widget first, then ancestors).
      Overrides:
      onCharTyped in class Widget
    • acceptsTextInput

      protected boolean acceptsTextInput()
      Description copied from class: Widget
      Whether this widget edits text and should drive the platform input method (IME): the scene enables the IME while such a widget holds focus and disables it otherwise, so composition never intercepts keys meant for non-text UI. Text widgets override this to true. Default: false.
      Overrides:
      acceptsTextInput in class Widget
    • allowsWordJumps

      protected boolean allowsWordJumps()
      Whether modifier+arrow/delete may move word-wise (see PasswordField).
    • composingText

      public String composingText()
      Returns:
      the text currently being composed by the IME (empty when not composing)
    • onPreedit

      protected void onPreedit(PreeditEvent event)
      Description copied from class: Widget
      In-progress IME composition ("preedit") for this widget while it is focused: the still-composing text, shown inline but not yet committed (the commit later arrives as Widget.onCharTyped(limn.scene.event.CharEvent)). Only widgets that accept text input receive it. Default: ignored.
      Overrides:
      onPreedit in class Widget
    • caretRect

      protected Rect caretRect()
      Description copied from class: Widget
      The caret rectangle in scene coordinates (logical points) used to place the IME candidate window; null when there is no caret to anchor (or the widget is not laid out yet). Consulted by the scene only while this widget is focused and Widget.acceptsTextInput() is true.
      Overrides:
      caretRect in class Widget
    • showContextMenu

      protected void showContextMenu(float localX, float localY)
      Raises the Cut/Copy/Paste/Select All menu at a point in this widget's own coordinates.

      Protected so a subclass with different clipboard rules can suppress or replace it; PasswordField does not need to, because allowClipboardCopy() already greys the two rows that would let a secret out.

    • showContextMenuForFocus

      protected void showContextMenuForFocus()
      Raises the same menu for a request that carries no point: the Menu key or Shift+F10, where the caret is the only place the user can have meant. Protected for the same reason as its pointer twin, and a subclass suppressing one has to suppress both.
    • fireChange

      protected void fireChange()
      Notifies the change listener with the current text; for subclasses that edit the model directly.
    • onFocusGained

      protected void onFocusGained()
      Description copied from class: Widget
      Called when this widget takes keyboard focus. Default: nothing.
      Overrides:
      onFocusGained in class Widget
    • onFocusLost

      protected void onFocusLost()
      Description copied from class: Widget
      Called when this widget loses keyboard focus. Default: nothing.
      Overrides:
      onFocusLost in class Widget