Class ColorPicker
Hue and saturation live on the widget rather than being re-derived from the colour. Grey has no hue and black has no saturation, so a colour alone cannot say where the cursor was: dragging the value down to black and back up would otherwise return red. What was last chosen survives the trip.
Alpha is a mode. setAlphaEnabled(boolean) removes the whole alpha line, rail
and number together, and color() then always returns an opaque colour. Alpha
is the last line, so losing it shortens the picker rather than rearranging it.
Two opposite answers about direction live in this one widget, and unifying them would be wrong. The rails are value axes laid along the reading axis, so their sweep, their thumb, their pointer and their horizontal arrows all turn round together when the picker reads right to left. The saturation/value field and the hue ramp do not turn round: the first is a colour space, whose white corner is a convention every artist has met and not a side of the page, and the second is vertical, which no direction touches. The ramp still changes sides, because the row holding it is what places it.
The answer is a display-range Color. An application authoring light rather
than pixels needs a multiplier beside this widget, not inside it: the swatch cannot
show what such a multiplier does, since normalising an over-range colour for display
divides it straight back out.
Not a dialog; put it in one with Dialog.setContent(limn.scene.Widget<?>), or inline it in a
panel. final, unlike most components here: it is a composite of four painted
parts wired to one model, and a subclass would be overriding paint and layout it does
not own. Compose one instead.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumWhich numeric model the channel row is showing. -
Constructor Summary
ConstructorsConstructorDescriptionA picker starting on opaque white, with the alpha line offered. -
Method Summary
Modifier and TypeMethodDescriptioncolor()The chosen colour.format()The notation the channel rows are currently showing.protected voidhandleUserChange(Change.Aspect aspect) Runs this widget's application handler for a change it has just announced.booleanWhether the alpha line is offered; when off,color()is always opaque.protected voidWhat the picker is to an assistive technology: oneAccessible.Role.COLOR_CHOOSERnode, and nothing else on it.Fires on every move: a picker shows its answer live, it does not wait for OK.Fires once with the settled colour when a drag ends, the difference between a preview and a decision, and the same splitSlider.onChange/Slider.onCommitmakes.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 floatThe 8 points the saturation/value cursor's ring can hang past the picker's box.setAlphaEnabled(boolean enabled) Whether the picker offers alpha at all (defaulttrue).Moves the selection.setFormat(ColorPicker.Format format) Which numeric model the channel rows show.setInitialColor(Color color) Sets the colour and the "before" swatch: this is what opening the picker on an existing colour means.Methods inherited from class limn.scene.Widget
acceptsTextInput, accessibleLabelAccessKey, accessibleLabelTarget, accessibleLabelText, add, add, announceChange, backdropReach, baselineOffset, baselineOffsetOf, caretRect, children, clipboard, clipHeight, clipsChildren, clipWidth, clipX, clipY, controlSize, cursor, declaredControlSize, declaredLayoutDirection, declaredLocale, focusArrivedBackward, focusArrivedByTraversal, height, hitTest, hitTestChild, id, imageCursor, inheritanceHost, invalidate, invalidate, invalidateAccessible, isEnabled, isFocusable, isFocused, isRightToLeft, isSelfEnabled, isShowing, isVisible, isVisibleByItsOwnFlag, layoutBox, layoutDirection, locale, localToScene, localToSceneBounds, markNeedsContainedLayout, markNeedsLayout, markNeedsLayoutInPlace, maxHeight, maxWidth, measure, minHeight, minWidth, moveChild, moveChild, moveChild, neutralBase, notifyChange, notifyTextEdit, observeChanges, onAccessibilityAction, onAccessibilityChild, onAccessibilityChildAction, onAccessibilityChildIdentity, onAttached, onCanEdit, onCharTyped, onDetached, onDragExit, onDragOver, onDragStart, onDrop, onEdit, onFocusGained, onFocusLost, onHitTest, onMouseEvent, onPaint, onPaintOverlay, onPreedit, onSyntheticAction, overlayPassesPointer, paintChildren, paintsDecoration, paintsFromBackdrop, paintWidget, parent, remove, remove, requestFocus, requestFocus, revealInView, revealInView, runHandler, scene, sceneNanos, sceneToLocal, self, setAccessibleDescribedBy, setAccessibleDescription, setAccessibleDescription, setAccessibleIgnored, setAccessibleLabelledBy, setAccessibleName, setAccessibleName, setAccessibleRole, setControlSize, setCursor, setEnabled, setFocusable, setId, setImageCursor, setInheritanceHost, setLayoutDirection, setLocale, setMaxHeight, setMaxWidth, setMinHeight, setMinWidth, setPresentedElsewhere, setTooltip, setTooltip, setVisible, shapeText, textRuler, tooltip, tooltipSource, width, x, y
-
Constructor Details
-
ColorPicker
public ColorPicker()A picker starting on opaque white, with the alpha line offered.
-
-
Method Details
-
color
The chosen colour. Opaque unlesssetAlphaEnabled(boolean)says otherwise. -
setInitialColor
Sets the colour and the "before" swatch: this is what opening the picker on an existing colour means. UsesetColor(limn.graphics.Color)to move the selection without moving the comparison. -
setColor
Moves the selection. Hue and saturation are taken fromcoloronly when it has them: a grey has no hue to read, and overwriting the current one with zero would swing the field to red for no reason the artist can see. -
setAlphaEnabled
Whether the picker offers alpha at all (defaulttrue). Turning it off hides the ramp and the field and makescolor()opaque, for the many things being coloured that cannot be translucent.Announces
RANGE/CODEfor the flag, and before it, when turning alpha off moved the colour,VALUE/ADJUSTMENT: the colour moved as a consequence of the call, which named alpha and not the colour. -
isAlphaEnabled
public boolean isAlphaEnabled()Whether the alpha line is offered; when off,color()is always opaque. -
setFormat
Which numeric model the channel rows show. The visual field is always HSV.The one entry point, whichever end it came from: a tab the user clicked arrives here through the picker's watcher on the pane, and this pushes the selection back the other way for a caller that set it. No guard is needed for the round trip: the format is written first, so the pane's announcement finds the picker already there and the watcher returns.
-
format
The notation the channel rows are currently showing. -
onChange
Fires on every move: a picker shows its answer live, it does not wait for OK. That makes a delivery a preview: seeonCommitfor the one that is a decision. Never forsetColor(limn.graphics.Color),setInitialColor(limn.graphics.Color)orsetAlphaEnabled(boolean), which are a caller's writes; to hear every move whatever caused it, watch the picker instead.- Parameters:
listener- the handler, ornullto clear the slot- Returns:
- this picker
- Throws:
IllegalStateException- if a handler is already registered
-
onCommit
Fires once with the settled colour when a drag ends, the difference between a preview and a decision, and the same splitSlider.onChange/Slider.onCommitmakes. This is where an undo entry is closed, or a value written to a document. A typed hex, a typed or stepped channel and a typed alpha commit too, because a key press is a whole gesture.- Parameters:
listener- the handler, ornullto clear the slot- Returns:
- this picker
- Throws:
IllegalStateException- if a handler is already registered
-
handleUserChange
Description copied from class:WidgetRuns this widget's application handler for a change it has just announced. Called byWidget.runHandler(limn.scene.Change), and so byWidget.notifyChange(limn.scene.Change), only for aUSERorigin and after every watcher has run.A component overrides it once and switches on the aspect, reading its own accessors for the payload: a slider passes
value(), a combo boxselectedIndex(), a text fieldtext(). A component with no handler does not override it.An override that does not recognise the aspect must call
super, because a subclass of a component that has a handler inherits that component's dispatch through this method: aSearchFieldthat overrode this for its own aspect and did not chain would silently deleteTextField's. The ordering lives in the base class, this is the one link in it a subclass can break, and the contract test drives every subclass through its parent's gestures to catch one that does.- Overrides:
handleUserChangein classWidget<ColorPicker>- Parameters:
aspect- what changed; the origin isUSERor this is not called
-
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.- Specified by:
onMeasurein classWidget<ColorPicker>
-
onLayout
protected void onLayout()Description copied from class:WidgetContainers position children here (measure +Widget.layoutBox(float, float, float, float)per child).- Overrides:
onLayoutin classWidget<ColorPicker>
-
paintOutset
protected float paintOutset()The 8 points the saturation/value cursor's ring can hang past the picker's box. The field's top edge is the picker's top and its leading edge the picker's leading edge, and the ring is centred on the exact colour, so parked on white, on the pure hue or anywhere along those two edges, half of it lies outside the picker. Declared so that a repaint pass landing only in that band, where a neighbour above or beside the picker repaints, still paints the picker rather than skipping it and wiping the ring's arc there.- Overrides:
paintOutsetin classWidget<ColorPicker>
-
onKeyEvent
Description copied from class:WidgetKey events (focused widget first, then ancestors).- Overrides:
onKeyEventin classWidget<ColorPicker>
-
onAccessibility
What the picker is to an assistive technology: oneAccessible.Role.COLOR_CHOOSERnode, and nothing else on it. Everything a reader can read or operate is a real widget under it with a node of its own — the hex field, the notation tabs, each channel's rail and number, the alpha line — so the chooser is the container those are read inside and not a second copy of what they say.The role is not optional here, and that is the whole reason this hook exists. The picker is focusable from its constructor, because the arrow keys walk the saturation/value plane, and a focusable widget survives the transparency predicate whatever it declares. One that declares no role is published as an unknown control and names this class in an application's log, which is a defect a toolkit class may not ship.
No name is declared. The widget holds no title, no placeholder and no tooltip, so it has nothing to hand over: an application names it with
setAccessibleName, and a picker put in a dialog is named by the dialog around it.And no value text holding the hex, which is the obvious thing to publish and is wrong twice over. A text with no number beside it is dropped by the builder, and a value change is raised only when a number moves, so such a text would cost a formatted string on every drag step and reach nobody; and a colour has no honest scalar to pair it with. The colour is readable where it is shown, in the hex field's own text and in the channel numbers, each of which is a node in its own right.
No step verbs either. This widget's arrows move two axes — saturation across, value up — and one pair of verbs cannot say which; the plane they walk is the saturation/value field's box rather than this one, so a verb for it belongs on the node carrying that box.
The box, the language, the children in tree order, the enabled, visible, showing, focusable and focused bits, and the focus and scroll-into-view verbs are all the walk's, and are correct without help: the root column is laid out over the whole of this widget, so every descendant's rectangle is inside the chooser's.
- Overrides:
onAccessibilityin classWidget<ColorPicker>- Parameters:
a- the node being described
-