Class: Tuile::Component::IntegerField

Inherits:
AbstractWrappingField show all
Includes:
HasBadInput
Defined in:
lib/tuile/component/integer_field.rb,
sig/tuile.rbs

Overview

A single-line field whose #value is an Integer (or nil when empty). The buffer only ever holds 0–9 and a single leading -: a key that would break that is dropped without moving the caret, and so is a paste that would ("12abc34" lands nothing, rather than sieving through as "1234"). Up/Down step the value by one (an empty field counting as 0). An empty or otherwise un-parseable buffer reads back as nil:

field = Component::IntegerField.new
field.on_value_change { |e| puts e.value.inspect }  # Integer or nil, per change
field.value = 42                                   # field shows "42"
field.value                                        # => 42
field.clear                                        # empties it; value => nil

It wraps a TextField rather than subclassing one, so its face carries only the typed HasValue value seam and never the widget's String-typed text; AbstractWrappingField supplies the wrapping. Give it a single-row #rect.

The value is a derived parse of the buffer

#value is Integer(buffer, 10) (or nil), recomputed on read — the buffer is the single source of truth, AbstractWrappingField#value= just writes it. So "-" alone and "" both read as nil, and on_value_change fires eagerly once per real value change: typing 0→7 in "07" shifts the buffer but not the value (7), so it does not fire. No normalization — a typed "007" stays "007" on screen though its value is 7.

min/max, a + sign, and thousands separators are deliberately out of scope (range and formatting are a forms concern).

UI-thread-confined, like every component (see Screen).

Defined Under Namespace

Classes: Field

Constant Summary collapse

BAD_INPUT_MESSAGE =

Returns what #bad_input_message reports for a buffer that is typeable but not an integer.

Returns:

  • (String) —

    what #bad_input_message reports for a buffer that is typeable but not an integer.

"not a whole number"

Instance Attribute Summary

Attributes inherited from AbstractWrappingField

#editor

Instance Method Summary collapse

Methods included from HasBadInput

listener

Methods included from Listeners::Declare

#listener

Methods included from HasValidation

listener

Methods inherited from AbstractWrappingField

#active=, #clear, #commit, #commit_and_notify, #cursor_position, #empty?, #fire_if_changed, #focusable?, #handle_focus, #handle_key?, #notify_on_edit?, #on_enter, #on_value_change, #placeholder, #placeholder=, #relayout, #value=

Methods included from HasPlaceholder

#placeholder, #placeholder=

Methods included from HasValue

#clear, #empty?, #focusable?, listener, #on_value_change, #value=

Constructor Details

#initialize ⇒ IntegerField

Returns a new instance of IntegerField.



67
68
69
70
71
72
# File 'lib/tuile/component/integer_field.rb', line 67

def initialize
  super(Field.new)
  # Not the general on_key interceptor: that slot stays free for the app.
  editor.on_key_up { step(1) }
  editor.on_key_down { step(-1) }
end

Instance Method Details

#bad_input? ⇒ Boolean

@return — true iff the field is holding input its value cannot represent.

Returns:

  • (Boolean)


12040
# File 'sig/tuile.rbs', line 12040

def bad_input?: () -> bool

#bad_input_message ⇒ String?

The lone "-" the filter has to admit is the field's whole bad-input residue; an empty buffer is empty, not bad (HasBadInput).

Returns:

  • (String, nil)


99
# File 'lib/tuile/component/integer_field.rb', line 99

def bad_input_message = value.nil? && !editor.text.empty? ? BAD_INPUT_MESSAGE : nil

#bad_input_settled? ⇒ Boolean

Whether the report may be shown yet. true here, so the well and the notice are as continuous as the report: a FloatField reddens at the half-typed "1.", which is a fair warning while the residue is one or two transient buffers. Override it to latch where the grammar makes every prefix bad input, or the field is red for the whole time the user types a correct value:

def bad_input_settled? = @settled   # set on commit, cleared on an edit

It gates what is shown — the ink and the push — and never #bad_input?, the pull a save gate asks at the click (design/decisions.md D_bad_input). Public because its readers are the app and a composite relaying a child's report, the same reason #bad_input_message is.

Returns:

  • (Boolean)


12055
# File 'sig/tuile.rbs', line 12055

def bad_input_settled?: () -> bool

#empty_value ⇒ void

This method returns an undefined value.

nil, not "": an integer field with no parseable number is empty.



94
# File 'lib/tuile/component/integer_field.rb', line 94

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).

Returns:



12109
# File 'sig/tuile.rbs', line 12109

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.

Returns:

  • (Boolean)


12064
# File 'sig/tuile.rbs', line 12064

def error_ink?: () -> bool

#error_message ⇒ StyledString?

@return — why the field is invalid, or nil when it is not; nil until something sets it.

Returns:



12092
# File 'sig/tuile.rbs', line 12092

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

Parameters:



12102
# File 'sig/tuile.rbs', line 12102

def error_message=: ((String | StyledString)? new_message) -> void

#handle_editor_change ⇒ void

This method returns an undefined value.

Rides AbstractWrappingField's edit funnel, so every includer wrapping an editor announces its report with no wiring of its own.

super is guarded because an includer need not wrap an editor — DateTimeField is a Layout::Horizontal and never calls this.



12077
# File 'sig/tuile.rbs', line 12077

def handle_editor_change: () -> void

#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.

Returns:

  • (::Array[String])


12113
# File 'sig/tuile.rbs', line 12113

def inspect_details: () -> ::Array[String]

#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.

Returns:



12036
# File 'sig/tuile.rbs', line 12036

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.

Returns:



12088
# File 'sig/tuile.rbs', line 12088

def on_error_message_change: () -> Listeners

#set_value(new_value, from_user:) ⇒ void

This method returns an undefined value.

Writes new_value into the buffer and parks the caret at its end; fires AbstractWrappingField#on_value_change only if the value actually changed.

@param new_value — nil empties the field.

@param from_user — see HasValue#set_value.

Parameters:

  • new_value (Integer, nil)
  • from_user: (Boolean)


87
88
89
90
# File 'lib/tuile/component/integer_field.rb', line 87

def set_value(new_value, from_user:)
  editor.set_value(new_value.nil? ? "" : new_value.to_s, from_user:)
  editor.caret = editor.text.length
end

#shown_message ⇒ StyledString, ...

The field's own report when it is showable, else whatever HasValidation#shown_message has — bad input outranks a verdict.

Returns:



12059
# File 'sig/tuile.rbs', line 12059

def shown_message: () -> (StyledString | String)?

#step(delta) ⇒ void

This method returns an undefined value.

Nudges #value by delta, treating an empty/un-parseable field as 0.

@param delta

Parameters:

  • delta (Integer)


106
# File 'lib/tuile/component/integer_field.rb', line 106

def step(delta) = set_value((value || 0) + delta, from_user: true)

#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.



12082
# File 'sig/tuile.rbs', line 12082

def sync_bad_input: () -> void

#value ⇒ Integer?

@return — the parsed buffer; nil when empty or not a valid integer (e.g. a lone "-").

Returns:

  • (Integer, nil)


76
77
78
79
80
# File 'lib/tuile/component/integer_field.rb', line 76

def value
  Integer(editor.text, 10)
rescue ArgumentError
  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).

Returns:

  • (Boolean)


12070
# File 'sig/tuile.rbs', line 12070

def wears_bad_input_ink?: () -> bool