Enum Class LayoutDirection
- All Implemented Interfaces:
Serializable,Comparable<LayoutDirection>,Constable
Mirroring in this toolkit is a placement decision, taken during layout and paint by the widget that owns the coordinate. There is no mirror transform at the canvas root and there must never be one: a global flip would turn correctly shaped text into a mirror image needing a per-run un-flip, would flip every image and every video frame, and would put an inverse transform on the hot path of every hit test.
This enum is the axis only. It carries no numbers and no locale knowledge, so the
toolkit can own it without knowing anything about text, which is what lets a raw
Row act as a direction scope for the components inside it. It is the
companion of ControlSize, resolved by the same chain, invalidated by its own epoch, and
read under the same two rules.
The coexistence contract
A widget's direction is inherited down the tree: its own declared value, else the nearest declaring ancestor's, else its scene's default, else its host's, elseprocessDefault().
LTR is the process default and mirroring is opt-in per subtree, which is what makes
a Hebrew interface holding a left-to-right code editor, log pane, URL bar or JSON viewer
expressible: each of those is a subtree that reads one way inside an interface that reads the
other, and a direction derived from a process-wide locale could not say so. An application in
Arabic writes one line at its scene root; a subtree that disagrees writes one more.
This is never read from the locale
Nothing in the scene graph consultsI18n.locale() to decide a direction. Language and
direction are different axes: a Hebrew subtree inside an English interface still shows English
strings, it just lays them out right to left. forLocale(java.util.Locale) exists so an application
can bridge the two at its own call site, typically once at startup; no widget and no scene calls
it, and none must, since inside a widget it would smuggle the process locale back into the axis
this enum exists to keep out of it. The one caller in the toolkit's own modules is the native
dialog the Windows backend shows when no window can be made at all: it has no scene to inherit
a direction from, and its text is in the process locale.
Never read this in a constructor
Widget.add(limn.scene.Widget<?>) assigns the child's parent after the child is fully constructed, so
new Button("OK") runs with no parent and resolves to the process default whatever its
eventual parent declares. A direction captured at construction is permanently wrong with no path
to recovery, exactly as a captured ControlSize is.
Read it in onMeasure, onPaint, onLayout or an event handler, where the
tree is complete, and resolve it once per pass into a local: two resolutions that disagree
inside one onPaint put the caret on one side and the selection band on the other.
What a direction change does to a held value
A paragraph's base direction decides which bidi level a boundary neutral takes, which decides which run it extends, which decides which face measures it. A line of mixed content therefore measures a fraction of a point differently in the two directions. The amount is one face's disagreement about a space per neutral at the paragraph's edge — an interior neutral does not move at all — so it is small on any real line, and it is nonetheless enough to make a heldShapedText and a cached measurement both stale across a change —
which is why ShapedText.matches(java.lang.String, limn.graphics.Font, limn.graphics.ShapedText.Direction, limn.graphics.TextRuler) takes a direction and why
Widget.measure(limn.scene.Constraints) keys its cache on the resolved one.
A 1.x minor release may add constants to this enum: a switch over it needs a
default branch.
- See Also:
-
Nested Class Summary
Nested classes/interfaces inherited from class java.lang.Enum
Enum.EnumDesc<E extends Enum<E>> -
Enum Constant Summary
Enum Constants -
Method Summary
Modifier and TypeMethodDescriptionstatic LayoutDirectionMaps a locale to the direction its script is normally written in: the bridge betweenI18nand this axis, offered so that an application can writeLayoutDirection.setProcessDefault(LayoutDirection.forLocale(locale))at startup instead of writing the same table itself.booleanWhat a line with no strong character falls back to in a tree that reads this way: the shaper's base forShapedText.Direction.of(java.lang.String, limn.graphics.ShapedText.Direction).static SubscriptionobserveChanges(Runnable listener) Subscribes to process-default changes.opposite()static LayoutDirectionstatic voidsetProcessDefault(LayoutDirection direction) Sets the process-wide fallback direction: the root of the inheritance chain, and the one line an application that is entirely right to left writes at startup.static LayoutDirectionReturns the enum constant of this class with the specified name.static LayoutDirection[]values()Returns an array containing the constants of this enum class, in the order they are declared.
-
Enum Constant Details
-
LTR
Left to right: the default, and what every existing Limn UI renders as. -
RTL
Right to left: Arabic, Hebrew, Persian, Urdu and the rest of the right-to-left scripts.
-
-
Method Details
-
values
Returns an array containing the constants of this enum class, in the order they are declared.- Returns:
- an array containing the constants of this enum class, in the order they are declared
-
valueOf
Returns the enum constant of this class with the specified name. The string must match exactly an identifier used to declare an enum constant in this class. (Extraneous whitespace characters are not permitted.)- Parameters:
name- the name of the enum constant to be returned.- Returns:
- the enum constant with the specified name
- Throws:
IllegalArgumentException- if this enum class has no constant with the specified nameNullPointerException- if the argument is null
-
opposite
- Returns:
- the opposite direction;
LTR.opposite() == RTL
-
isRightToLeft
public boolean isRightToLeft()- Returns:
- whether this is
RTL, for thedir == RTLtests that fill widgets
-
neutralBase
What a line with no strong character falls back to in a tree that reads this way: the shaper's base forShapedText.Direction.of(java.lang.String, limn.graphics.ShapedText.Direction). A fallback and never an imposition — a Latin caption in a right-to-left tree still reads left to right, because a strong character already decided it. This is the one place that says which text direction a layout direction implies;Widget.neutralBase()reads it for the widget's own.- Returns:
ShapedText.Direction.RTLforRTL, elseShapedText.Direction.LTR
-
processDefault
- Returns:
- the direction used where nothing in the tree and no scene declares one
-
setProcessDefault
Sets the process-wide fallback direction: the root of the inheritance chain, and the one line an application that is entirely right to left writes at startup. Every live scene re-measures, overlays included and unbound scenes included; widgets and scenes that declare their own direction are unaffected. No-op when unchanged. UI thread only. -
observeChanges
Subscribes to process-default changes.- Parameters:
listener- what to run when the default direction moves- Returns:
- a handle that unsubscribes; cancelling it twice is a no-op. UI thread
-
forLocale
Maps a locale to the direction its script is normally written in: the bridge betweenI18nand this axis, offered so that an application can writeLayoutDirection.setProcessDefault(LayoutDirection.forLocale(locale))at startup instead of writing the same table itself.No widget and no scene calls this, and a widget that calls it has reintroduced the process-wide locale as a source of direction, which is the one thing this axis exists to prevent. Its one caller in the toolkit is the Windows backend's no-driver dialog, which runs where no scene exists. It is a function from a locale to a direction, at an application's call site, and a starting point rather than a policy: an application whose user has chosen a direction should honour that choice instead.
The script is consulted first, because a language can be written in either direction and only the script says which. A locale that names no script is read, as CLDR reads it, for the script its language is likely written in where the locale is: the language's own, except in the regions that change it. Punjabi runs left to right and
pa-PK, Punjabi in Pakistan, is written in Arabic and runs right to left; Sindhi runs right to left andsd-INis Devanagari; a named script still decides over the region, sopa-Guru-PKruns left to right. A region named with no language is read for the language CLDR takes it to mean, sound-SAruns right to left. Anything unrecognised isLTR, which is the same fallback the first-strong rule makes for text with no strong character.The locale is read as ICU reads it before anything is decided: a code ICU replaces with another language is that language (
pes, Iranian Persian, isfaandpan-PKispa-PK), and a region named by its UN number is the region it stands for (az-364isaz-IR), as a retired region is (und-YDisund-YE). These are the same readings the digits, the plural rules, the string bundles and the calendars take.- Parameters:
locale- the locale to classify- Returns:
RTLwhen that locale's script is written right to left, elseLTR- Throws:
NullPointerException- iflocaleis null
-