Module: Tuile::Component::HasValue
- Extended by:
- Listeners::Declare
- Includes:
- HasValidation
- Included in:
- AbstractStringField, AbstractWrappingField, Checkbox, CheckboxGroup, ComboBox, DateTimeField, RadioGroup, Select
- Defined in:
- lib/tuile/component/has_value.rb,
sig/tuile.rbs
Overview
The value seam every input component shares: a settable/gettable #value
of any type, an #on_value_change listener, #empty?, and #clear. A
form (a future binder) drives a mix of field types uniformly through it,
not caring that a TextField's value is a String while another field's
is a domain object.
field.on_value_change { |e| puts "now: #{e.value.inspect}" }
field.value = "hello" # fires the listener, e.from_user? false
field.clear # value = empty_value, fires again
Who made the change
Every event tells typing from a programmatic write, so a listener keeps no guard flag of its own:
field.on_value_change { |e| open_palette if e.from_user? }
field.value = "x" # e.from_user? is false
field.set_value("x", from_user: true) # an app writing for the user
The default #set_value/#value keep the value in @value and are
enough for a component with nothing more natural — you get a repaint and
the listener for free. An includer whose value lives elsewhere overrides
both (AbstractStringField backs them with its text buffer); the
override point is #set_value, never #value=, which is only its
from_user: false spelling. Override #empty_value when the empty
sentinel isn't nil (a text field's is "").
HasValidation comes with it, so every field carries the
error_message a validator writes and paints its own error ink.
Implementation details
Deliberately smaller than Vaadin's HasValue: read-only,
required-indicator and converters belong to the not-yet-built form layer,
not here. Of Vaadin's event payload, isFromClient is carried (as
ValueChangeEvent#from_user?) and getOldValue is not, until something
reads it (D_from_user).
Defined Under Namespace
Classes: ValueChangeEvent
Class Method Summary collapse
-
.listener ⇒ void
Defines the slot's reader, which returns the Listeners — or, given a block, registers it and returns the
Proc.
Instance Method Summary collapse
-
#clear ⇒ void
Resets #value to #empty_value.
-
#empty? ⇒ Boolean
Empty of value: a field whose parse is partial reports
truewhile the user is looking at glyphs it could not use, so ask Tuile::Component::HasBadInput#bad_input? first. - #empty_value ⇒ Object
-
#error_bg_color ⇒ Color?
The invalid well, picked up by everything this component paints — including the inner face of a composed field and the List of a group, neither of which forwards anything: both declare no background of their own, so the ordinary chain walks up to this (overrides #error_bg_color).
-
#error_ink? ⇒ Boolean
Whether to paint the invalid well right now.
-
#error_message ⇒ StyledString?
@return — why the field is invalid, or
nilwhen it is not;niluntil something sets it. -
#error_message= ⇒ void
Sets the verdict and repaints the field in Theme#error_color;
nilclears it. -
#focusable? ⇒ Boolean
Input fields are focusable by default (overrides #focusable?); a read-only display field could override back to
false. -
#inspect_details ⇒ ::Array[String]
Adds
value=…to #inspect, omitted while the value is nil. -
#on_error_message_change ⇒ Listeners
Fired with an Tuile::Component::HasValidation::ErrorMessageChangeEvent whenever #error_message actually changes — never on a no-op set.
-
#on_value_change ⇒ Listeners
Fired with a ValueChangeEvent whenever #value actually changes — never on a no-op set.
-
#set_value(new_value, from_user:) ⇒ void
Writes the value and fires #on_value_change carrying
from_user. -
#shown_message ⇒ StyledString, ...
The message a consumer with cells of its own paints beside the field: the verdict here, and HasBadInput widens it to prefer the field's own report, which outranks a verdict computed a pass ago.
-
#value ⇒ Object
@return — the current value;
niluntil first set. -
#value=(new_value) ⇒ void
A programmatic write: #set_value with
from_user: false.
Methods included from Listeners::Declare
Class Method Details
.listener ⇒ void
This method returns an undefined value.
Defines the slot's reader, which returns the Listeners — or, given a
block, registers it and returns the Proc.
An optional block is the slot's transition block, run on the owner whenever the list becomes non-empty or empty again. It is what lets a widget install a bridge only while somebody is listening:
listener :on_enter do |claimed|
claimed ? editor.on_enter << @bridge : editor.on_enter.remove(@bridge)
end
The list is built on first read, so no mixin has to remember a
constructor line — which means in-class code goes through the reader and
never touches @on_foo, nil until somebody asks.
@param name — the slot's full name, on_-prefixed. Spelling it out is what keeps on_value_change greppable from its declaration.
@return — name.
8786 |
# File 'sig/tuile.rbs', line 8786
def self.listener: (Symbol name) ?{ (bool claimed) -> void } -> Symbol
|
Instance Method Details
#clear ⇒ void
This method returns an undefined value.
Resets #value to #empty_value.
An includer whose input can outrun its value (Tuile::Component::HasBadInput) must clear
the input: a field holding bad input already reads empty_value, so
inheriting this default — over a #set_value that returns early on a
no-op set — is a clear that leaves the garbage on screen. A clear is
programmatic: its event says from_user? == false.
116 |
# File 'lib/tuile/component/has_value.rb', line 116 def clear = (self.value = empty_value) |
#empty? ⇒ Boolean
Empty of value: a field whose parse is partial reports true while the
user is looking at glyphs it could not use, so ask
Tuile::Component::HasBadInput#bad_input? first.
@return — true iff #value equals #empty_value.
106 |
# File 'lib/tuile/component/has_value.rb', line 106 def empty? = value == empty_value |
#empty_value ⇒ Object
120 |
# File 'lib/tuile/component/has_value.rb', line 120 def empty_value = nil |
#error_bg_color ⇒ Color?
The invalid well, picked up by everything this component paints — including the inner face of a composed field and the List of a group, neither of which forwards anything: both declare no background of their own, so the ordinary chain walks up to this (overrides Tuile::Component#error_bg_color).
8826 |
# File 'sig/tuile.rbs', line 8826
def error_bg_color: () -> Color?
|
#error_ink? ⇒ Boolean
Whether to paint the invalid well right now. Its own hook because Tuile::Component::HasBadInput widens it: a field holding input its value cannot represent is invalid on the face too, even with no verdict written.
8831 |
# File 'sig/tuile.rbs', line 8831
def error_ink?: () -> bool
|
#error_message ⇒ StyledString?
@return — why the field is invalid, or nil when it
is not; nil until something sets it.
8796 |
# File 'sig/tuile.rbs', line 8796
def error_message: () -> StyledString?
|
#error_message= ⇒ void
This method returns an undefined value.
Sets the verdict and repaints the field in Theme#error_color; nil
clears it. No-op (no repaint, no listener) when unchanged. A String is
parsed via StyledString.parse, as Tuile::Component::HasCaption#caption= does.
Safe on a detached field — an app validates a form it assembled but has not mounted, and Tuile::Component#invalidate is already a no-op there.
@param new_message
8806 |
# File 'sig/tuile.rbs', line 8806
def error_message=: ((String | StyledString)? new_message) -> void
|
#focusable? ⇒ Boolean
Input fields are focusable by default (overrides Tuile::Component#focusable?);
a read-only display field could override back to false. Only
focusable? lives here — tab_stop? diverges between leaf fields and
composing wrappers, so it stays per-class (design/decisions.md
D_integer_field).
128 |
# File 'lib/tuile/component/has_value.rb', line 128 def focusable? = true |
#inspect_details ⇒ ::Array[String]
Adds value=… to Tuile::Component#inspect, omitted while the value is nil.
134 135 136 137 138 139 140 141 142 |
# File 'lib/tuile/component/has_value.rb', line 134 def inspect_details v = value return super if v.nil? # Truncate before #inspect, not after: a TextArea's value is its whole # buffer. v = "#{v[0, 40]}…" if v.is_a?(String) && v.length > 40 super + ["value=#{v.inspect}"] end |
#on_error_message_change ⇒ Listeners
Fired with an Tuile::Component::HasValidation::ErrorMessageChangeEvent whenever #error_message actually changes — never on a no-op set. The container that paints the message registers here, and so may an app painting its own: the list takes both, which a single slot could not.
8792 |
# File 'sig/tuile.rbs', line 8792
def on_error_message_change: () -> Listeners
|
#on_value_change ⇒ Listeners
Fired with a ValueChangeEvent whenever #value actually changes — never on a no-op set.
65 |
# File 'lib/tuile/component/has_value.rb', line 65 listener :on_value_change |
#set_value(new_value, from_user:) ⇒ void
This method returns an undefined value.
Writes the value and fires #on_value_change carrying from_user.
No-op (no repaint, no listener) when equal to the current value.
field.set_value(Date.today, from_user: true) # a "Today" button's click
The flag is the writer's claim, never derived from key dispatch and
never checked: pass true when the write is a user's gesture — the
gem's own key and mouse handlers do, as does Testing.set_value — and
false for what the app decides alone. A wrong flag is silent, and
false is the side that fails safe (D_from_user).
The override point for an includer: call super with from_user:.
@param new_value
@param from_user
94 95 96 97 98 99 100 |
# File 'lib/tuile/component/has_value.rb', line 94 def set_value(new_value, from_user:) return if value == new_value @value = new_value invalidate on_value_change.fire(ValueChangeEvent.new(source: self, value: new_value, from_user:)) end |
#shown_message ⇒ StyledString, ...
The message a consumer with cells of its own paints beside the field: the verdict here, and Tuile::Component::HasBadInput widens it to prefer the field's own report, which outranks a verdict computed a pass ago.
field.on_error_message_change { message_label.caption = field.shown_message.to_s }
field.on_bad_input_change { message_label.caption = field.shown_message.to_s }
Register on both: two channels with two writers, either of which moves it.
@return — nil when there is nothing to show;
a plain String when it is the field's own bad-input report.
8819 |
# File 'sig/tuile.rbs', line 8819
def shown_message: () -> (StyledString | String)?
|
#value ⇒ Object
@return — the current value; nil until first set.
68 |
# File 'lib/tuile/component/has_value.rb', line 68 def value = @value |
#value=(new_value) ⇒ void
This method returns an undefined value.
A programmatic write: #set_value with from_user: false. Defined
here once and never overridden — override #set_value instead, since a
setter has no call syntax for the keyword.
@param new_value
75 76 77 |
# File 'lib/tuile/component/has_value.rb', line 75 def value=(new_value) set_value(new_value, from_user: false) end |