Class: Tuile::Component::List
- Inherits:
-
Tuile::Component
- Object
- Tuile::Component
- Tuile::Component::List
- Defined in:
- lib/tuile/component/list.rb
Overview
A scrollable list of String items with cursor support.
Items are lines painted directly into the component’s #rect. Lines are automatically clipped horizontally. Vertical scrolling is supported via #top_line; the list can also automatically scroll to the bottom if #auto_scroll is enabled.
Cursor is supported; call #cursor= to change cursor behavior. The cursor responds to arrows, ‘jk`, Home/End, Ctrl+U/D and scrolls the list automatically.
Defined Under Namespace
Classes: Cursor
Instance Attribute Summary collapse
-
#auto_scroll ⇒ Boolean
If true and a line is added or new content is set, auto-scrolls to the bottom.
-
#cursor ⇒ Cursor
The list’s cursor.
-
#on_item_chosen ⇒ Proc?
Callback fired when an item is chosen — by pressing Enter on the cursor’s item, or by left-clicking an item.
-
#scrollbar_visibility ⇒ Symbol
Scrollbar visibility: ‘:gone` or `:visible`.
-
#show_cursor_when_inactive ⇒ Boolean
When true, the cursor highlight is painted even while the list is inactive (e.g. when focus is on a sibling search field).
-
#top_line ⇒ Integer
Top line of the viewport.
Attributes inherited from Tuile::Component
Instance Method Summary collapse
-
#add_line(line) ⇒ void
Adds a line.
-
#add_lines(lines) ⇒ void
Appends given lines.
- #content_size ⇒ Size
- #focusable? ⇒ Boolean
-
#handle_key(key) ⇒ Boolean
True if the key was handled.
- #handle_mouse(event) ⇒ void
-
#initialize ⇒ List
constructor
A new instance of List.
-
#lines {|buffer| ... } ⇒ Array<String>
Without a block, returns the current lines.
-
#lines=(lines) ⇒ void
Sets new lines.
-
#repaint ⇒ void
Paints the list items into #rect.
-
#select_next(query, include_current: false) ⇒ Boolean
Moves the cursor to the next line whose text contains ‘query` (case-insensitive substring match).
-
#select_prev(query, include_current: false) ⇒ Boolean
Mirror of #select_next that walks the list backwards.
Methods inherited from Tuile::Component
#active=, #active?, #attached?, #children, #cursor_position, #depth, #find_shortcut_component, #focus, #keyboard_hint, #on_child_removed, #on_focus, #on_tree, #root, #screen
Constructor Details
#initialize ⇒ List
Returns a new instance of List.
16 17 18 19 20 21 22 23 24 25 |
# File 'lib/tuile/component/list.rb', line 16 def initialize super @lines = [] @auto_scroll = false @top_line = 0 @cursor = Cursor::None.new @scrollbar_visibility = :gone @show_cursor_when_inactive = false @on_item_chosen = nil end |
Instance Attribute Details
#auto_scroll ⇒ Boolean
Returns if true and a line is added or new content is set, auto-scrolls to the bottom.
36 37 38 |
# File 'lib/tuile/component/list.rb', line 36 def auto_scroll @auto_scroll end |
#cursor ⇒ Cursor
Returns the list’s cursor.
42 43 44 |
# File 'lib/tuile/component/list.rb', line 42 def cursor @cursor end |
#on_item_chosen ⇒ Proc?
Returns callback fired when an item is chosen — by pressing Enter on the cursor’s item, or by left-clicking an item. Called as ‘proc.call(index, line)` with the chosen 0-based index and its line. Never fires when the cursor’s position is outside the content (e.g. Tuile::Component::List::Cursor::None, or empty content).
32 33 34 |
# File 'lib/tuile/component/list.rb', line 32 def on_item_chosen @on_item_chosen end |
#scrollbar_visibility ⇒ Symbol
Returns scrollbar visibility: ‘:gone` or `:visible`.
45 46 47 |
# File 'lib/tuile/component/list.rb', line 45 def @scrollbar_visibility end |
#show_cursor_when_inactive ⇒ Boolean
Returns when true, the cursor highlight is painted even while the list is inactive (e.g. when focus is on a sibling search field). Defaults to false.
50 51 52 |
# File 'lib/tuile/component/list.rb', line 50 def show_cursor_when_inactive @show_cursor_when_inactive end |
#top_line ⇒ Integer
Returns top line of the viewport. 0 or positive.
39 40 41 |
# File 'lib/tuile/component/list.rb', line 39 def top_line @top_line end |
Instance Method Details
#add_line(line) ⇒ void
This method returns an undefined value.
Adds a line.
135 136 137 |
# File 'lib/tuile/component/list.rb', line 135 def add_line(line) add_lines [line] end |
#add_lines(lines) ⇒ void
This method returns an undefined value.
Appends given lines. Each entry is coerced via ‘#to_s`, split on `n` into separate lines, and trailing whitespace stripped — symmetric with #lines=.
144 145 146 147 148 149 150 |
# File 'lib/tuile/component/list.rb', line 144 def add_lines(lines) screen.check_locked @lines += lines.flat_map { it.to_s.split("\n") }.map(&:rstrip) @content_size = nil update_top_line_if_auto_scroll invalidate end |
#content_size ⇒ Size
153 154 155 156 157 158 159 |
# File 'lib/tuile/component/list.rb', line 153 def content_size @content_size ||= begin content_width = @lines.map { |line| Unicode::DisplayWidth.of(Rainbow.uncolor(line)) }.max || 0 width = @lines.empty? ? 0 : content_width + 2 Size.new(width, @lines.size) end end |
#focusable? ⇒ Boolean
161 |
# File 'lib/tuile/component/list.rb', line 161 def focusable? = true |
#handle_key(key) ⇒ Boolean
Returns true if the key was handled.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/tuile/component/list.rb', line 165 def handle_key(key) if !active? false elsif super true elsif key == Keys::PAGE_UP move_top_line_by(-) true elsif key == Keys::PAGE_DOWN move_top_line_by() true elsif key == Keys::ENTER && cursor_on_item? fire_item_chosen true elsif @cursor.handle_key(key, @lines.size, ) invalidate true else false end end |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/tuile/component/list.rb', line 213 def handle_mouse(event) super if event. == :scroll_down move_top_line_by(4) elsif event. == :scroll_up move_top_line_by(-4) else return unless rect.contains?(event.point) line = event.y - rect.top + top_line if @cursor.handle_mouse(line, event, @lines.size) invalidate end fire_item_chosen if event. == :left && line >= 0 && line < @lines.size && cursor_on_item? end end |
#lines {|buffer| ... } ⇒ Array<String>
Without a block, returns the current lines. With a block, fully re-populates the list: “‘ruby list.lines do |buffer|
buffer << "Hello!"
end “‘
124 125 126 127 128 129 130 |
# File 'lib/tuile/component/list.rb', line 124 def lines return @lines unless block_given? buffer = [] yield buffer self.lines = buffer end |
#lines=(lines) ⇒ void
This method returns an undefined value.
Sets new lines. Each entry is coerced via ‘#to_s`, split on `n` into separate lines, and trailing whitespace stripped — symmetric with #add_lines, so the stored `@lines` is always `Array<String>`.
104 105 106 107 108 109 110 111 |
# File 'lib/tuile/component/list.rb', line 104 def lines=(lines) raise TypeError, "expected Array, got #{lines.inspect}" unless lines.is_a? Array @lines = lines.flat_map { it.to_s.split("\n") }.map(&:rstrip) @content_size = nil update_top_line_if_auto_scroll invalidate end |
#repaint ⇒ void
This method returns an undefined value.
Paints the list items into Tuile::Component#rect.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/tuile/component/list.rb', line 233 def repaint super return if rect.empty? width = rect.width = if VerticalScrollBar.new(rect.height, line_count: @lines.size, top_line: @top_line) end (0..(rect.height - 1)).each do |line_no| line_index = line_no + @top_line line = paintable_line(line_index, line_no, width, ) screen.print TTY::Cursor.move_to(rect.left, line_no + rect.top), line end end |
#select_next(query, include_current: false) ⇒ Boolean
Moves the cursor to the next line whose text contains ‘query` (case-insensitive substring match). Search wraps around the end of the list. Only lines reachable by the current #cursor are considered.
199 200 201 |
# File 'lib/tuile/component/list.rb', line 199 def select_next(query, include_current: false) search_and_go(query, include_current: include_current, reverse: false) end |
#select_prev(query, include_current: false) ⇒ Boolean
Mirror of #select_next that walks the list backwards.
207 208 209 |
# File 'lib/tuile/component/list.rb', line 207 def select_prev(query, include_current: false) search_and_go(query, include_current: include_current, reverse: true) end |