Class TextArea
setSoftWrap(boolean) breaks them at the text column's edge instead, at the
opportunities the UI language's line-break rule finds, and the horizontal
axis goes quiet.
Colors come from the Theme, metrics from the SizeTokens row of the
ControlSize step resolved on this widget, and every stroke from
Strokes.
The content inset is anisotropic, and deliberately so. Horizontally it is
fieldPadH, the same token TextField insets its text by, so a field and an
area stacked in a form put their first character on the same column. Vertically it is
areaPad: a text margin rather than a vertical-centring pad, which is why it is
its own token and nothing outside this widget has to agree with it.
The preferred box is derived per step to hold a roughly constant amount of visible content rather than a constant box: an editor is where character count matters most.
Every geometric question is asked of a shaped line, one per visible line. Caret x, hit
testing and the selection band all come from the ShapedText of the line they concern
(shapedRow(int, limn.components.SizeTokens)) rather than from the measured width of a prefix of it, because with a
shaper in the pipeline the width of the first n characters of a line is not the width of
those characters inside it: they join, ligate, kern and reorder differently. The
consequences a user sees are that a click lands on the character under the pointer in mixed text,
that a selection crossing a direction boundary paints as the two or more boxes it really covers,
and that Left and Right move the caret by what is next on the screen.
Left and Right are visual; everything else is logical. Home, End, word movement,
Backspace, Delete, Page and Up/Down all step through the string, because each has to
name a contiguous range: Shift+Home makes a selection and a selection is one range of the
buffer. So in right-to-left text Left and Ctrl+Left move the caret in opposite directions, which
is what Windows and GTK do and the lesser of the two evils.
Soft wrap is a row map, never a second copy of the document. Wrapping, each hard
line holds the char offsets its rows start at (ints), a prefix sum turns a global row into a
line and back, and every geometry question — caret, click, selection band, scroll extent
— is asked of the row under it, through the same shaped-line window the unwrapped mode
paints from (unwrapped, a line simply is its one row). Building the map costs one
shaping per line and is paid when the text is replaced or the column width, font, ruler epoch
or locale moves; an edit re-wraps only the lines it touched, told apart by
TextEditModel.lineDamage(). That seam exists because re-deriving per-line state from
textVersion() alone means re-shaping the document per keystroke — the cliff
ADR 031 §8.2 measured at 22 ms per character typed — and a soft wrap
that reintroduced it would be a regression dressed as a feature.
The scrollbars do not follow the step, so the overlay bars stay 15 pt wide while the pads shrink at the small steps and the bars float over more live text there. An accepted cost: at XSMALL, prefer a trailing right margin in the surrounding layout if the last column matters.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected booleanWhether 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.Whether the scrollbars overlay the text or reserve a gutter.protected floatThe first line's baseline, the very expressiononPaint(limn.graphics.Canvas)draws line 0 with, taken atscrollY == 0.protected RectThe caret rectangle in scene coordinates (logical points) used to place the IME candidate window;nullwhen there is no caret to anchor (or the widget is not laid out yet).insertText(String text) Insertstextat the cursor (replacing any selection), as if typed.model()The editing model: caret, selection and undo.Called with the full text after every edit, typed or programmatic.protected voidonCharTyped(CharEvent event) Committed text input (focused widget first, then ancestors).protected voidCalled when this widget takes keyboard focus.protected voidCalled when this widget loses keyboard focus.protected voidonKeyEvent(KeyEvent event) Key events (focused widget first, then ancestors).protected voidonLayout()Containers position children here (measure +Widget.layoutBox(float, float, float, float)per child).protected SizeonMeasure(Constraints constraints) Reports the size this widget wants withinconstraints.protected voidonMouseEvent(MouseEvent event) Mouse events (bubbling); callevent.consume()when handled.protected voidWidget's own background/content, in local coordinates.protected voidonPreedit(PreeditEvent event) 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 asWidget.onCharTyped(limn.scene.event.CharEvent)).scrollBy(float dx, float dy) Scrolls by a delta in logical points (clamped to the content).floatHorizontal scroll offset in logical points,0at the leading edge: the left edge in a left-to-right subtree and the right edge in a right-to-left one.floatVertical scroll offset in logical points,0at the first line.setBarLayout(ScrollGutters.Layout layout) Sets whether the bars float over the text or reserve strips of their own (defaultScrollGutters.Layout.OVERLAY, which is what prose wants).setError(boolean error) Convenience:ERRORwhen true,NONEwhen false.setPreferredSize(float width, float height) Overrides the step'sareaWidth/areaHeight.setSoftWrap(boolean wrap) Turns soft wrap on or off (default off: long lines scroll).Replaces the contents, clearing the selection and undo history.Sets the validation state; colors the border danger/warning/success.protected voidshowContextMenu(float localX, float localY) Raises the Cut/Copy/Paste/Select All menu at a point in this widget's own coordinates: the same menu, and the same enabled rules, asTextField's.protected voidRaises 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.booleansoftWrap()Whether long lines wrap at the text column's edge instead of scrolling.text()The full contents, lines joined by\n.Methods inherited from class limn.scene.Widget
add, baselineOffsetOf, children, clipboard, clipsChildren, controlSize, cursor, declaredControlSize, declaredLayoutDirection, declaredLocale, focusArrivedByTraversal, height, hitTest, imageCursor, invalidate, invalidate, isEnabled, isFocusable, isFocused, isShowing, isVisible, layoutBox, layoutDirection, locale, localToSceneX, localToSceneY, markNeedsContainedLayout, markNeedsLayout, measure, moveChild, neutralBase, onAttached, onDetached, onFileDrop, onPaintOverlay, overlayPassesPointer, paintChildren, paintOutset, paintWidget, parent, remove, requestFocus, revealInView, scene, sceneToLocalX, sceneToLocalY, setControlSize, setCursor, setEnabled, setFocusable, setImageCursor, setInheritanceHost, setLayoutDirection, setLocale, setTooltip, setTooltip, setVisible, shapeText, textRuler, tooltip, width, x, y
-
Constructor Details
-
TextArea
public TextArea()An empty editor.
-
-
Method Details
-
text
The full contents, lines joined by\n. -
setText
Replaces the contents, clearing the selection and undo history. UI thread only. -
setSoftWrap
Turns soft wrap on or off (default off: long lines scroll). Wrapping, a line wider than the text column breaks at the opportunitiesBreakIteratorfinds under the UI language — the same walkLabelwraps with, so a paragraph breaks in an editor exactly where it breaks in the prose beside it, unspaced CJK and dictionary-segmented Thai included — and the horizontal axis goes quiet: nothing overflows it,scrollXOffset()pins at0, and the horizontal bar shows no thumb. Reading right to left every row is flush against the edge reading starts from, the right one, exactly as unwrapped lines are.Wrapped, Up, Down and Page move by visual row on a sticky goal x, where unwrapped they move by hard line on the model's sticky goal column: a column is the right invariant while a line is one row and meaningless once it is several. Left and Right step onto the neighbouring row when the current one runs out, exactly as they already step onto the neighbouring line. Home, End and Shift+Home stay hard-line and logical, as everywhere else in the toolkit, so a selection is always one range of the buffer.
A caret whose index sits exactly on a soft break is two places on screen — the end of one row and the start of the next — and the caret's side says which, the same side that already disambiguates a direction boundary. The whitespace a break drops is not deleted: it hangs past the margin, undrawn, and a caret inside it shows at the row's drawn end.
Toggling resets the horizontal scroll, because the axis changes meaning; the vertical offset keeps its value and re-clamps against the new extent. UI thread only.
-
softWrap
public boolean softWrap()Whether long lines wrap at the text column's edge instead of scrolling. -
onChange
Called with the full text after every edit, typed or programmatic. -
insertText
Insertstextat the cursor (replacing any selection), as if typed. UI thread. -
setValidation
Sets the validation state; colors the border danger/warning/success. -
setError
Convenience:ERRORwhen true,NONEwhen false. -
setPreferredSize
Overrides the step'sareaWidth/areaHeight. A negative value on either axis restores that axis's token, sosetPreferredSize(-1, 150)pins the height and lets the width follow the step. -
model
The editing model: caret, selection and undo. Mutating it directly bypassesonChange. -
scrollXOffset
public float scrollXOffset()Horizontal scroll offset in logical points,0at the leading edge: the left edge in a left-to-right subtree and the right edge in a right-to-left one. The range is[0, maxScrollX]in both, so "scrolled to the start" is0either way and a reset on a content change needs no branch. Under soft wrap nothing overflows this axis and the offset stays0. -
scrollYOffset
public float scrollYOffset()Vertical scroll offset in logical points,0at the first line. -
scrollBy
Scrolls by a delta in logical points (clamped to the content). UI thread only. -
setBarLayout
Sets whether the bars float over the text or reserve strips of their own (defaultScrollGutters.Layout.OVERLAY, which is what prose wants). -
barLayout
Whether the scrollbars overlay the text or reserve a gutter. -
onMeasure
Description copied from class:WidgetReports the size this widget wants withinconstraints. 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
ControlSizeand theLayoutDirectiononce each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, andI18n.locale()answers it wherever text is resolved. -
baselineOffset
protected float baselineOffset()The first line's baseline, the very expressiononPaint(limn.graphics.Canvas)draws line 0 with, taken atscrollY == 0. Deliberately not scroll-dependent: the scroll offset is view state, and a BASELINE row that re-aligned itself as the user scrolled would jitter.No empty-text guard here, unlike the single-line controls. An editor always paints a line box (the caret sits in it) whether or not the model holds text, so falling back to
super.baselineOffset()(the bottom edge) would make typing the first character jump a whole ~140 pt row.- Overrides:
baselineOffsetin classWidget
-
onLayout
protected void onLayout()Description copied from class:WidgetContainers position children here (measure +Widget.layoutBox(float, float, float, float)per child). -
onPaint
Description copied from class:WidgetWidget's own background/content, in local coordinates. -
onMouseEvent
Description copied from class:WidgetMouse events (bubbling); callevent.consume()when handled.- Overrides:
onMouseEventin classWidget
-
onKeyEvent
Description copied from class:WidgetKey events (focused widget first, then ancestors).- Overrides:
onKeyEventin classWidget
-
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: the same menu, and the same enabled rules, asTextField's. -
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. -
onCharTyped
Description copied from class:WidgetCommitted text input (focused widget first, then ancestors).- Overrides:
onCharTypedin classWidget
-
acceptsTextInput
protected boolean acceptsTextInput()Description copied from class:WidgetWhether 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 totrue. Default:false.- Overrides:
acceptsTextInputin classWidget
-
composingText
- Returns:
- the text currently being composed by the IME (empty when not composing)
-
onPreedit
Description copied from class:WidgetIn-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 asWidget.onCharTyped(limn.scene.event.CharEvent)). Only widgets that accept text input receive it. Default: ignored. -
caretRect
Description copied from class:WidgetThe caret rectangle in scene coordinates (logical points) used to place the IME candidate window;nullwhen there is no caret to anchor (or the widget is not laid out yet). Consulted by the scene only while this widget is focused andWidget.acceptsTextInput()is true. -
onFocusGained
protected void onFocusGained()Description copied from class:WidgetCalled when this widget takes keyboard focus. Default: nothing.- Overrides:
onFocusGainedin classWidget
-
onFocusLost
protected void onFocusLost()Description copied from class:WidgetCalled when this widget loses keyboard focus. Default: nothing.- Overrides:
onFocusLostin classWidget
-