Class: Tuile::Component::TimeField
- Inherits:
-
AbstractWrappingField
- Object
- Component
- AbstractWrappingField
- Tuile::Component::TimeField
- Includes:
- HasBadInput
- Defined in:
- lib/tuile/component/time_field.rb,
sig/tuile.rbs
Overview
A single-line field whose #value is a time of day — a wall-clock
reading with no date and no zone, carried as a Time pinned to
MIDNIGHT. Give it a single-row #rect:
field = Component::TimeField.new
field.on_value_change { |e| puts e.value&.strftime("%H:%M") }
field.set_to(13, 45) # field shows "13:45"
field.placeholder # => "hh:mm"
field.value # => 2000-01-01 13:45:00 UTC
Up/Down step by #step and PageUp/PageDown by an hour (an empty field steps to now), wrapping at midnight — a clock has no day to carry into.
The value is an instant, and that is a real cost
There is no civil-time class in Ruby, so the value is a Time on a fixed
epoch date in UTC — which means it is a perfectly good Time that is
wrong anywhere an instant was meant:
field.value = Time.now # takes the wall clock it reads
field.value == Time.now # => false — different date, different zone
An app that forgets to combine it with a date gets the year 2000 in its output, which is visible rather than subtly wrong. Combine it with a date at your own boundary; MIDNIGHT names the epoch.
Precision is the step, not a format
Seconds are shown exactly when #step is under a minute, and there is no per-field format setter at all:
field.step = 1 # now shows "13:45:00", Up walks a second
field.step = 60 # back to "13:45" — the default
field.formats # a report of the list in force
The spelling — separator, digit order, 12- or 24-hour — comes from
Screen#locale and survives that switch, so a Finnish user sees 13.45
and 13.45.00 rather than a colon either way. An app wanting a spelling
the probe did not find sets it for the session, and every field follows:
screen.locale = Locale::ISO.with(time_formats: ["%I:%M:%S %p"])
Input the field cannot parse is reported, not filtered
A time's grammar is not prefix-closed ("1" is a prefix of "13:45" and
is not a time), so nothing is filtered: every character is admitted, typed
or pasted, and the residue is reported through HasBadInput. A form asks
HasBadInput#bad_input? before HasValue#empty?, since a field full of
garbage reads nil:
field.value # => nil
field.empty? # => true — empty of *value*
field.bad_input? # => true
The red well, unlike that report, waits for a commit gesture: since
every prefix of a time is bad input, 1, 13, 13: stay quiet, leaving
the field (or pressing ENTER) reddens what did not parse, and the next
edit clears it again.
The value notice waits for the same gesture
A prefix of a time can also parse cleanly: typing 13:45 passes
through 13:4, a perfectly good four minutes past one. So
HasValue#on_value_change does not fire per keystroke, but when the user
leaves the field or presses ENTER — and a form recalculating from it
never sees those intermediate readings.
#value does not wait: it is a live parse of the buffer at every moment, so a save gate reached without leaving the field reads the time on screen. Nor does a change nobody had to type — a AbstractWrappingField#value=, a #set_to, an arrow-key step, a AbstractWrappingField#clear and a reparse under a new #step all fire as they happen.
Implementation details
- The buffer is the single source of truth. #value is a parse of it, recomputed on read — so #step= and a Screen#locale= can change the value with no edit, and a buffer the field cannot parse is left exactly as typed, because the user has to see what they wrote in order to fix it.
- Narrowing #step= does not truncate.
13:45:30stays in the buffer when the step widens back past a minute, and simply reads as bad input — the field never discards a second a user meant.13:45:00does narrow, because dropping a zero discards nothing. 24:00is rejected, though ISO 8601 permits it as an end-of-day:Timecannot hold it, andTime.utc(…, 24, 0, 0)is silently the next day. So is a 60th second (Locale::TimeFormats.parse).- Canonicalizing fires no HasValue#on_value_change — the spelling changed, not the value. Up/Down canonicalize too, since they go through AbstractWrappingField#value=.
UI-thread-confined, like every component (see Screen).
Constant Summary collapse
- MIDNIGHT =
The epoch every value sits on, midnight UTC — matching what Rails'
timecolumn casts to, so an ActiveRecord round-trip is exact.UTC, not local, and that is not a detail: a local epoch would put every value on a date whose offset the zone can change, making some wall times unrepresentable or silently shifted — the whole DST class, removed.
Time.utc(2000, 1, 1).freeze
- SECONDS_PER_DAY =
86_400- SECONDS_PER_HOUR =
Returns the PageUp/PageDown stride.
3600- SECONDS_VISIBLE_BELOW =
The stride below which the field shows seconds. See #step=.
60- DEFAULT_STEP =
60- BAD_INPUT_MESSAGE =
Returns what HasBadInput#bad_input_message reports for a buffer no format in force parses.
"not a valid time"- MAX_TEXT_LENGTH =
No time format renders past ~20 columns, so this caps nothing a locale supplies — it stops a pasted novel from sitting in the buffer.
64
Instance Attribute Summary collapse
-
#step ⇒ Integer
@return — how many seconds Up/Down move the value, and — under a minute — the reason seconds are shown at all.
Attributes inherited from AbstractWrappingField
Class Method Summary collapse
-
.time_of_day(hour, minute, second = 0) ⇒ Time
Builds a value, not a field: a
Timeto compare one a field handed back against, without hand-writing the epoch.
Instance Method Summary collapse
-
#advance(time, delta) ⇒ Time
@param
time. -
#bad_input? ⇒ Boolean
@return — true iff the field is holding input its value cannot represent.
-
#bad_input_message ⇒ String?
Nothing a format parses is bad input, and an empty buffer is empty rather than bad (HasBadInput) — so this reports the residue of a grammar that cannot be filtered as it is typed: every prefix of a time, and everything that is simply not one.
-
#bad_input_settled? ⇒ Boolean
Every prefix of a time is bad input, so the report is latched to the commit gestures instead of shown per keystroke:
1,13,13:on the way to13:45never redden and announce nothing, and a time the field cannot parse reddens the moment the user leaves the field or presses ENTER (HasBadInput). -
#coerce(new_value) ⇒ Time
@param
new_value. -
#commit ⇒ void
Rewrites a buffer that parses in the primary format, leaving one that does not exactly as the user typed it — and settles the field either way, so input it could not parse starts painting the well.
-
#derive_formats(current) ⇒ ::Array[String]
@param
current. -
#derived_placeholder ⇒ String?
@return — the hint for the primary format, or
nilwhen it holds a directive the humanizer cannot translate exactly — never a half-translated one. -
#empty_value ⇒ void
nil, not"": a time field with no parseable time is empty. -
#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
Widens HasValidation#error_ink?: bad input paints the invalid well too, with no verdict written — once it is showable, and unless a child wears it instead.
-
#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. -
#formats ⇒ ::Array[String]
The formats in force, primary first — the locale's spelling (Locale#time_formats) reduced to this field's precision, with the seconds-bearing forms in front when #step shows seconds so that typing
13:45still parses and widens to13:45:00. -
#handle_editor_change ⇒ void
An edit is the user having another go, so the well goes quiet again until the next commit gesture.
-
#handle_key?(key) ⇒ Boolean
Claims PageUp/PageDown for the hour step; they reach this field only because the editor declines them.
- #handle_locale_changed ⇒ void
-
#initialize ⇒ TimeField
constructor
A new instance of TimeField.
-
#inspect_details ⇒ ::Array[String]
Adds
error_message=…to #inspect, omitted while valid — so a Testing.get failure dump says which field is already flagged. -
#losslessly(time) ⇒ Time?
A narrowing #step= must not discard seconds the user typed — but dropping a zero second discards nothing, and reddening
13:45:00for switching to minute display would be a bug rather than a report. -
#notify_on_edit? ⇒ Boolean
false: a prefix of a time can parse cleanly (13:4for13:45), so the notice settles onto the commit gestures, exactly as the ink does. -
#now ⇒ Time
@return — the local wall clock, truncated to this field's precision — landing on now rather than a stride away from it.
-
#on_bad_input_change ⇒ Listeners
Fired with a HasBadInput::BadInputChangeEvent whenever the showable report changes — see the class doc for what that is and why it is not #bad_input?.
-
#on_error_message_change ⇒ Listeners
Fired with an HasValidation::ErrorMessageChangeEvent whenever #error_message actually changes — never on a no-op set.
-
#placeholder=(text) ⇒ void
Overrides the hint derived from the primary format.
-
#reformat(carried = nil) ⇒ void
Re-derives the hint (which was pushed into the editor, so a repaint alone would keep the old one) and rewrites a buffer that still parses — the one path a #step= and a Screen#locale= share, since both mean "the format list changed under a buffer".
- #seconds_hidden? ⇒ Boolean
-
#set_to(hour, minute, second = 0) ⇒ void
Sets the value from its parts, so nothing assembles a
Timeon the epoch only to hand it straight back. -
#set_to_now ⇒ void
Sets the value to the local wall clock, truncated to this field's precision — the same place Up/Down land an empty field.
-
#set_value(new_value, from_user:) ⇒ void
Writes
new_valueinto the buffer in the primary format and parks the caret at its end; fires HasValue#on_value_change only if the value actually changed. -
#settle(flag) ⇒ void
@param
flag. -
#shown_message ⇒ StyledString, ...
The field's own report when it is showable, else whatever HasValidation#shown_message has — bad input outranks a verdict.
-
#step_by(delta) ⇒ void
Steps #value by
deltaseconds; an empty or unparseable field steps to now (as #set_to_now would) instead, anddeltais ignored. -
#sync_bad_input ⇒ void
Fires #on_bad_input_change when the showable report has really changed — the sole writer of
@last_bad_input, called from wherever an input of that expression moves. - #sync_placeholder ⇒ void
-
#value ⇒ Time?
@return — the buffer parsed by the first format that matches it whole, on MIDNIGHT's date;
nilwhen the buffer is empty or no format parses it. -
#wears_bad_input_ink? ⇒ Boolean
Whether this component paints the well for its own report.
Methods included from HasBadInput
Methods included from Listeners::Declare
Methods included from HasValidation
Methods inherited from AbstractWrappingField
#active=, #clear, #commit_and_notify, #cursor_position, #empty?, #fire_if_changed, #focusable?, #handle_focus, #on_enter, #on_value_change, #placeholder, #relayout, #value=
Methods included from HasPlaceholder
Methods included from HasValue
#clear, #empty?, #focusable?, listener, #on_value_change, #value=
Constructor Details
#initialize ⇒ TimeField
Returns a new instance of TimeField.
155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/tuile/component/time_field.rb', line 155 def initialize super(TextField.new) editor.max_text_length = MAX_TEXT_LENGTH # Claiming the editor's two arrow slots, not the general interceptor: # that one stays free for the app. editor.on_key_up { step_by(@step) } editor.on_key_down { step_by(-@step) } @settled = false @placeholder_override = nil @step = DEFAULT_STEP sync_placeholder end |
Instance Attribute Details
#step ⇒ Integer
@return — how many seconds Up/Down move the value, and — under a minute — the reason seconds are shown at all. See #step=.
235 236 237 |
# File 'lib/tuile/component/time_field.rb', line 235 def step @step end |
Class Method Details
.time_of_day(hour, minute, second = 0) ⇒ Time
Builds a value, not a field: a Time to compare one a field handed
back against, without hand-writing the epoch.
Component::TimeField.time_of_day(13, 45) # => 2000-01-01 13:45:00 UTC
field.value == Component::TimeField.time_of_day(9, 30)
To set a field, #set_to is shorter and reads as the mutation it is.
@param hour — 0..23.
@param minute — 0..59.
@param second — 0..59.
@return — on MIDNIGHT's date, in UTC.
146 147 148 149 150 151 152 153 |
# File 'lib/tuile/component/time_field.rb', line 146 def self.time_of_day(hour, minute, second = 0) unless Locale::TimeFormats.in_range?(hour, minute, second) raise ArgumentError, "expected a time of day (0..23, 0..59, 0..59), got #{[hour, minute, second].inspect}" end Time.utc(MIDNIGHT.year, MIDNIGHT.month, MIDNIGHT.day, hour, minute, second) end |
Instance Method Details
#advance(time, delta) ⇒ Time
@param time
@param delta — seconds, either sign.
@return — wrapped into the day: 23:59 + a minute is 00:00.
450 |
# File 'lib/tuile/component/time_field.rb', line 450 def advance(time, delta) = MIDNIGHT + (((time - MIDNIGHT).to_i + delta) % SECONDS_PER_DAY) |
#bad_input? ⇒ Boolean
@return — true iff the field is holding input its value cannot represent.
10727 |
# File 'sig/tuile.rbs', line 10727
def bad_input?: () -> bool
|
#bad_input_message ⇒ String?
Nothing a format parses is bad input, and an empty buffer is empty rather than bad (HasBadInput) — so this reports the residue of a grammar that cannot be filtered as it is typed: every prefix of a time, and everything that is simply not one.
322 |
# File 'lib/tuile/component/time_field.rb', line 322 def = value.nil? && !editor.text.empty? ? BAD_INPUT_MESSAGE : nil |
#bad_input_settled? ⇒ Boolean
Every prefix of a time is bad input, so the report is latched to the
commit gestures instead of shown per keystroke: 1, 13, 13: on the
way to 13:45 never redden and announce nothing, and a time the field
cannot parse reddens the moment the user leaves the field or presses
ENTER (HasBadInput).
330 |
# File 'lib/tuile/component/time_field.rb', line 330 def bad_input_settled? = @settled |
#coerce(new_value) ⇒ Time
@param new_value
430 431 432 433 434 435 436 |
# File 'lib/tuile/component/time_field.rb', line 430 def coerce(new_value) unless %i[hour min sec].all? { new_value.respond_to?(_1) } raise TypeError, "expected a time of day answering hour/min/sec, got #{new_value.inspect}" end self.class.time_of_day(new_value.hour, new_value.min, new_value.sec) end |
#commit ⇒ void
This method returns an undefined value.
Rewrites a buffer that parses in the primary format, leaving one that does not exactly as the user typed it — and settles the field either way, so input it could not parse starts painting the well.
352 353 354 355 356 357 |
# File 'lib/tuile/component/time_field.rb', line 352 def commit time = value # The user's: a commit gesture, and a held notice announces here. set_value(time, from_user: true) unless time.nil? # …which unsettles, hence the order settle(true) end |
#derive_formats(current) ⇒ ::Array[String]
@param current
@return — frozen.
412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/tuile/component/time_field.rb', line 412 def derive_formats(current) spellings = current.time_formats stripped = spellings.map { Locale::TimeFormats.strip_seconds(_1) }.uniq return stripped.freeze if seconds_hidden? full = spellings.select { Locale::TimeFormats.seconds?(_1) } # A locale whose own spelling stops at minutes has no seconds form to # widen into, and splicing a separator would be inventing one. full = [Locale::ISO.time_formats.first] if full.empty? (full + stripped).uniq.freeze end |
#derived_placeholder ⇒ String?
@return — the hint for the primary format, or nil when it
holds a directive the humanizer cannot translate exactly — never a
half-translated one.
481 |
# File 'lib/tuile/component/time_field.rb', line 481 def derived_placeholder = Locale::TimeFormats.humanize(formats.first) |
#empty_value ⇒ void
This method returns an undefined value.
nil, not "": a time field with no parseable time is empty.
231 |
# File 'lib/tuile/component/time_field.rb', line 231 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).
10774 |
# File 'sig/tuile.rbs', line 10774
def error_bg_color: () -> Color?
|
#error_ink? ⇒ Boolean
Widens HasValidation#error_ink?: bad input paints the invalid well too, with no verdict written — once it is showable, and unless a child wears it instead.
10736 |
# File 'sig/tuile.rbs', line 10736
def error_ink?: () -> bool
|
#error_message ⇒ StyledString?
@return — why the field is invalid, or nil when it
is not; nil until something sets it.
10757 |
# File 'sig/tuile.rbs', line 10757
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 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
10767 |
# File 'sig/tuile.rbs', line 10767
def error_message=: ((String | StyledString)? new_message) -> void
|
#formats ⇒ ::Array[String]
The formats in force, primary first — the locale's spelling
(Locale#time_formats) reduced to this field's precision, with the
seconds-bearing forms in front when #step shows seconds so that
typing 13:45 still parses and widens to 13:45:00.
field.formats # => ["%H:%M"] at the default step
field.step = 1
field.formats # => ["%H:%M:%S", "%H:%M"]
A report, not a request — there is deliberately no writer. The spelling is a session convention (Screen#locale=) and the precision is #step; a per-field override would be a third authority over one fact.
@return — frozen.
288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/tuile/component/time_field.rb', line 288 def formats current = locale # Keyed on both inputs rather than snapshotted, since this is read on # every repaint (through HasBadInput#error_ink?) and derives its answer # with a StringScanner per entry. if !@formats_locale.equal?(current) || @formats_step != @step @formats_locale = current @formats_step = @step @formats = derive_formats(current) end @formats end |
#handle_editor_change ⇒ void
This method returns an undefined value.
An edit is the user having another go, so the well goes quiet again until the next commit gesture.
368 369 370 371 372 373 |
# File 'lib/tuile/component/time_field.rb', line 368 def handle_editor_change # Unsettled before the sync riding `super`, which would otherwise read # the new buffer against the old latch. settle(false) super end |
#handle_key?(key) ⇒ Boolean
Claims PageUp/PageDown for the hour step; they reach this field only because the editor declines them.
@param key
@return — true for the two page keys, else whatever super
returns.
337 338 339 340 341 342 343 344 |
# File 'lib/tuile/component/time_field.rb', line 337 def handle_key?(key) case key when Keys::PAGE_UP then step_by(SECONDS_PER_HOUR) when Keys::PAGE_DOWN then step_by(-SECONDS_PER_HOUR) else return super end true end |
#handle_locale_changed ⇒ void
This method returns an undefined value.
376 377 378 379 |
# File 'lib/tuile/component/time_field.rb', line 376 def handle_locale_changed super reformat end |
#inspect_details ⇒ ::Array[String]
Adds error_message=… to Tuile::Component#inspect, omitted while valid — so
a Testing.get failure dump says which field is already flagged.
10778 |
# File 'sig/tuile.rbs', line 10778
def inspect_details: () -> ::Array[String]
|
#losslessly(time) ⇒ Time?
A narrowing #step= must not discard seconds the user typed — but
dropping a zero second discards nothing, and reddening 13:45:00 for
switching to minute display would be a bug rather than a report. So a
value the new primary can still write exactly is carried across.
@param time
@return — time if the new primary round-trips it, else nil.
403 404 405 406 407 408 |
# File 'lib/tuile/component/time_field.rb', line 403 def losslessly(time) return nil if time.nil? primary = formats.first Locale::TimeFormats.parse(time.strftime(primary), primary, MIDNIGHT) == time ? time : nil end |
#notify_on_edit? ⇒ Boolean
false: a prefix of a time can parse cleanly (13:4 for 13:45), so
the notice settles onto the commit gestures, exactly as the ink does.
The class docs carry the case.
363 |
# File 'lib/tuile/component/time_field.rb', line 363 def notify_on_edit? = false |
#now ⇒ Time
@return — the local wall clock, truncated to this field's precision — landing on now rather than a stride away from it.
454 455 456 457 |
# File 'lib/tuile/component/time_field.rb', line 454 def now wall = Time.now self.class.time_of_day(wall.hour, wall.min, seconds_hidden? ? 0 : wall.sec) end |
#on_bad_input_change ⇒ Listeners
Fired with a HasBadInput::BadInputChangeEvent whenever the showable report changes — see the class doc for what that is and why it is not #bad_input?.
Empty means nobody outside the field is showing the report, which is the common case: the red well needs no notice, since it reads the pull on every paint.
10723 |
# File 'sig/tuile.rbs', line 10723
def on_bad_input_change: () -> Listeners
|
#on_error_message_change ⇒ Listeners
Fired with an 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.
10753 |
# File 'sig/tuile.rbs', line 10753
def on_error_message_change: () -> Listeners
|
#placeholder=(text) ⇒ void
This method returns an undefined value.
Overrides the hint derived from the primary format.
field.placeholder = "when it happened" # a hint of your own
field.placeholder = "" # no hint at all
field.placeholder = nil # back to the derived one
@param text — nil restores the derived hint, "" suppresses it.
311 312 313 314 315 |
# File 'lib/tuile/component/time_field.rb', line 311 def placeholder=(text) # The editor validates the type, so a bad one raises before it is stored. editor.placeholder = text || derived_placeholder @placeholder_override = text end |
#reformat(carried = nil) ⇒ void
This method returns an undefined value.
Re-derives the hint (which was pushed into the editor, so a repaint alone would keep the old one) and rewrites a buffer that still parses — the one path a #step= and a Screen#locale= share, since both mean "the format list changed under a buffer".
@param carried — what the buffer meant under the outgoing formats, where the caller could still read it; only #step= can.
390 391 392 393 394 395 |
# File 'lib/tuile/component/time_field.rb', line 390 def reformat(carried = nil) sync_placeholder time = value || losslessly(carried) self.value = time unless time.nil? fire_if_changed(from_user: false) # for the buffer that just *stopped* parsing: nothing above touched it end |
#seconds_hidden? ⇒ Boolean
425 |
# File 'lib/tuile/component/time_field.rb', line 425 def seconds_hidden? = @step >= SECONDS_VISIBLE_BELOW |
#set_to(hour, minute, second = 0) ⇒ void
This method returns an undefined value.
Sets the value from its parts, so nothing assembles a Time on the
epoch only to hand it straight back.
field.set_to(13, 45) # shows "13:45"
field.set_to(9, 30, 15) # the seconds show only while step < 60
@param hour — 0..23.
@param minute — 0..59.
@param second — 0..59.
215 216 217 |
# File 'lib/tuile/component/time_field.rb', line 215 def set_to(hour, minute, second = 0) self.value = self.class.time_of_day(hour, minute, second) end |
#set_to_now ⇒ void
This method returns an undefined value.
Sets the value to the local wall clock, truncated to this field's precision — the same place Up/Down land an empty field.
field.set_to_now # "13:45" at the default step, "13:45:37" under a minute
225 226 227 |
# File 'lib/tuile/component/time_field.rb', line 225 def set_to_now self.value = now end |
#set_value(new_value, from_user:) ⇒ void
This method returns an undefined value.
Writes new_value into the buffer in the primary format and parks the
caret at its end; fires HasValue#on_value_change only if the value
actually changed.
Lenient about what it takes: the receiver's own hour / min / sec
are read and rebuilt on MIDNIGHT, so a Time, a DateTime and a
Sequel::SQLTime all work and any date, zone or fraction of a second
they carried is dropped.
@param new_value — nil empties the field.
@param from_user — see HasValue#set_value.
196 197 198 199 200 201 202 |
# File 'lib/tuile/component/time_field.rb', line 196 def set_value(new_value, from_user:) editor.set_value(new_value.nil? ? "" : coerce(new_value).strftime(formats.first), from_user:) editor.caret = editor.text.length # The edit above announced nothing ({#notify_on_edit?}); a time written # rather than typed has no prefix to be mistaken for a value. fire_if_changed(from_user:) end |
#settle(flag) ⇒ void
This method returns an undefined value.
@param flag
461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/tuile/component/time_field.rb', line 461 def settle(flag) return if @settled == flag @settled = flag # Nothing else painted: an ENTER on an untouched buffer writes no cells, # and neither does leaving the field with bad input in it. invalidate # The latch moves without the buffer, so this is the one input to the # showable report that {HasBadInput}'s edit funnel cannot see. sync_bad_input end |
#shown_message ⇒ StyledString, ...
The field's own report when it is showable, else whatever HasValidation#shown_message has — bad input outranks a verdict.
10731 |
# File 'sig/tuile.rbs', line 10731
def shown_message: () -> (StyledString | String)?
|
#step_by(delta) ⇒ void
This method returns an undefined value.
Steps #value by delta seconds; an empty or unparseable field steps
to now (as #set_to_now would) instead, and delta is ignored.
@param delta — seconds, either sign.
442 443 444 445 |
# File 'lib/tuile/component/time_field.rb', line 442 def step_by(delta) time = value set_value(time.nil? ? now : advance(time, delta), from_user: true) end |
#sync_bad_input ⇒ void
This method returns an undefined value.
Fires #on_bad_input_change when the showable report has really
changed — the sole writer of @last_bad_input, called from wherever an
input of that expression moves.
10747 |
# File 'sig/tuile.rbs', line 10747
def sync_bad_input: () -> void
|
#sync_placeholder ⇒ void
This method returns an undefined value.
474 475 476 |
# File 'lib/tuile/component/time_field.rb', line 474 def sync_placeholder editor.placeholder = @placeholder_override || derived_placeholder end |
#value ⇒ Time?
@return — the buffer parsed by the first format that matches
it whole, on MIDNIGHT's date; nil when the buffer is empty or no
format parses it.
171 172 173 174 175 176 177 178 179 180 |
# File 'lib/tuile/component/time_field.rb', line 171 def value text = editor.text return nil if text.empty? formats.each do |format| time = Locale::TimeFormats.parse(text, format, MIDNIGHT) return time unless time.nil? end nil end |
#wears_bad_input_ink? ⇒ Boolean
Whether this component paints the well for its own report. true
here; a composite relaying a child's report answers false while that
child is the one holding it, so the fault reddens where it happened
rather than across the whole widget (DateTimeField).
10742 |
# File 'sig/tuile.rbs', line 10742
def wears_bad_input_ink?: () -> bool
|