Class: Tuile::Component::IntegerField

Inherits:
Component
  • Object
show all
Includes:
HasContent, HasValue
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 user may type only 09 and a single leading -; anything else is silently rejected without moving the caret. 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 = ->(n) { puts n.inspect }  # Integer or nil, per change
field.value = 42                                   # field shows "42"
field.value                                        # => 42
field.clear                                        # empties it; value => nil

Like ComboBox, it composes a TextField (its single HasContent child) rather than subclassing one — its face carries only the typed HasValue value seam, never the widget's String-typed text. It's the same wrapper shape as ComboBox minus the dropdown: a digit-filtered text field re-exposed as a typed input. 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, #value= just writes it. So "-" alone and "" both read as nil, and on_value_change fires eagerly once per real value change: typing 07 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).

Instance Attribute Summary

Attributes included from HasValue

#on_value_change

Attributes included from HasContent

#content

Instance Method Summary collapse

Constructor Details

#initializeIntegerField

Returns a new instance of IntegerField.



39
40
41
42
43
44
45
46
# File 'lib/tuile/component/integer_field.rb', line 39

def initialize
  super()
  @last_value = nil
  field = TextField.new
  field.on_change = ->(_text) { fire_if_changed }
  field.on_key = method(:field_key)
  self.content = field
end

Instance Method Details

#accepts?(char) ⇒ Boolean

A digit anywhere, or a - only as the very first character.

@param char — a single printable character.

Parameters:

  • char (String)

Returns:

  • (Boolean)


116
117
118
119
120
# File 'lib/tuile/component/integer_field.rb', line 116

def accepts?(char)
  return true if char.match?(/\A[0-9]\z/)

  char == "-" && content.caret.zero? && !content.text.start_with?("-")
end

#clearvoid

This method returns an undefined value.

Resets #value to #empty_value.



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

def clear: () -> void

#cursor_positionPoint?

@return — the field's caret (the hardware cursor is delegated to the inner field).

Returns:



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

def cursor_position = content.cursor_position

#empty?Boolean

@return — true iff #value equals #empty_value.

Returns:

  • (Boolean)


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

def empty?: () -> bool

#empty_valuevoid

This method returns an undefined value.

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



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

def empty_value = nil

#field_key(key) ⇒ Boolean

The field's key interceptor, consulted before the field acts on the key: Up/Down step the value; a printable key the field mustn't accept is swallowed (so a rejected key never moves the caret); everything else — digits, the leading sign, and all editing/navigation keys — falls through.

@param key

@return — true to consume the key.

Parameters:

  • key (String)

Returns:

  • (Boolean)


99
100
101
102
103
104
105
106
# File 'lib/tuile/component/integer_field.rb', line 99

def field_key(key)
  case key
  when Keys::UP_ARROW then step(1)
  when Keys::DOWN_ARROW then step(-1)
  else return Keys.printable?(key) && !accepts?(key)
  end
  true
end

#fire_if_changedvoid

This method returns an undefined value.

Re-emits HasValue#on_value_change with the freshly-parsed #value, but only when it differs from the last one fired — so a buffer edit that leaves the value unchanged ("7""07") stays silent.



126
127
128
129
130
131
132
# File 'lib/tuile/component/integer_field.rb', line 126

def fire_if_changed
  v = value
  return if v == @last_value

  @last_value = v
  on_value_change&.call(v)
end

#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 (DECISIONS.md D-integer-field).

Returns:

  • (Boolean)


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

def focusable?: () -> bool

#handle_mousevoid

This method returns an undefined value.

@param event

Parameters:



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

def handle_mouse: (MouseEvent event) -> void

#layout(field) ⇒ void

This method returns an undefined value.

Places the wrapped field across the whole rect (HasContent hook).

@param field

Parameters:



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

def layout(field) = (field.rect = rect)

#on_enterProc, ...

Fired when ENTER is pressed in the field; see TextField#on_enter.

@return — no-arg callable, or nil.

Returns:

  • (Proc, Method, nil)


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

def on_enter = content.on_enter

#on_enter=(callback) ⇒ void

This method returns an undefined value.

@param callback

Parameters:

  • callback (Proc, Method, nil)


79
80
81
# File 'lib/tuile/component/integer_field.rb', line 79

def on_enter=(callback)
  content.on_enter = callback
end

#on_focusvoid

This method returns an undefined value.



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

def on_focus: () -> void

#rect=void

This method returns an undefined value.

@param rect

Parameters:



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

def rect=: (Rect rect) -> void

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


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

def step(delta) = (self.value = (value || 0) + delta)

#valueInteger?

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

Returns:

  • (Integer, nil)


50
51
52
53
54
# File 'lib/tuile/component/integer_field.rb', line 50

def value
  Integer(content.text, 10)
rescue ArgumentError
  nil
end

#value=(new_value) ⇒ void

This method returns an undefined value.

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

@param new_valuenil empties the field.

Parameters:

  • new_value (Integer, nil)


60
61
62
63
# File 'lib/tuile/component/integer_field.rb', line 60

def value=(new_value)
  content.text = new_value.nil? ? "" : new_value.to_s
  content.caret = content.text.length
end