Class: Tuile::Component::TextField

Inherits:
TextInput
  • Object
show all
Defined in:
lib/tuile/component/text_field.rb,
sig/tuile.rbs

Overview

A single-line text input field with hardware-cursor caret.

The field does not scroll. Any keystroke that would make Tuile::Component::TextInput#text longer than rect.width - 1 (the last column is reserved for the caret past the last char) is rejected.

The caret is a logical index in 0..text.length. The hardware cursor is positioned by Screen after each repaint cycle when this component is focused; see #cursor_position.

Instance Attribute Summary collapse

Attributes inherited from TextInput

#caret, #on_change, #on_escape, #on_key, #text

Instance Method Summary collapse

Methods inherited from TextInput

#background, #default_on_escape, #delete_at_caret, #delete_before_caret, #empty?, #focusable?, #handle_key, #on_caret_mutated, #on_text_mutated, #tab_stop?, #word_left, #word_right

Constructor Details

#initializeTextField

Returns a new instance of TextField.



15
16
17
18
19
20
# File 'lib/tuile/component/text_field.rb', line 15

def initialize
  super
  @on_key_up = nil
  @on_key_down = nil
  @on_enter = nil
end

Instance Attribute Details

#on_enterProc, ...

Optional callback fired when ENTER is pressed. When set, ENTER is consumed by the field; when nil, ENTER falls through to the parent (default behavior).

@return — no-arg callable, or nil.

Returns:

  • (Proc, Method, nil)


40
41
42
# File 'lib/tuile/component/text_field.rb', line 40

def on_enter
  @on_enter
end

#on_key_downProc, ...

Optional callback fired when the DOWN arrow key is pressed. When set, DOWN is consumed by the field; when nil, DOWN falls through to the parent (default behavior). Only triggered by Keys::DOWN_ARROW, not by j, since j is a printable character inserted into Tuile::Component::TextInput#text.

@return — no-arg callable, or nil.

Returns:

  • (Proc, Method, nil)


34
35
36
# File 'lib/tuile/component/text_field.rb', line 34

def on_key_down
  @on_key_down
end

#on_key_upProc, ...

Optional callback fired when the UP arrow key is pressed. When set, UP is consumed by the field; when nil, UP falls through to the parent (default behavior). Only triggered by Keys::UP_ARROW, not by k, since k is a printable character inserted into Tuile::Component::TextInput#text.

@return — no-arg callable, or nil.

Returns:

  • (Proc, Method, nil)


27
28
29
# File 'lib/tuile/component/text_field.rb', line 27

def on_key_up
  @on_key_up
end

Instance Method Details

#cursor_positionPoint?

Returns:



43
44
45
46
47
# File 'lib/tuile/component/text_field.rb', line 43

def cursor_position
  return nil unless rect.width.positive?

  Point.new(rect.left + @caret, rect.top)
end

#handle_mouse(event) ⇒ void

This method returns an undefined value.

@param event

Parameters:



51
52
53
54
55
56
# File 'lib/tuile/component/text_field.rb', line 51

def handle_mouse(event)
  super
  return unless event.button == :left && rect.contains?(event.point)

  self.caret = (event.x - rect.left).clamp(0, @text.length)
end

#handle_text_input_key(key) ⇒ Boolean

@param key

Parameters:

  • key (String)

Returns:

  • (Boolean)


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/tuile/component/text_field.rb', line 79

def handle_text_input_key(key)
  case key
  when *Keys::HOMES then self.caret = 0
  when *Keys::ENDS_ then self.caret = @text.length
  when *Keys::BACKSPACES then delete_before_caret
  when Keys::DELETE then delete_at_caret
  when Keys::UP_ARROW
    return false if @on_key_up.nil?

    @on_key_up.call
  when Keys::DOWN_ARROW
    return false if @on_key_down.nil?

    @on_key_down.call
  when Keys::ENTER
    return false if @on_enter.nil?

    @on_enter.call
  else
    return insert(key) if Keys.printable?(key)

    return super
  end
  true
end

#insert(char) ⇒ Boolean

@param char

Parameters:

  • char (String)

Returns:

  • (Boolean)


123
124
125
126
127
128
129
130
# File 'lib/tuile/component/text_field.rb', line 123

def insert(char)
  return false if @text.length >= max_text_length

  new_text = @text.dup.insert(@caret, char)
  @caret += 1
  self.text = new_text
  true
end

#max_text_lengthInteger

Maximum number of characters Tuile::Component::TextInput#text can hold given current width.

Returns:

  • (Integer)


119
# File 'lib/tuile/component/text_field.rb', line 119

def max_text_length = (rect.width - 1).clamp(0, nil)

#on_width_changedvoid

This method returns an undefined value.



106
107
108
109
110
111
112
113
# File 'lib/tuile/component/text_field.rb', line 106

def on_width_changed
  super
  return if @text.length <= max_text_length

  @text = @text[0, [max_text_length, 0].max]
  @caret = @caret.clamp(0, @text.length)
  @on_change&.call(@text)
end

#preprocess_text(new_text) ⇒ String

Truncate to fit rect.width - 1 — single-line fields can't grow past their width.

@param new_text

Parameters:

  • new_text (String)

Returns:

  • (String)


72
73
74
75
# File 'lib/tuile/component/text_field.rb', line 72

def preprocess_text(new_text)
  new_text = new_text.to_s
  new_text.length > max_text_length ? new_text[0, max_text_length] : new_text
end

#repaintvoid

This method returns an undefined value.



59
60
61
62
63
64
# File 'lib/tuile/component/text_field.rb', line 59

def repaint
  return if rect.empty?

  padded = @text + (" " * (rect.width - @text.length))
  screen.buffer.set_line(rect.left, rect.top, background(padded))
end