ratatui_ruby 1.4.0-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +15 -0
  3. data/LICENSES/AGPL-3.0-or-later.txt +661 -0
  4. data/LICENSES/CC-BY-SA-4.0.txt +427 -0
  5. data/LICENSES/CC0-1.0.txt +121 -0
  6. data/LICENSES/LGPL-3.0-or-later.txt +304 -0
  7. data/LICENSES/MIT-0.txt +16 -0
  8. data/LICENSES/MIT.txt +21 -0
  9. data/REUSE.toml +42 -0
  10. data/exe/.gitkeep +0 -0
  11. data/ext/ratatui_ruby/.cargo/config.toml +13 -0
  12. data/ext/ratatui_ruby/.gitignore +4 -0
  13. data/ext/ratatui_ruby/Cargo.lock +1737 -0
  14. data/ext/ratatui_ruby/Cargo.toml +24 -0
  15. data/ext/ratatui_ruby/clippy.toml +7 -0
  16. data/ext/ratatui_ruby/extconf.rb +21 -0
  17. data/ext/ratatui_ruby/src/color.rs +82 -0
  18. data/ext/ratatui_ruby/src/errors.rs +28 -0
  19. data/ext/ratatui_ruby/src/events.rs +700 -0
  20. data/ext/ratatui_ruby/src/frame.rs +241 -0
  21. data/ext/ratatui_ruby/src/lib.rs +343 -0
  22. data/ext/ratatui_ruby/src/lib_header.rs +11 -0
  23. data/ext/ratatui_ruby/src/rendering.rs +158 -0
  24. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  25. data/ext/ratatui_ruby/src/style.rs +469 -0
  26. data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
  27. data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
  28. data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
  29. data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
  30. data/ext/ratatui_ruby/src/terminal/queries.rs +231 -0
  31. data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
  32. data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
  33. data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
  34. data/ext/ratatui_ruby/src/text.rs +225 -0
  35. data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
  36. data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
  37. data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
  38. data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
  39. data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
  40. data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
  41. data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
  42. data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
  43. data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
  44. data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
  45. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
  46. data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
  47. data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
  48. data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
  49. data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
  50. data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
  51. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
  52. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
  53. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
  54. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  55. data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
  56. data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
  57. data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
  58. data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
  59. data/lib/ratatui_ruby/backend/window_size.rb +50 -0
  60. data/lib/ratatui_ruby/backend.rb +59 -0
  61. data/lib/ratatui_ruby/buffer/cell.rb +212 -0
  62. data/lib/ratatui_ruby/buffer.rb +149 -0
  63. data/lib/ratatui_ruby/cell.rb +208 -0
  64. data/lib/ratatui_ruby/debug.rb +215 -0
  65. data/lib/ratatui_ruby/draw.rb +63 -0
  66. data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
  67. data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
  68. data/lib/ratatui_ruby/event/key/character.rb +53 -0
  69. data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
  70. data/lib/ratatui_ruby/event/key/media.rb +46 -0
  71. data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
  72. data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
  73. data/lib/ratatui_ruby/event/key/system.rb +47 -0
  74. data/lib/ratatui_ruby/event/key.rb +479 -0
  75. data/lib/ratatui_ruby/event/mouse.rb +291 -0
  76. data/lib/ratatui_ruby/event/none.rb +53 -0
  77. data/lib/ratatui_ruby/event/paste.rb +130 -0
  78. data/lib/ratatui_ruby/event/resize.rb +221 -0
  79. data/lib/ratatui_ruby/event/sync.rb +52 -0
  80. data/lib/ratatui_ruby/event.rb +163 -0
  81. data/lib/ratatui_ruby/frame.rb +257 -0
  82. data/lib/ratatui_ruby/labs/a11y.rb +182 -0
  83. data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
  84. data/lib/ratatui_ruby/labs.rb +47 -0
  85. data/lib/ratatui_ruby/layout/alignment.rb +91 -0
  86. data/lib/ratatui_ruby/layout/constraint.rb +337 -0
  87. data/lib/ratatui_ruby/layout/layout.rb +258 -0
  88. data/lib/ratatui_ruby/layout/position.rb +81 -0
  89. data/lib/ratatui_ruby/layout/rect.rb +733 -0
  90. data/lib/ratatui_ruby/layout/size.rb +62 -0
  91. data/lib/ratatui_ruby/layout.rb +29 -0
  92. data/lib/ratatui_ruby/list_state.rb +201 -0
  93. data/lib/ratatui_ruby/output_guard.rb +171 -0
  94. data/lib/ratatui_ruby/ratatui_ruby.so +0 -0
  95. data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
  96. data/lib/ratatui_ruby/style/color.rb +149 -0
  97. data/lib/ratatui_ruby/style/style.rb +147 -0
  98. data/lib/ratatui_ruby/style.rb +19 -0
  99. data/lib/ratatui_ruby/symbols.rb +435 -0
  100. data/lib/ratatui_ruby/synthetic_events.rb +106 -0
  101. data/lib/ratatui_ruby/table_state.rb +251 -0
  102. data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
  103. data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
  104. data/lib/ratatui_ruby/terminal.rb +66 -0
  105. data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
  106. data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
  107. data/lib/ratatui_ruby/test_helper/event_injection.rb +241 -0
  108. data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
  109. data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
  110. data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
  111. data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
  112. data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
  113. data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
  114. data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
  115. data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
  116. data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
  117. data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
  118. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
  119. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
  120. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
  121. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
  122. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
  123. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
  124. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
  125. data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
  126. data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
  127. data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
  128. data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
  129. data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
  130. data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
  131. data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
  132. data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
  133. data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
  134. data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
  135. data/lib/ratatui_ruby/test_helper.rb +115 -0
  136. data/lib/ratatui_ruby/text/line.rb +245 -0
  137. data/lib/ratatui_ruby/text/span.rb +158 -0
  138. data/lib/ratatui_ruby/text.rb +99 -0
  139. data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
  140. data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
  141. data/lib/ratatui_ruby/tui/core.rb +67 -0
  142. data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
  143. data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
  144. data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
  145. data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
  146. data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
  147. data/lib/ratatui_ruby/tui.rb +106 -0
  148. data/lib/ratatui_ruby/version.rb +12 -0
  149. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
  150. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
  151. data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
  152. data/lib/ratatui_ruby/widgets/block.rb +266 -0
  153. data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
  154. data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
  155. data/lib/ratatui_ruby/widgets/cell.rb +59 -0
  156. data/lib/ratatui_ruby/widgets/center.rb +71 -0
  157. data/lib/ratatui_ruby/widgets/chart.rb +172 -0
  158. data/lib/ratatui_ruby/widgets/clear.rb +66 -0
  159. data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
  160. data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
  161. data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
  162. data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
  163. data/lib/ratatui_ruby/widgets/list.rb +252 -0
  164. data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
  165. data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
  166. data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
  167. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
  168. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
  169. data/lib/ratatui_ruby/widgets/row.rb +123 -0
  170. data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
  171. data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
  172. data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
  173. data/lib/ratatui_ruby/widgets/table.rb +213 -0
  174. data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
  175. data/lib/ratatui_ruby/widgets.rb +43 -0
  176. data/lib/ratatui_ruby.rb +555 -0
  177. data/sig/examples/app_all_events/app.rbs +11 -0
  178. data/sig/examples/app_all_events/model/app_model.rbs +23 -0
  179. data/sig/examples/app_all_events/model/event_entry.rbs +23 -0
  180. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  181. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  182. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  183. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  184. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  185. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  186. data/sig/examples/app_all_events/view.rbs +14 -0
  187. data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
  188. data/sig/examples/app_color_picker/app.rbs +17 -0
  189. data/sig/examples/app_external_editor/app.rbs +12 -0
  190. data/sig/examples/app_login_form/app.rbs +11 -0
  191. data/sig/examples/app_stateful_interaction/app.rbs +39 -0
  192. data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
  193. data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
  194. data/sig/examples/verify_readme_usage/app.rbs +17 -0
  195. data/sig/examples/widget_block_demo/app.rbs +38 -0
  196. data/sig/examples/widget_box_demo/app.rbs +17 -0
  197. data/sig/examples/widget_calendar_demo/app.rbs +17 -0
  198. data/sig/examples/widget_cell_demo/app.rbs +17 -0
  199. data/sig/examples/widget_chart_demo/app.rbs +17 -0
  200. data/sig/examples/widget_gauge_demo/app.rbs +17 -0
  201. data/sig/examples/widget_layout_split/app.rbs +16 -0
  202. data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
  203. data/sig/examples/widget_list_demo/app.rbs +17 -0
  204. data/sig/examples/widget_map_demo/app.rbs +17 -0
  205. data/sig/examples/widget_popup_demo/app.rbs +17 -0
  206. data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
  207. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
  208. data/sig/examples/widget_rect/app.rbs +18 -0
  209. data/sig/examples/widget_render/app.rbs +16 -0
  210. data/sig/examples/widget_rich_text/app.rbs +17 -0
  211. data/sig/examples/widget_scroll_text/app.rbs +17 -0
  212. data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
  213. data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
  214. data/sig/examples/widget_style_colors/app.rbs +20 -0
  215. data/sig/examples/widget_table_demo/app.rbs +17 -0
  216. data/sig/examples/widget_text_width/app.rbs +16 -0
  217. data/sig/generated/event_key_predicates.rbs +1348 -0
  218. data/sig/manifest.yaml +5 -0
  219. data/sig/patches/data.rbs +26 -0
  220. data/sig/patches/debugger__.rbs +8 -0
  221. data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
  222. data/sig/ratatui_ruby/backend.rbs +12 -0
  223. data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
  224. data/sig/ratatui_ruby/buffer.rbs +18 -0
  225. data/sig/ratatui_ruby/cell.rbs +44 -0
  226. data/sig/ratatui_ruby/clear.rbs +18 -0
  227. data/sig/ratatui_ruby/constraint.rbs +26 -0
  228. data/sig/ratatui_ruby/debug.rbs +45 -0
  229. data/sig/ratatui_ruby/draw.rbs +30 -0
  230. data/sig/ratatui_ruby/event.rbs +249 -0
  231. data/sig/ratatui_ruby/frame.rbs +23 -0
  232. data/sig/ratatui_ruby/interfaces.rbs +25 -0
  233. data/sig/ratatui_ruby/labs.rbs +90 -0
  234. data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
  235. data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
  236. data/sig/ratatui_ruby/layout/layout.rbs +45 -0
  237. data/sig/ratatui_ruby/layout/position.rbs +18 -0
  238. data/sig/ratatui_ruby/layout/rect.rbs +64 -0
  239. data/sig/ratatui_ruby/layout/size.rbs +18 -0
  240. data/sig/ratatui_ruby/list_state.rbs +23 -0
  241. data/sig/ratatui_ruby/output_guard.rbs +23 -0
  242. data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
  243. data/sig/ratatui_ruby/rect.rbs +17 -0
  244. data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
  245. data/sig/ratatui_ruby/session.rbs +93 -0
  246. data/sig/ratatui_ruby/style/color.rbs +22 -0
  247. data/sig/ratatui_ruby/style/style.rbs +29 -0
  248. data/sig/ratatui_ruby/symbols.rbs +141 -0
  249. data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
  250. data/sig/ratatui_ruby/table_state.rbs +27 -0
  251. data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
  252. data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
  253. data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
  254. data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
  255. data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
  256. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
  257. data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
  258. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
  259. data/sig/ratatui_ruby/test_helper.rbs +18 -0
  260. data/sig/ratatui_ruby/text/line.rbs +27 -0
  261. data/sig/ratatui_ruby/text/span.rbs +23 -0
  262. data/sig/ratatui_ruby/text.rbs +12 -0
  263. data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
  264. data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
  265. data/sig/ratatui_ruby/tui/core.rbs +23 -0
  266. data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
  267. data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
  268. data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
  269. data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
  270. data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
  271. data/sig/ratatui_ruby/tui.rbs +25 -0
  272. data/sig/ratatui_ruby/version.rbs +12 -0
  273. data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
  274. data/sig/ratatui_ruby/widgets/block.rbs +51 -0
  275. data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
  276. data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
  277. data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
  278. data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
  279. data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
  280. data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
  281. data/sig/ratatui_ruby/widgets/list.rbs +63 -0
  282. data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
  283. data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
  284. data/sig/ratatui_ruby/widgets/row.rbs +43 -0
  285. data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
  286. data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
  287. data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
  288. data/sig/ratatui_ruby/widgets/table.rbs +78 -0
  289. data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
  290. data/sig/ratatui_ruby/widgets.rbs +16 -0
  291. data/vendor/goodcop/base.yml +1047 -0
  292. metadata +729 -0
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Widgets
10
+ # Mixin that provides DWIM hash coercion for widget classes.
11
+ #
12
+ # When users call `tui.table(hash)` instead of `tui.table(**hash)`,
13
+ # Ruby's `...` forwarding passes the Hash as a positional argument,
14
+ # causing cryptic TypeErrors at the Rust FFI boundary.
15
+ #
16
+ # This mixin provides a `coerce_args` class method that detects
17
+ # this pattern and automatically splats the hash into keyword arguments.
18
+ #
19
+ # === Behavior
20
+ #
21
+ # - **Production mode**: Unknown keys are silently ignored
22
+ # - **Debug mode (RR_DEBUG=1)**: Raises ArgumentError to catch typos early
23
+ #
24
+ # === Usage
25
+ #
26
+ #--
27
+ # SPDX-SnippetBegin
28
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
29
+ # SPDX-License-Identifier: MIT-0
30
+ #++
31
+ # class Table < Data.define(:rows, :widths, ...)
32
+ # include CoerceableWidget
33
+ # end
34
+ #
35
+ # # In WidgetFactories:
36
+ # def table(first = nil, **kwargs)
37
+ # Widgets::Table.coerce_args(first, kwargs)
38
+ # end
39
+ #--
40
+ # SPDX-SnippetEnd
41
+ #++
42
+ module CoerceableWidget
43
+ ##
44
+ # Hook called when this module is included in a widget class.
45
+ #
46
+ # Extends the class with ClassMethods and defines KNOWN_KEYS constant
47
+ # from the Data.define members for validation.
48
+ #
49
+ # [base] The class including this module.
50
+ def self.included(base)
51
+ base.extend(ClassMethods)
52
+ base.const_set(:KNOWN_KEYS, base.members.freeze) unless base.const_defined?(:KNOWN_KEYS)
53
+ end
54
+
55
+ # Class methods extended onto widget classes.
56
+ module ClassMethods
57
+ # Coerces a bare Hash argument into keyword arguments.
58
+ #
59
+ # @param first [Hash, nil] First positional argument (bare hash case)
60
+ # @param kwargs [Hash] Keyword arguments (normal splatted case)
61
+ # @return [Object] New instance of the widget class
62
+ # @raise [ArgumentError] In debug mode, if unknown keys are present
63
+ def coerce_args(first, kwargs)
64
+ if first.is_a?(Hash) && kwargs.empty?
65
+ unknown = first.keys - self::KNOWN_KEYS
66
+ if unknown.any? && RatatuiRuby::Debug.enabled?
67
+ raise ArgumentError, "#{name}: unknown keys #{unknown.inspect}"
68
+ end
69
+ new(**first.slice(*self::KNOWN_KEYS))
70
+ else
71
+ new(**kwargs)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Widgets
10
+ # Controls the terminal cursor position.
11
+ #
12
+ # Interfaces are not just output; they require input. Users need a visual cue—a blinking block or line—to know where their keystrokes will appear.
13
+ #
14
+ # This widget renders a ghost. It does not draw a character but instructs the terminal to place the hardware cursor at specific coordinates.
15
+ #
16
+ # Use it for text editors, input fields, or command prompts.
17
+ #
18
+ # === Examples
19
+ #
20
+ #--
21
+ # SPDX-SnippetBegin
22
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
23
+ # SPDX-License-Identifier: MIT-0
24
+ #++
25
+ # Cursor.new(x: 10, y: 5)
26
+ #
27
+ #--
28
+ # SPDX-SnippetEnd
29
+ #++
30
+ # See also:
31
+ # - {Declarative implementation using Tree API}[link:/examples/app_login_form/app_rb.html]
32
+ # - {Component-based implementation using Frame API}[link:/examples/app_color_picker/app_rb.html]
33
+ # - RatatuiRuby::Frame#set_cursor_position (Frame API alternative)
34
+ class Cursor < Data.define(:x, :y)
35
+ include CoerceableWidget
36
+
37
+ ##
38
+ # :attr_reader: x
39
+ # X coordinate (column).
40
+
41
+ ##
42
+ # :attr_reader: y
43
+ # Y coordinate (row).
44
+
45
+ # Creates a new Cursor.
46
+ #
47
+ # [x] Integer.
48
+ # [y] Integer.
49
+ def initialize(x:, y:)
50
+ super(x: Integer(x), y: Integer(y))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Widgets
10
+ # Displays a standard progress bar.
11
+ #
12
+ # Long-running tasks create anxiety. Users need to know that the system is working and how much is left to do.
13
+ #
14
+ # This widget visualizes completion. It fills a bar based on a percentage.
15
+ #
16
+ # Use it for downloads, installations, or processing jobs.
17
+ #
18
+ # {rdoc-image:/doc/images/widget_gauge.png}[link:/examples/widget_gauge/app_rb.html]
19
+ #
20
+ # === Example
21
+ #
22
+ # Run the interactive demo from the terminal:
23
+ #
24
+ # ruby examples/widget_gauge/app.rb
25
+ class Gauge < Data.define(:ratio, :label, :style, :gauge_style, :block, :use_unicode)
26
+ include CoerceableWidget
27
+
28
+ ##
29
+ # :attr_reader: ratio
30
+ # Progress ratio from 0.0 to 1.0.
31
+
32
+ ##
33
+ # :attr_reader: label
34
+ # Text label to display (optional).
35
+ #
36
+ # Accepts String or Text::Span for rich styling.
37
+ #
38
+ # If nil, it often displays the percentage automatically depending on renderer logic,
39
+ # but explicit labels are preferred.
40
+
41
+ ##
42
+ # :attr_reader: style
43
+ # Base style applied to the entire gauge background (optional).
44
+
45
+ ##
46
+ # :attr_reader: gauge_style
47
+ # Style applied specifically to the filled bar portion (optional).
48
+
49
+ ##
50
+ # :attr_reader: block
51
+ # Optional wrapping block.
52
+
53
+ ##
54
+ # :attr_reader: use_unicode
55
+ # Whether to use Unicode block characters (true) or ASCII characters (false).
56
+ # Default is false (ASCII) to be conservative, though Ratatui defaults to true.
57
+
58
+ # Creates a new Gauge.
59
+ #
60
+ # [ratio] Float (0.0 - 1.0).
61
+ # [percent] Integer (0 - 100), alternative to ratio.
62
+ # [label] String or Text::Span (optional).
63
+ # [style] Style object for the background (optional).
64
+ # [gauge_style] Style object for the filled bar (optional).
65
+ # [block] Block widget (optional).
66
+ # [use_unicode] Boolean (default: true).
67
+ #
68
+ # Raises ArgumentError if percent is not 0..100.
69
+ def initialize(ratio: nil, percent: nil, label: nil, style: nil, gauge_style: nil, block: nil, use_unicode: true)
70
+ if percent
71
+ float_percent = Float(percent)
72
+ unless float_percent.between?(0, 100)
73
+ raise ArgumentError, "percent must be between 0 and 100 (got #{percent.inspect})"
74
+ end
75
+ # Float(Numeric) incorrectly returns Float? -- https://github.com/ruby/rbs/issues/2793
76
+ ratio = float_percent / 100.0 #: Float
77
+ end
78
+ ratio = Float(ratio || 0.0)
79
+ super(ratio:, label:, style:, gauge_style:, block:, use_unicode:)
80
+ end
81
+
82
+ # Returns true if the gauge has any fill (ratio > 0).
83
+ #
84
+ # === Example
85
+ #
86
+ #--
87
+ # SPDX-SnippetBegin
88
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
89
+ # SPDX-License-Identifier: MIT-0
90
+ #++
91
+ # Widgets::Gauge.new(ratio: 0.0).filled? # => false
92
+ # Widgets::Gauge.new(ratio: 0.5).filled? # => true
93
+ #--
94
+ # SPDX-SnippetEnd
95
+ #++
96
+ def filled?
97
+ ratio > 0
98
+ end
99
+
100
+ # Returns true if the gauge is at 100% or more (ratio >= 1.0).
101
+ #
102
+ # === Example
103
+ #
104
+ #--
105
+ # SPDX-SnippetBegin
106
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
107
+ # SPDX-License-Identifier: MIT-0
108
+ #++
109
+ # Widgets::Gauge.new(ratio: 0.99).complete? # => false
110
+ # Widgets::Gauge.new(ratio: 1.0).complete? # => true
111
+ #--
112
+ # SPDX-SnippetEnd
113
+ #++
114
+ def complete?
115
+ ratio >= 1.0
116
+ end
117
+
118
+ # Returns the progress as an integer percentage (0-100).
119
+ #
120
+ # Gauge stores progress as a ratio (0.0 to 1.0). User-facing code often
121
+ # displays percentages. Converting manually is tedious.
122
+ #
123
+ # This is the inverse of passing <tt>percent:</tt> to the constructor.
124
+ # Rounds down to the nearest integer.
125
+ #
126
+ # === Example
127
+ #
128
+ #--
129
+ # SPDX-SnippetBegin
130
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
131
+ # SPDX-License-Identifier: MIT-0
132
+ #++
133
+ # gauge = Widgets::Gauge.new(percent: 75)
134
+ # gauge.percent # => 75
135
+ #
136
+ # gauge = Widgets::Gauge.new(ratio: 0.456)
137
+ # gauge.percent # => 45
138
+ #--
139
+ # SPDX-SnippetEnd
140
+ #++
141
+ def percent
142
+ (ratio * 100).to_i
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Widgets
10
+ # Displays a compact, single-line progress bar.
11
+ #
12
+ # Screen space is precious. Standard block gauges are bulky and consume multiple rows.
13
+ #
14
+ # This widget compresses the feedback. It draws a progress bar using line characters, fitting perfectly into tight layouts or lists.
15
+ #
16
+ # Use it when you need to show status without stealing focus or space.
17
+ #
18
+ # {rdoc-image:/doc/images/widget_line_gauge.png}[link:/examples/widget_line_gauge/app_rb.html]
19
+ #
20
+ # === Example
21
+ #
22
+ # Run the interactive demo from the terminal:
23
+ #
24
+ # ruby examples/widget_line_gauge/app.rb
25
+ class LineGauge < Data.define(:ratio, :label, :style, :filled_style, :unfilled_style, :block, :filled_symbol, :unfilled_symbol)
26
+ include CoerceableWidget
27
+
28
+ ##
29
+ # :attr_reader: ratio
30
+ # Progress ratio from 0.0 to 1.0.
31
+
32
+ ##
33
+ # :attr_reader: label
34
+ # Optional label (String or Text::Span for rich styling).
35
+
36
+ ##
37
+ # :attr_reader: style
38
+ # Base style applied to the entire gauge.
39
+
40
+ ##
41
+ # :attr_reader: filled_style
42
+ # Style for the completed portion.
43
+
44
+ ##
45
+ # :attr_reader: unfilled_style
46
+ # Style for the remainder.
47
+
48
+ ##
49
+ # :attr_reader: block
50
+ # Optional wrapping block.
51
+
52
+ ##
53
+ # :attr_reader: filled_symbol
54
+ # Character for filled segments.
55
+
56
+ ##
57
+ # :attr_reader: unfilled_symbol
58
+ # Character for empty segments.
59
+
60
+ # Creates a new LineGauge.
61
+ #
62
+ # [ratio] Float (0.0 - 1.0).
63
+ # [percent] Integer (0 - 100), alternative to ratio.
64
+ # [label] String or Text::Span (optional).
65
+ # [style] Style (optional, base style for the gauge).
66
+ # [filled_style] Style.
67
+ # [unfilled_style] Style.
68
+ # [block] Block.
69
+ # [filled_symbol] String (default: <tt>"█"</tt>).
70
+ # [unfilled_symbol] String (default: <tt>"░"</tt>).
71
+ #
72
+ # Raises ArgumentError if percent is not 0..100.
73
+ def initialize(ratio: nil, percent: nil, label: nil, style: nil, filled_style: nil, unfilled_style: nil, block: nil, filled_symbol: "█", unfilled_symbol: "░")
74
+ if percent
75
+ float_percent = Float(percent)
76
+ unless float_percent.between?(0, 100)
77
+ raise ArgumentError, "percent must be between 0 and 100 (got #{percent.inspect})"
78
+ end
79
+ ratio = float_percent / 100.0
80
+ end
81
+ ratio = Float(ratio || 0.0)
82
+ super(
83
+ ratio:,
84
+ label:,
85
+ style:,
86
+ filled_style:,
87
+ unfilled_style:,
88
+ block:,
89
+ filled_symbol:,
90
+ unfilled_symbol:
91
+ )
92
+ end
93
+
94
+ # Returns true if the gauge has any fill (ratio > 0).
95
+ #
96
+ # === Example
97
+ #
98
+ #--
99
+ # SPDX-SnippetBegin
100
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
101
+ # SPDX-License-Identifier: MIT-0
102
+ #++
103
+ # Widgets::LineGauge.new(ratio: 0.0).filled? # => false
104
+ # Widgets::LineGauge.new(ratio: 0.5).filled? # => true
105
+ #--
106
+ # SPDX-SnippetEnd
107
+ #++
108
+ def filled?
109
+ ratio > 0
110
+ end
111
+
112
+ # Returns true if the gauge is at 100% or more (ratio >= 1.0).
113
+ #
114
+ # === Example
115
+ #
116
+ #--
117
+ # SPDX-SnippetBegin
118
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
119
+ # SPDX-License-Identifier: MIT-0
120
+ #++
121
+ # Widgets::LineGauge.new(ratio: 0.99).complete? # => false
122
+ # Widgets::LineGauge.new(ratio: 1.0).complete? # => true
123
+ #--
124
+ # SPDX-SnippetEnd
125
+ #++
126
+ def complete?
127
+ ratio >= 1.0
128
+ end
129
+
130
+ # Returns the progress as an integer percentage (0-100).
131
+ #
132
+ # LineGauge stores progress as a ratio (0.0 to 1.0). User-facing code often
133
+ # displays percentages. Converting manually is tedious.
134
+ #
135
+ # This is the inverse of passing <tt>percent:</tt> to the constructor.
136
+ # Rounds down to the nearest integer.
137
+ #
138
+ # === Example
139
+ #
140
+ #--
141
+ # SPDX-SnippetBegin
142
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
143
+ # SPDX-License-Identifier: MIT-0
144
+ #++
145
+ # lg = Widgets::LineGauge.new(percent: 75)
146
+ # lg.percent # => 75
147
+ #
148
+ # lg = Widgets::LineGauge.new(ratio: 0.456)
149
+ # lg.percent # => 45
150
+ #--
151
+ # SPDX-SnippetEnd
152
+ #++
153
+ def percent
154
+ (ratio * 100).to_i
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: LGPL-3.0-or-later
6
+ #++
7
+
8
+ module RatatuiRuby
9
+ module Widgets
10
+ # Displays a selectable list of items.
11
+ #
12
+ # Users need to choose from options. Menus, file explorers, and selectors are everywhere.
13
+ # Implementing navigation, highlighting, and scrolling state from scratch is tedious.
14
+ #
15
+ # This widget manages the list. It renders the items. It highlights the selection. It handles the scrolling window.
16
+ #
17
+ # Use it to build main menus, navigation sidebars, or logs.
18
+ #
19
+ # {rdoc-image:/doc/images/widget_list.png}[link:/examples/widget_list/app_rb.html]
20
+ #
21
+ # === Examples
22
+ #
23
+ #--
24
+ # SPDX-SnippetBegin
25
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
26
+ # SPDX-License-Identifier: MIT-0
27
+ #++
28
+ # # Basic List
29
+ # List.new(items: ["Item 1", "Item 2"])
30
+ #
31
+ # # Navigation Menu
32
+ # List.new(
33
+ # items: ["New Game", "Load Game", "Options", "Quit"],
34
+ # selected_index: 0,
35
+ # highlight_style: Style.new(bg: :blue),
36
+ # highlight_symbol: ">> "
37
+ # )
38
+ #--
39
+ # SPDX-SnippetEnd
40
+ #++
41
+ class List < Data.define(:items, :selected_index, :offset, :style, :highlight_style, :highlight_symbol, :repeat_highlight_symbol, :highlight_spacing, :direction, :scroll_padding, :block)
42
+ include CoerceableWidget
43
+
44
+ ##
45
+ # Highlight spacing: always show the spacing column.
46
+ HIGHLIGHT_ALWAYS = :always
47
+ ##
48
+ # Highlight spacing: show spacing only when an item is selected (default).
49
+ HIGHLIGHT_WHEN_SELECTED = :when_selected
50
+ ##
51
+ # Highlight spacing: never show the spacing column.
52
+ HIGHLIGHT_NEVER = :never
53
+
54
+ ##
55
+ # Direction: render items from top to bottom (default).
56
+ DIRECTION_TOP_TO_BOTTOM = :top_to_bottom
57
+ ##
58
+ # Direction: render items from bottom to top.
59
+ DIRECTION_BOTTOM_TO_TOP = :bottom_to_top
60
+
61
+ ##
62
+ # :attr_reader: items
63
+ # The items to display.
64
+ #
65
+ # Accepts Array of Strings, Text::Spans, Text::Lines, or ListItem objects.
66
+ # For styled individual rows, use ListItem with a style.
67
+
68
+ ##
69
+ # :attr_reader: selected_index
70
+ # Index of the active selection (Integer or nil).
71
+
72
+ ##
73
+ # :attr_reader: offset
74
+ # Scroll offset (Integer or nil).
75
+ #
76
+ # Controls the viewport's starting position in the list.
77
+ #
78
+ # When +nil+ (default), Ratatui auto-scrolls to keep the selection visible ("natural scrolling").
79
+ #
80
+ # When set, forces the viewport to start at this item index. Use this for:
81
+ # - **Passive scrolling**: Scroll through a log viewer without selecting items.
82
+ # - **Click-to-select math**: Calculate which item index corresponds to a click coordinate.
83
+ #
84
+ # *Important*: When both +offset+ and +selected_index+ are set, Ratatui may still adjust
85
+ # the viewport during rendering to ensure the selection stays visible. Set +selected_index+
86
+ # to +nil+ for fully manual scroll control.
87
+
88
+ ##
89
+ # :attr_reader: style
90
+ # Base style for unselected items.
91
+
92
+ ##
93
+ # :attr_reader: highlight_style
94
+ # Style for the selected item.
95
+
96
+ ##
97
+ # :attr_reader: highlight_symbol
98
+ # Symbol drawn before the selected item.
99
+
100
+ ##
101
+ # :attr_reader: repeat_highlight_symbol
102
+ # Whether to repeat the highlight symbol for each line of the selected item.
103
+
104
+ ##
105
+ # :attr_reader: highlight_spacing
106
+ # When to show the highlight symbol column.
107
+ #
108
+ # <tt>:always</tt>, <tt>:when_selected</tt>, or <tt>:never</tt>.
109
+
110
+ ##
111
+ # :attr_reader: direction
112
+ # Render direction.
113
+ #
114
+ # <tt>:top_to_bottom</tt> or <tt>:bottom_to_top</tt>.
115
+
116
+ ##
117
+ # :attr_reader: scroll_padding
118
+ # Number of items to keep visible above/below the selected item when scrolling (Integer or nil).
119
+
120
+ ##
121
+ # :attr_reader: block
122
+ # Optional wrapping block.
123
+
124
+ # Creates a new List.
125
+ #
126
+ # Integer parameters accept any object responding to +to_int+ or +to_i+ (duck-typed).
127
+ #
128
+ # [items] Array of Strings, Text::Spans, Text::Lines, or ListItem objects.
129
+ # [selected_index] Numeric (nullable, coerced to Integer).
130
+ # [offset] Numeric (nullable, coerced to Integer). Forces scroll position when set.
131
+ # [style] Style object.
132
+ # [highlight_style] Style object.
133
+ # [highlight_symbol] String (default: <tt>"> "</tt>).
134
+ # [repeat_highlight_symbol] Boolean (default: <tt>false</tt>).
135
+ # [highlight_spacing] Symbol (default: <tt>:when_selected</tt>).
136
+ # [direction] Symbol (default: <tt>:top_to_bottom</tt>).
137
+ # [scroll_padding] Numeric (nullable, coerced to Integer, default: <tt>nil</tt>).
138
+ # [block] Block (optional).
139
+ def initialize(items: [], selected_index: nil, offset: nil, style: nil, highlight_style: nil, highlight_symbol: "> ", repeat_highlight_symbol: false, highlight_spacing: :when_selected, direction: :top_to_bottom, scroll_padding: nil, block: nil)
140
+ super(
141
+ items:,
142
+ selected_index: selected_index.nil? ? nil : Integer(selected_index),
143
+ offset: offset.nil? ? nil : Integer(offset),
144
+ style:,
145
+ highlight_style:,
146
+ highlight_symbol:,
147
+ repeat_highlight_symbol:,
148
+ highlight_spacing:,
149
+ direction:,
150
+ scroll_padding: scroll_padding.nil? ? nil : Integer(scroll_padding),
151
+ block:
152
+ )
153
+ end
154
+
155
+ # Returns true if an item is selected.
156
+ #
157
+ # === Example
158
+ #
159
+ #--
160
+ # SPDX-SnippetBegin
161
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
162
+ # SPDX-License-Identifier: MIT-0
163
+ #++
164
+ # list = Widgets::List.new(items: %w[a b c])
165
+ # list.selected? # => false
166
+ #
167
+ # list = Widgets::List.new(items: %w[a b c], selected_index: 1)
168
+ # list.selected? # => true
169
+ #
170
+ #--
171
+ # SPDX-SnippetEnd
172
+ #++
173
+ # Returns: Boolean.
174
+ def selected?
175
+ !selected_index.nil?
176
+ end
177
+
178
+ # Returns true if the list contains no items.
179
+ #
180
+ # === Example
181
+ #
182
+ #--
183
+ # SPDX-SnippetBegin
184
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
185
+ # SPDX-License-Identifier: MIT-0
186
+ #++
187
+ # list = Widgets::List.new(items: [])
188
+ # list.empty? # => true
189
+ #
190
+ #--
191
+ # SPDX-SnippetEnd
192
+ #++
193
+ # Returns: Boolean.
194
+ def empty?
195
+ items.empty?
196
+ end
197
+
198
+ # Returns the number of items in the list.
199
+ #
200
+ # === Example
201
+ #
202
+ #--
203
+ # SPDX-SnippetBegin
204
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
205
+ # SPDX-License-Identifier: MIT-0
206
+ #++
207
+ # list = Widgets::List.new(items: %w[alpha beta gamma])
208
+ # list.len # => 3
209
+ #
210
+ #--
211
+ # SPDX-SnippetEnd
212
+ #++
213
+ # Returns: Integer.
214
+ def len
215
+ items.length
216
+ end
217
+
218
+ alias length len
219
+ alias size len
220
+
221
+ # NOTE: No 'selection' alias - it's ambiguous whether it returns an item or index.
222
+ # Use selected_index for the index, selected_item for the item.
223
+
224
+ # Returns the currently selected item, or nil if nothing is selected.
225
+ #
226
+ # Accessing the selected item directly requires looking up +items[selected_index]+
227
+ # after checking that +selected_index+ is not nil. This is verbose.
228
+ #
229
+ # This method encapsulates that pattern.
230
+ #
231
+ # === Example
232
+ #
233
+ #--
234
+ # SPDX-SnippetBegin
235
+ # SPDX-FileCopyrightText: 2026 Kerrick Long
236
+ # SPDX-License-Identifier: MIT-0
237
+ #++
238
+ # list = Widgets::List.new(items: %w[alpha beta gamma], selected_index: 1)
239
+ # list.selected_item # => "beta"
240
+ #
241
+ #--
242
+ # SPDX-SnippetEnd
243
+ #++
244
+ # Returns: The item at the selected index, or nil if no selection.
245
+ def selected_item
246
+ return nil if selected_index.nil?
247
+
248
+ items[selected_index]
249
+ end
250
+ end
251
+ end
252
+ end