ratatui_ruby 0.4.0 → 0.6.0

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 (441) hide show
  1. checksums.yaml +4 -4
  2. data/.builds/ruby-3.2.yml +1 -1
  3. data/.builds/ruby-3.3.yml +1 -1
  4. data/.builds/ruby-3.4.yml +1 -1
  5. data/.builds/ruby-4.0.0.yml +1 -1
  6. data/AGENTS.md +98 -176
  7. data/CHANGELOG.md +80 -6
  8. data/README.md +19 -7
  9. data/REUSE.toml +15 -0
  10. data/doc/application_architecture.md +179 -45
  11. data/doc/application_testing.md +80 -32
  12. data/doc/contributors/design/ruby_frontend.md +48 -8
  13. data/doc/contributors/design/rust_backend.md +1 -0
  14. data/doc/contributors/developing_examples.md +191 -48
  15. data/doc/contributors/documentation_style.md +7 -0
  16. data/doc/contributors/examples_audit/p1_high.md +21 -0
  17. data/doc/contributors/examples_audit/p2_moderate.md +81 -0
  18. data/doc/contributors/examples_audit.md +41 -0
  19. data/doc/contributors/index.md +2 -0
  20. data/doc/event_handling.md +21 -7
  21. data/doc/images/app_all_events.png +0 -0
  22. data/doc/images/app_color_picker.png +0 -0
  23. data/doc/images/app_login_form.png +0 -0
  24. data/doc/images/app_stateful_interaction.png +0 -0
  25. data/doc/images/verify_quickstart_dsl.png +0 -0
  26. data/doc/images/verify_quickstart_layout.png +0 -0
  27. data/doc/images/verify_quickstart_lifecycle.png +0 -0
  28. data/doc/images/verify_readme_usage.png +0 -0
  29. data/doc/images/widget_barchart_demo.png +0 -0
  30. data/doc/images/widget_block_demo.png +0 -0
  31. data/doc/images/widget_box_demo.png +0 -0
  32. data/doc/images/widget_calendar_demo.png +0 -0
  33. data/doc/images/widget_canvas_demo.png +0 -0
  34. data/doc/images/widget_cell_demo.png +0 -0
  35. data/doc/images/widget_center_demo.png +0 -0
  36. data/doc/images/widget_chart_demo.png +0 -0
  37. data/doc/images/widget_gauge_demo.png +0 -0
  38. data/doc/images/widget_layout_split.png +0 -0
  39. data/doc/images/widget_line_gauge_demo.png +0 -0
  40. data/doc/images/widget_list_demo.png +0 -0
  41. data/doc/images/widget_overlay_demo.png +0 -0
  42. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  43. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  44. data/doc/images/widget_render.png +0 -0
  45. data/doc/images/widget_rich_text.png +0 -0
  46. data/doc/images/widget_scroll_text.png +0 -0
  47. data/doc/images/widget_scrollbar_demo.png +0 -0
  48. data/doc/images/widget_sparkline_demo.png +0 -0
  49. data/doc/images/widget_style_colors.png +0 -0
  50. data/doc/images/widget_table_demo.png +0 -0
  51. data/doc/images/widget_table_flex.png +0 -0
  52. data/doc/images/widget_tabs_demo.png +0 -0
  53. data/doc/images/widget_text_width.png +0 -0
  54. data/doc/interactive_design.md +25 -30
  55. data/doc/quickstart.md +150 -130
  56. data/doc/terminal_limitations.md +92 -0
  57. data/examples/app_all_events/README.md +99 -0
  58. data/examples/app_all_events/app.rb +96 -0
  59. data/examples/app_all_events/model/app_model.rb +157 -0
  60. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  61. data/examples/app_all_events/model/event_entry.rb +92 -0
  62. data/examples/app_all_events/model/msg.rb +37 -0
  63. data/examples/app_all_events/model/timestamp.rb +54 -0
  64. data/examples/app_all_events/update.rb +73 -0
  65. data/examples/app_all_events/view/app_view.rb +78 -0
  66. data/examples/app_all_events/view/controls_view.rb +52 -0
  67. data/examples/app_all_events/view/counts_view.rb +59 -0
  68. data/examples/app_all_events/view/live_view.rb +70 -0
  69. data/examples/app_all_events/view/log_view.rb +55 -0
  70. data/examples/app_all_events/view.rb +7 -0
  71. data/examples/app_color_picker/README.md +134 -0
  72. data/examples/app_color_picker/app.rb +74 -0
  73. data/examples/app_color_picker/clipboard.rb +84 -0
  74. data/examples/app_color_picker/color.rb +191 -0
  75. data/examples/app_color_picker/controls.rb +90 -0
  76. data/examples/app_color_picker/copy_dialog.rb +166 -0
  77. data/examples/app_color_picker/export_pane.rb +126 -0
  78. data/examples/app_color_picker/harmony.rb +56 -0
  79. data/examples/app_color_picker/input.rb +174 -0
  80. data/examples/app_color_picker/main_container.rb +178 -0
  81. data/examples/app_color_picker/palette.rb +109 -0
  82. data/examples/app_login_form/README.md +47 -0
  83. data/examples/{login_form → app_login_form}/app.rb +38 -42
  84. data/examples/app_stateful_interaction/README.md +31 -0
  85. data/examples/app_stateful_interaction/app.rb +272 -0
  86. data/examples/timeout_demo.rb +43 -0
  87. data/examples/verify_quickstart_dsl/README.md +48 -0
  88. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +17 -6
  89. data/examples/verify_quickstart_layout/README.md +71 -0
  90. data/examples/verify_quickstart_layout/app.rb +71 -0
  91. data/examples/verify_quickstart_lifecycle/README.md +56 -0
  92. data/examples/verify_quickstart_lifecycle/app.rb +54 -0
  93. data/examples/verify_readme_usage/README.md +43 -0
  94. data/examples/verify_readme_usage/app.rb +40 -0
  95. data/examples/widget_barchart_demo/README.md +49 -0
  96. data/examples/widget_barchart_demo/app.rb +238 -0
  97. data/examples/widget_block_demo/README.md +34 -0
  98. data/examples/widget_block_demo/app.rb +256 -0
  99. data/examples/widget_box_demo/README.md +45 -0
  100. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  101. data/examples/widget_calendar_demo/README.md +39 -0
  102. data/examples/widget_calendar_demo/app.rb +109 -0
  103. data/examples/widget_canvas_demo/README.md +27 -0
  104. data/examples/widget_canvas_demo/app.rb +123 -0
  105. data/examples/widget_cell_demo/README.md +36 -0
  106. data/examples/widget_cell_demo/app.rb +111 -0
  107. data/examples/widget_center_demo/README.md +29 -0
  108. data/examples/widget_center_demo/app.rb +116 -0
  109. data/examples/widget_chart_demo/README.md +41 -0
  110. data/examples/widget_chart_demo/app.rb +218 -0
  111. data/examples/widget_gauge_demo/README.md +41 -0
  112. data/examples/widget_gauge_demo/app.rb +212 -0
  113. data/examples/widget_layout_split/README.md +44 -0
  114. data/examples/widget_layout_split/app.rb +246 -0
  115. data/examples/widget_line_gauge_demo/README.md +41 -0
  116. data/examples/widget_line_gauge_demo/app.rb +217 -0
  117. data/examples/widget_list_demo/README.md +49 -0
  118. data/examples/widget_list_demo/app.rb +366 -0
  119. data/examples/widget_map_demo/README.md +39 -0
  120. data/examples/{map_demo → widget_map_demo}/app.rb +24 -21
  121. data/examples/widget_overlay_demo/app.rb +248 -0
  122. data/examples/widget_popup_demo/README.md +36 -0
  123. data/examples/widget_popup_demo/app.rb +104 -0
  124. data/examples/widget_ratatui_logo_demo/README.md +34 -0
  125. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  126. data/examples/widget_ratatui_mascot_demo/README.md +34 -0
  127. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  128. data/examples/widget_rect/README.md +38 -0
  129. data/examples/widget_rect/app.rb +205 -0
  130. data/examples/widget_render/README.md +37 -0
  131. data/examples/widget_render/app.rb +184 -0
  132. data/examples/widget_rich_text/README.md +35 -0
  133. data/examples/widget_rich_text/app.rb +166 -0
  134. data/examples/widget_scroll_text/README.md +37 -0
  135. data/examples/widget_scroll_text/app.rb +107 -0
  136. data/examples/widget_scrollbar_demo/README.md +37 -0
  137. data/examples/widget_scrollbar_demo/app.rb +153 -0
  138. data/examples/widget_sparkline_demo/README.md +42 -0
  139. data/examples/widget_sparkline_demo/app.rb +275 -0
  140. data/examples/widget_style_colors/README.md +34 -0
  141. data/examples/widget_style_colors/app.rb +19 -21
  142. data/examples/widget_table_demo/README.md +48 -0
  143. data/examples/widget_table_demo/app.rb +239 -0
  144. data/examples/widget_tabs_demo/README.md +41 -0
  145. data/examples/widget_tabs_demo/app.rb +181 -0
  146. data/examples/widget_text_width/README.md +35 -0
  147. data/examples/widget_text_width/app.rb +106 -0
  148. data/ext/ratatui_ruby/Cargo.lock +11 -4
  149. data/ext/ratatui_ruby/Cargo.toml +2 -1
  150. data/ext/ratatui_ruby/src/events.rs +359 -62
  151. data/ext/ratatui_ruby/src/frame.rs +227 -0
  152. data/ext/ratatui_ruby/src/lib.rs +110 -27
  153. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  154. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  155. data/ext/ratatui_ruby/src/style.rs +138 -57
  156. data/ext/ratatui_ruby/src/terminal.rs +42 -22
  157. data/ext/ratatui_ruby/src/text.rs +14 -7
  158. data/ext/ratatui_ruby/src/widgets/barchart.rs +74 -54
  159. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  160. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  161. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  162. data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
  163. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  164. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
  165. data/ext/ratatui_ruby/src/widgets/list.rs +211 -12
  166. data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
  167. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  168. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  169. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  170. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  171. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  172. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +97 -3
  173. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  174. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  175. data/ext/ratatui_ruby/src/widgets/table.rs +121 -5
  176. data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
  177. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  178. data/lib/ratatui_ruby/cell.rb +7 -7
  179. data/lib/ratatui_ruby/event/key/character.rb +35 -0
  180. data/lib/ratatui_ruby/event/key/media.rb +44 -0
  181. data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
  182. data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
  183. data/lib/ratatui_ruby/event/key/system.rb +45 -0
  184. data/lib/ratatui_ruby/event/key.rb +112 -52
  185. data/lib/ratatui_ruby/event/mouse.rb +3 -3
  186. data/lib/ratatui_ruby/event/none.rb +43 -0
  187. data/lib/ratatui_ruby/event/paste.rb +1 -1
  188. data/lib/ratatui_ruby/event.rb +56 -4
  189. data/lib/ratatui_ruby/frame.rb +183 -0
  190. data/lib/ratatui_ruby/list_state.rb +88 -0
  191. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +13 -13
  192. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  193. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  194. data/lib/ratatui_ruby/schema/block.rb +163 -168
  195. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  196. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  197. data/lib/ratatui_ruby/schema/center.rb +46 -46
  198. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  199. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  200. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  201. data/lib/ratatui_ruby/schema/cursor.rb +30 -25
  202. data/lib/ratatui_ruby/schema/gauge.rb +54 -52
  203. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  204. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  205. data/lib/ratatui_ruby/schema/list.rb +103 -80
  206. data/lib/ratatui_ruby/schema/list_item.rb +41 -0
  207. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  208. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  209. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  210. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  211. data/lib/ratatui_ruby/schema/rect.rb +99 -56
  212. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  213. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  214. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  215. data/lib/ratatui_ruby/schema/style.rb +66 -46
  216. data/lib/ratatui_ruby/schema/table.rb +126 -115
  217. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  218. data/lib/ratatui_ruby/schema/text.rb +69 -1
  219. data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
  220. data/lib/ratatui_ruby/session/autodoc.rb +482 -0
  221. data/lib/ratatui_ruby/session.rb +55 -23
  222. data/lib/ratatui_ruby/table_state.rb +90 -0
  223. data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
  224. data/lib/ratatui_ruby/test_helper/snapshot.rb +390 -0
  225. data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
  226. data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
  227. data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
  228. data/lib/ratatui_ruby/test_helper.rb +66 -193
  229. data/lib/ratatui_ruby/version.rb +1 -1
  230. data/lib/ratatui_ruby.rb +100 -51
  231. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  232. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  233. data/sig/examples/app_all_events/model/events.rbs +15 -0
  234. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  235. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  236. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  237. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  238. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  239. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  240. data/sig/examples/app_all_events/view.rbs +8 -0
  241. data/sig/examples/app_all_events/view_state.rbs +15 -0
  242. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  243. data/sig/examples/app_login_form/app.rbs +11 -0
  244. data/sig/examples/app_stateful_interaction/app.rbs +33 -0
  245. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  246. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  247. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  248. data/sig/examples/widget_block_demo/app.rbs +32 -0
  249. data/sig/examples/widget_box_demo/app.rbs +11 -0
  250. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  251. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  252. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  253. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  254. data/sig/examples/widget_layout_split/app.rbs +10 -0
  255. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  256. data/sig/examples/widget_list_demo/app.rbs +12 -0
  257. data/sig/examples/widget_map_demo/app.rbs +11 -0
  258. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  259. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  260. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  261. data/sig/examples/widget_rect/app.rbs +12 -0
  262. data/sig/examples/widget_render/app.rbs +10 -0
  263. data/sig/examples/widget_rich_text/app.rbs +11 -0
  264. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  265. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  266. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  267. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  268. data/sig/examples/widget_table_demo/app.rbs +11 -0
  269. data/sig/examples/widget_text_width/app.rbs +10 -0
  270. data/sig/ratatui_ruby/event.rbs +11 -1
  271. data/sig/ratatui_ruby/frame.rbs +11 -0
  272. data/sig/ratatui_ruby/list_state.rbs +13 -0
  273. data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -4
  274. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
  275. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  276. data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
  277. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  278. data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
  279. data/sig/ratatui_ruby/schema/list.rbs +4 -2
  280. data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
  281. data/sig/ratatui_ruby/schema/rect.rbs +3 -0
  282. data/sig/ratatui_ruby/schema/style.rbs +3 -3
  283. data/sig/ratatui_ruby/schema/table.rbs +3 -1
  284. data/sig/ratatui_ruby/schema/text.rbs +8 -6
  285. data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
  286. data/sig/ratatui_ruby/session.rbs +107 -0
  287. data/sig/ratatui_ruby/table_state.rbs +15 -0
  288. data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
  289. data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
  290. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
  291. data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
  292. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
  293. data/sig/ratatui_ruby/test_helper.rbs +5 -4
  294. data/tasks/autodoc/examples.rb +79 -0
  295. data/tasks/autodoc/inventory.rb +63 -0
  296. data/tasks/autodoc/member.rb +56 -0
  297. data/tasks/autodoc/name.rb +19 -0
  298. data/tasks/autodoc/notice.rb +26 -0
  299. data/tasks/autodoc/rbs.rb +38 -0
  300. data/tasks/autodoc/rdoc.rb +45 -0
  301. data/tasks/autodoc.rake +53 -0
  302. data/tasks/bump/changelog.rb +3 -3
  303. data/tasks/bump/history.rb +2 -2
  304. data/tasks/bump/links.rb +67 -0
  305. data/tasks/doc.rake +600 -6
  306. data/tasks/example_viewer.html.erb +172 -0
  307. data/tasks/lint.rake +8 -4
  308. data/tasks/resources/index.html.erb +6 -0
  309. data/tasks/sourcehut.rake +70 -30
  310. data/tasks/terminal_preview/app_screenshot.rb +14 -6
  311. data/tasks/terminal_preview/crash_report.rb +7 -9
  312. data/tasks/terminal_preview/launcher_script.rb +4 -6
  313. data/tasks/terminal_preview/preview_collection.rb +4 -6
  314. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  315. data/tasks/terminal_preview/saved_screenshot.rb +10 -11
  316. data/tasks/terminal_preview/terminal_window.rb +7 -9
  317. data/tasks/test.rake +1 -1
  318. data/tasks/website/index_page.rb +3 -3
  319. data/tasks/website/version.rb +10 -10
  320. data/tasks/website/version_menu.rb +10 -12
  321. data/tasks/website/versioned_documentation.rb +49 -17
  322. data/tasks/website/website.rb +6 -8
  323. data/tasks/website.rake +4 -4
  324. metadata +232 -127
  325. data/LICENSES/BSD-2-Clause.txt +0 -9
  326. data/doc/contributors/better_dx.md +0 -543
  327. data/doc/contributors/example_analysis.md +0 -82
  328. data/doc/images/all_events.png +0 -0
  329. data/doc/images/block_padding.png +0 -0
  330. data/doc/images/block_titles.png +0 -0
  331. data/doc/images/box_demo.png +0 -0
  332. data/doc/images/calendar_demo.png +0 -0
  333. data/doc/images/cell_demo.png +0 -0
  334. data/doc/images/chart_demo.png +0 -0
  335. data/doc/images/flex_layout.png +0 -0
  336. data/doc/images/gauge_demo.png +0 -0
  337. data/doc/images/line_gauge_demo.png +0 -0
  338. data/doc/images/list_demo.png +0 -0
  339. data/doc/images/list_styles.png +0 -0
  340. data/doc/images/login_form.png +0 -0
  341. data/doc/images/quickstart_dsl.png +0 -0
  342. data/doc/images/quickstart_lifecycle.png +0 -0
  343. data/doc/images/readme_usage.png +0 -0
  344. data/doc/images/rich_text.png +0 -0
  345. data/doc/images/scroll_text.png +0 -0
  346. data/doc/images/scrollbar_demo.png +0 -0
  347. data/doc/images/sparkline_demo.png +0 -0
  348. data/doc/images/table_flex.png +0 -0
  349. data/doc/images/table_select.png +0 -0
  350. data/examples/all_events/app.rb +0 -169
  351. data/examples/all_events/app.rbs +0 -7
  352. data/examples/all_events/test_app.rb +0 -139
  353. data/examples/analytics/app.rb +0 -258
  354. data/examples/analytics/app.rbs +0 -7
  355. data/examples/analytics/test_app.rb +0 -132
  356. data/examples/block_padding/app.rb +0 -63
  357. data/examples/block_padding/app.rbs +0 -7
  358. data/examples/block_padding/test_app.rb +0 -31
  359. data/examples/block_titles/app.rb +0 -61
  360. data/examples/block_titles/app.rbs +0 -7
  361. data/examples/block_titles/test_app.rb +0 -34
  362. data/examples/box_demo/app.rbs +0 -7
  363. data/examples/box_demo/test_app.rb +0 -88
  364. data/examples/calendar_demo/app.rb +0 -101
  365. data/examples/calendar_demo/app.rbs +0 -7
  366. data/examples/calendar_demo/test_app.rb +0 -108
  367. data/examples/cell_demo/app.rb +0 -108
  368. data/examples/cell_demo/app.rbs +0 -7
  369. data/examples/cell_demo/test_app.rb +0 -36
  370. data/examples/chart_demo/app.rb +0 -203
  371. data/examples/chart_demo/app.rbs +0 -7
  372. data/examples/chart_demo/test_app.rb +0 -102
  373. data/examples/custom_widget/app.rb +0 -51
  374. data/examples/custom_widget/app.rbs +0 -7
  375. data/examples/custom_widget/test_app.rb +0 -30
  376. data/examples/flex_layout/app.rb +0 -156
  377. data/examples/flex_layout/app.rbs +0 -7
  378. data/examples/flex_layout/test_app.rb +0 -65
  379. data/examples/gauge_demo/app.rb +0 -182
  380. data/examples/gauge_demo/test_app.rb +0 -120
  381. data/examples/hit_test/app.rb +0 -175
  382. data/examples/hit_test/app.rbs +0 -7
  383. data/examples/hit_test/test_app.rb +0 -102
  384. data/examples/line_gauge_demo/app.rb +0 -190
  385. data/examples/line_gauge_demo/app.rbs +0 -7
  386. data/examples/line_gauge_demo/test_app.rb +0 -129
  387. data/examples/list_demo/app.rb +0 -253
  388. data/examples/list_demo/test_app.rb +0 -237
  389. data/examples/list_styles/app.rb +0 -140
  390. data/examples/list_styles/app.rbs +0 -7
  391. data/examples/list_styles/test_app.rb +0 -157
  392. data/examples/login_form/app.rbs +0 -7
  393. data/examples/login_form/test_app.rb +0 -51
  394. data/examples/map_demo/app.rbs +0 -7
  395. data/examples/map_demo/test_app.rb +0 -149
  396. data/examples/mouse_events/app.rb +0 -97
  397. data/examples/mouse_events/app.rbs +0 -7
  398. data/examples/mouse_events/test_app.rb +0 -53
  399. data/examples/popup_demo/app.rb +0 -103
  400. data/examples/popup_demo/app.rbs +0 -7
  401. data/examples/popup_demo/test_app.rb +0 -54
  402. data/examples/quickstart_dsl/app.rbs +0 -7
  403. data/examples/quickstart_dsl/test_app.rb +0 -29
  404. data/examples/quickstart_lifecycle/app.rb +0 -39
  405. data/examples/quickstart_lifecycle/app.rbs +0 -7
  406. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  407. data/examples/ratatui_logo_demo/app.rb +0 -79
  408. data/examples/ratatui_logo_demo/app.rbs +0 -7
  409. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  410. data/examples/ratatui_mascot_demo/app.rb +0 -84
  411. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  412. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  413. data/examples/readme_usage/app.rb +0 -29
  414. data/examples/readme_usage/app.rbs +0 -7
  415. data/examples/readme_usage/test_app.rb +0 -29
  416. data/examples/rich_text/app.rb +0 -141
  417. data/examples/rich_text/app.rbs +0 -7
  418. data/examples/rich_text/test_app.rb +0 -166
  419. data/examples/scroll_text/app.rb +0 -103
  420. data/examples/scroll_text/app.rbs +0 -7
  421. data/examples/scroll_text/test_app.rb +0 -110
  422. data/examples/scrollbar_demo/app.rb +0 -143
  423. data/examples/scrollbar_demo/app.rbs +0 -7
  424. data/examples/scrollbar_demo/test_app.rb +0 -77
  425. data/examples/sparkline_demo/app.rb +0 -240
  426. data/examples/sparkline_demo/test_app.rb +0 -107
  427. data/examples/table_flex/app.rb +0 -65
  428. data/examples/table_flex/app.rbs +0 -7
  429. data/examples/table_flex/test_app.rb +0 -36
  430. data/examples/table_select/app.rb +0 -198
  431. data/examples/table_select/app.rbs +0 -7
  432. data/examples/table_select/test_app.rb +0 -180
  433. data/examples/widget_style_colors/test_app.rb +0 -48
  434. data/tasks/bump/comparison_links.rb +0 -41
  435. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  436. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  437. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  438. /data/doc/images/{map_demo.png → widget_map_demo.png} +0 -0
  439. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  440. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  441. /data/{doc/images/ratatui_logo_demo.png → exe/.gitkeep} +0 -0
@@ -1,156 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
- # SPDX-License-Identifier: AGPL-3.0-or-later
5
-
6
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
- require "ratatui_ruby"
8
-
9
- class FlexLayoutApp
10
- def run
11
- RatatuiRuby.run do
12
- loop do
13
- render
14
- break if handle_input == :quit
15
- end
16
- end
17
- end
18
-
19
- private
20
-
21
- def render
22
- view_tree = RatatuiRuby::Layout.new(
23
- direction: :vertical,
24
- constraints: [
25
- RatatuiRuby::Constraint.length(3),
26
- RatatuiRuby::Constraint.fill(1),
27
- RatatuiRuby::Constraint.fill(1),
28
- RatatuiRuby::Constraint.fill(1),
29
- RatatuiRuby::Constraint.fill(1)
30
- ],
31
- children: [
32
- RatatuiRuby::Paragraph.new(
33
- text: "Fill & Flex Layout Demo (press 'q' to quit)",
34
- block: RatatuiRuby::Block.new(title: "Header", borders: [:all])
35
- ),
36
- fill_demo_row,
37
- space_between_demo_row,
38
- space_evenly_demo_row,
39
- ratio_demo_row
40
- ]
41
- )
42
-
43
- RatatuiRuby.draw(view_tree)
44
- end
45
-
46
- def fill_demo_row
47
- RatatuiRuby::Layout.new(
48
- direction: :horizontal,
49
- constraints: [
50
- RatatuiRuby::Constraint.fill(1),
51
- RatatuiRuby::Constraint.fill(3)
52
- ],
53
- children: [
54
- RatatuiRuby::Block.new(
55
- title: "Fill(1)",
56
- borders: [:all],
57
- border_color: "red"
58
- ),
59
- RatatuiRuby::Block.new(
60
- title: "Fill(3)",
61
- borders: [:all],
62
- border_color: "blue"
63
- )
64
- ]
65
- )
66
- end
67
-
68
- def space_between_demo_row
69
- RatatuiRuby::Layout.new(
70
- direction: :horizontal,
71
- flex: :space_between,
72
- constraints: [
73
- RatatuiRuby::Constraint.length(12),
74
- RatatuiRuby::Constraint.length(12),
75
- RatatuiRuby::Constraint.length(12)
76
- ],
77
- children: [
78
- RatatuiRuby::Block.new(
79
- title: "Block A",
80
- borders: [:all],
81
- border_color: "green"
82
- ),
83
- RatatuiRuby::Block.new(
84
- title: "Block B",
85
- borders: [:all],
86
- border_color: "yellow"
87
- ),
88
- RatatuiRuby::Block.new(
89
- title: "Block C",
90
- borders: [:all],
91
- border_color: "magenta"
92
- )
93
- ]
94
- )
95
- end
96
-
97
- def space_evenly_demo_row
98
- RatatuiRuby::Layout.new(
99
- direction: :horizontal,
100
- flex: :space_evenly,
101
- constraints: [
102
- RatatuiRuby::Constraint.length(12),
103
- RatatuiRuby::Constraint.length(12),
104
- RatatuiRuby::Constraint.length(12)
105
- ],
106
- children: [
107
- RatatuiRuby::Block.new(
108
- title: "Even A",
109
- borders: [:all],
110
- border_color: "cyan"
111
- ),
112
- RatatuiRuby::Block.new(
113
- title: "Even B",
114
- borders: [:all],
115
- border_color: "blue"
116
- ),
117
- RatatuiRuby::Block.new(
118
- title: "Even C",
119
- borders: [:all],
120
- border_color: "red"
121
- )
122
- ]
123
- )
124
- end
125
-
126
- def ratio_demo_row
127
- RatatuiRuby::Layout.new(
128
- direction: :horizontal,
129
- constraints: [
130
- RatatuiRuby::Constraint.ratio(1, 4),
131
- RatatuiRuby::Constraint.ratio(3, 4)
132
- ],
133
- children: [
134
- RatatuiRuby::Block.new(
135
- title: "Ratio(1, 4)",
136
- borders: [:all],
137
- border_color: "green"
138
- ),
139
- RatatuiRuby::Block.new(
140
- title: "Ratio(3, 4)",
141
- borders: [:all],
142
- border_color: "magenta"
143
- )
144
- ]
145
- )
146
- end
147
-
148
- def handle_input
149
- event = RatatuiRuby.poll_event
150
- return unless event
151
-
152
- :quit if event == "q" || event == :ctrl_c
153
- end
154
- end
155
-
156
- FlexLayoutApp.new.run if __FILE__ == $0
@@ -1,7 +0,0 @@
1
- class FlexLayoutApp
2
- # @public
3
- def self.new: () -> FlexLayoutApp
4
-
5
- # @public
6
- def run: () -> void
7
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
- # SPDX-License-Identifier: AGPL-3.0-or-later
5
-
6
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "app"
11
-
12
- class TestFlexLayout < Minitest::Test
13
- include RatatuiRuby::TestHelper
14
-
15
- def setup
16
- @app = FlexLayoutApp.new
17
- end
18
-
19
- def test_render_header
20
- with_test_terminal do
21
- # Queue quit
22
- inject_key(:q)
23
-
24
- @app.run
25
-
26
- assert_includes buffer_content[0], "Header"
27
- assert buffer_content.any? { |line| line.include?("Fill & Flex Layout Demo") }
28
- end
29
- end
30
-
31
- def test_fill_constraint_ratio
32
- with_test_terminal do
33
- # Queue quit
34
- inject_key(:q)
35
-
36
- @app.run
37
-
38
- # Fill(1) and Fill(3) should split horizontally in a 1:3 ratio
39
- assert buffer_content.any? { |line| line.include?("Fill(1)") }
40
- assert buffer_content.any? { |line| line.include?("Fill(3)") }
41
- end
42
- end
43
-
44
- def test_space_between_blocks
45
- with_test_terminal do
46
- # Queue quit
47
- inject_key(:q)
48
-
49
- @app.run
50
-
51
- # Three blocks with space_between flex should have equal spacing
52
- assert buffer_content.any? { |line| line.include?("Block A") }
53
- assert buffer_content.any? { |line| line.include?("Block B") }
54
- assert buffer_content.any? { |line| line.include?("Block C") }
55
- end
56
- end
57
-
58
- def test_quit_on_q
59
- with_test_terminal do
60
- inject_key(:q)
61
- @app.run
62
- # Success if it returns
63
- end
64
- end
65
- end
@@ -1,182 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
- # SPDX-License-Identifier: AGPL-3.0-or-later
5
-
6
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
- require "ratatui_ruby"
8
-
9
- # Demonstrates Gauge widget with interactive attribute cycling.
10
- class GaugeDemoApp
11
- def initialize
12
- @ratio = 0.65
13
- @ratios = [0.0, 0.25, 0.5, 0.65, 0.8, 0.95, 1.0]
14
- @ratio_index = 3
15
-
16
- @gauge_colors = [
17
- { name: "Green", color: :green },
18
- { name: "Yellow", color: :yellow },
19
- { name: "Red", color: :red },
20
- { name: "Cyan", color: :cyan },
21
- { name: "Blue", color: :blue }
22
- ]
23
- @gauge_color_index = 0
24
-
25
- @bg_styles = [
26
- { name: "None", style: nil },
27
- { name: "Dark Gray BG", style: RatatuiRuby::Style.new(fg: :dark_gray) },
28
- { name: "White on Black", style: RatatuiRuby::Style.new(fg: :white, bg: :black) },
29
- { name: "Bold White", style: RatatuiRuby::Style.new(fg: :white, modifiers: [:bold]) }
30
- ]
31
- @bg_style_index = 1
32
-
33
- @use_unicode_options = [true, false]
34
- @use_unicode_index = 0
35
-
36
- @label_modes = [
37
- { name: "Percentage", template: ->(ratio) { "#{(ratio * 100).to_i}%" } },
38
- { name: "Ratio (decimal)", template: ->(ratio) { format("%.2f", ratio) } },
39
- { name: "Progress", template: ->(ratio) { "Progress: #{(ratio * 100).to_i}%" } },
40
- { name: "None", template: ->(ratio) { nil } }
41
- ]
42
- @label_mode_index = 0
43
- end
44
-
45
- def run
46
- RatatuiRuby.run do
47
- loop do
48
- render
49
- break if handle_input == :quit
50
- end
51
- end
52
- end
53
-
54
- private
55
-
56
- def render
57
- @ratio = @ratios[@ratio_index]
58
- gauge_color = @gauge_colors[@gauge_color_index][:color]
59
- bg_style = @bg_styles[@bg_style_index][:style]
60
- use_unicode = @use_unicode_options[@use_unicode_index]
61
- label_template = @label_modes[@label_mode_index][:template]
62
-
63
- gauge_style = RatatuiRuby::Style.new(fg: gauge_color)
64
- label = label_template.call(@ratio)
65
-
66
- layout = RatatuiRuby::Layout.new(
67
- direction: :vertical,
68
- constraints: [
69
- RatatuiRuby::Constraint.fill(1),
70
- RatatuiRuby::Constraint.length(6)
71
- ],
72
- children: [
73
- # Main content area with multiple gauge examples
74
- RatatuiRuby::Layout.new(
75
- direction: :vertical,
76
- constraints: [
77
- RatatuiRuby::Constraint.length(1),
78
- RatatuiRuby::Constraint.fill(1),
79
- RatatuiRuby::Constraint.fill(1),
80
- RatatuiRuby::Constraint.fill(1),
81
- RatatuiRuby::Constraint.length(1)
82
- ],
83
- children: [
84
- RatatuiRuby::Paragraph.new(
85
- text: "Gauge Widget Demo",
86
- style: RatatuiRuby::Style.new(modifiers: [:bold])
87
- ),
88
- # Gauge 1: Main interactive gauge
89
- RatatuiRuby::Gauge.new(
90
- ratio: @ratio,
91
- label:,
92
- style: bg_style,
93
- gauge_style:,
94
- use_unicode:,
95
- block: RatatuiRuby::Block.new(title: "Interactive Gauge")
96
- ),
97
- # Gauge 2: Inverse ratio for comparison
98
- RatatuiRuby::Gauge.new(
99
- ratio: 1.0 - @ratio,
100
- label: label_template.call(1.0 - @ratio),
101
- style: bg_style,
102
- gauge_style:,
103
- use_unicode:,
104
- block: RatatuiRuby::Block.new(title: "Inverse (1.0 - ratio)")
105
- ),
106
- # Gauge 3: Fixed at different stages
107
- RatatuiRuby::Gauge.new(
108
- ratio: [@ratio, 0.5].max,
109
- label: "Min 50%",
110
- style: RatatuiRuby::Style.new(fg: :dark_gray),
111
- gauge_style: RatatuiRuby::Style.new(fg: :magenta),
112
- use_unicode:,
113
- block: RatatuiRuby::Block.new(title: "Min Threshold (Magenta)")
114
- ),
115
- RatatuiRuby::Paragraph.new(text: "")
116
- ]
117
- ),
118
- # Bottom controls panel
119
- RatatuiRuby::Block.new(
120
- title: "Controls",
121
- borders: [:all],
122
- children: [
123
- RatatuiRuby::Paragraph.new(
124
- text: [
125
- # Navigation & General
126
- RatatuiRuby::Text::Line.new(spans: [
127
- RatatuiRuby::Text::Span.new(content: "←/→", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
128
- RatatuiRuby::Text::Span.new(content: ": Adjust Ratio (#{format('%.2f', @ratio)}) "),
129
- RatatuiRuby::Text::Span.new(content: "q", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
130
- RatatuiRuby::Text::Span.new(content: ": Quit")
131
- ]),
132
- # Styling
133
- RatatuiRuby::Text::Line.new(spans: [
134
- RatatuiRuby::Text::Span.new(content: "g", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
135
- RatatuiRuby::Text::Span.new(content: ": Color (#{@gauge_colors[@gauge_color_index][:name]}) "),
136
- RatatuiRuby::Text::Span.new(content: "b", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
137
- RatatuiRuby::Text::Span.new(content: ": Background (#{@bg_styles[@bg_style_index][:name]})")
138
- ]),
139
- # Options
140
- RatatuiRuby::Text::Line.new(spans: [
141
- RatatuiRuby::Text::Span.new(content: "u", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
142
- RatatuiRuby::Text::Span.new(content: ": Unicode (#{use_unicode ? 'On' : 'Off'}) "),
143
- RatatuiRuby::Text::Span.new(content: "l", style: RatatuiRuby::Style.new(modifiers: [:bold, :underlined])),
144
- RatatuiRuby::Text::Span.new(content: ": Label (#{@label_modes[@label_mode_index][:name]})")
145
- ])
146
- ]
147
- )
148
- ]
149
- )
150
- ]
151
- )
152
-
153
- RatatuiRuby.draw(layout)
154
- end
155
-
156
- def handle_input
157
- event = RatatuiRuby.poll_event
158
- return unless event
159
-
160
- case event
161
- in {type: :key, code: "q"} | {type: :key, code: "c", modifiers: ["ctrl"]}
162
- :quit
163
- in type: :key, code: "right"
164
- @ratio_index = (@ratio_index + 1) % @ratios.length
165
- in type: :key, code: "left"
166
- @ratio_index = (@ratio_index - 1) % @ratios.length
167
- in type: :key, code: "g"
168
- @gauge_color_index = (@gauge_color_index + 1) % @gauge_colors.length
169
- in type: :key, code: "b"
170
- @bg_style_index = (@bg_style_index + 1) % @bg_styles.length
171
- in type: :key, code: "u"
172
- @use_unicode_index = (@use_unicode_index + 1) % @use_unicode_options.length
173
- in type: :key, code: "l"
174
- @label_mode_index = (@label_mode_index + 1) % @label_modes.length
175
- else
176
- # Ignore other events
177
- nil
178
- end
179
- end
180
- end
181
-
182
- GaugeDemoApp.new.run if __FILE__ == $PROGRAM_NAME
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
- # SPDX-License-Identifier: AGPL-3.0-or-later
5
-
6
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
- require "ratatui_ruby"
8
- require "ratatui_ruby/test_helper"
9
- require "minitest/autorun"
10
- require_relative "app"
11
-
12
- class TestGaugeDemo < Minitest::Test
13
- include RatatuiRuby::TestHelper
14
-
15
- def setup
16
- @app = GaugeDemoApp.new
17
- end
18
-
19
- def test_initial_render
20
- with_test_terminal do
21
- inject_key(:q)
22
- @app.run
23
-
24
- content = buffer_content.join("\n")
25
- assert_includes content, "Interactive Gauge"
26
- assert_includes content, "Inverse"
27
- assert_includes content, "Min Threshold"
28
- end
29
- end
30
-
31
- def test_ratio_increment
32
- with_test_terminal do
33
- inject_key(:right)
34
- inject_key(:q)
35
- @app.run
36
-
37
- content = buffer_content.join("\n")
38
- # After pressing right from index 3 (0.65), should be at index 4 (0.80)
39
- assert_includes content, "0.80"
40
- end
41
- end
42
-
43
- def test_ratio_decrement
44
- with_test_terminal do
45
- inject_key(:left)
46
- inject_key(:q)
47
- @app.run
48
-
49
- content = buffer_content.join("\n")
50
- # After pressing left from index 3 (0.65), should be at index 2 (0.50)
51
- assert_includes content, "0.50"
52
- end
53
- end
54
-
55
- def test_gauge_color_cycling
56
- with_test_terminal do
57
- inject_key(:g)
58
- inject_key(:q)
59
- @app.run
60
-
61
- content = buffer_content.join("\n")
62
- # After pressing g from index 0 (Green), should be at index 1 (Yellow)
63
- assert_includes content, "Yellow"
64
- refute_includes content, "Green"
65
- end
66
- end
67
-
68
- def test_background_style_cycling
69
- with_test_terminal do
70
- inject_key(:b)
71
- inject_key(:q)
72
- @app.run
73
-
74
- content = buffer_content.join("\n")
75
- # After pressing b from index 1 (Dark Gray BG), should be at index 2 (White on Black)
76
- assert_includes content, "White on Black"
77
- refute_includes content, "Dark Gray BG"
78
- end
79
- end
80
-
81
- def test_unicode_toggle
82
- with_test_terminal do
83
- inject_key(:u)
84
- inject_key(:q)
85
- @app.run
86
-
87
- content = buffer_content.join("\n")
88
- # After toggling unicode, should still show gauge controls
89
- assert_includes content, "Off"
90
- end
91
- end
92
-
93
- def test_label_mode_cycling
94
- with_test_terminal do
95
- inject_key(:l)
96
- inject_key(:q)
97
- @app.run
98
-
99
- content = buffer_content.join("\n")
100
- # After pressing l from index 0 (Percentage), should be at index 1 (Ratio decimal)
101
- # The gauge should show decimal format instead of percentage
102
- assert_includes content, "0.65"
103
- end
104
- end
105
-
106
- def test_multiple_interactions
107
- with_test_terminal do
108
- inject_keys(:right, :g, :b, :u, :l, :q)
109
- @app.run
110
-
111
- content = buffer_content.join("\n")
112
- # Verify all controls are visible
113
- assert_includes content, "Adjust Ratio"
114
- assert_includes content, "Color"
115
- assert_includes content, "Background"
116
- assert_includes content, "Unicode"
117
- assert_includes content, "Label"
118
- end
119
- end
120
- end
@@ -1,175 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
4
- # SPDX-License-Identifier: AGPL-3.0-or-later
5
-
6
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
7
- require "ratatui_ruby"
8
-
9
- # Demonstrates hit testing using Layout.split with the Cached Layout Pattern.
10
- #
11
- # This example shows how to calculate layout regions *once per frame* and reuse
12
- # those regions for both rendering and hit testing. This is essential for
13
- # immediate-mode UI development where the same layout is used by multiple
14
- # subsystems (render, event handling, etc.).
15
- #
16
- # Controls:
17
- # - Left/Right arrows: Adjust split ratio
18
- # - Click: Detect which panel was clicked
19
- # - q: Quit
20
- class HitTestApp
21
- def initialize
22
- @left_ratio = 50
23
- @message = "Click a panel or adjust ratio"
24
- @last_click = nil
25
- @hotkey_style = RatatuiRuby::Style.new(modifiers: [:bold, :underlined])
26
- end
27
-
28
- def run
29
- RatatuiRuby.run do
30
- loop do
31
- calculate_layout # Phase 1: Layout calculation (once per frame)
32
- render # Phase 2: Draw to terminal
33
- break if handle_input == :quit # Phase 3: Consume input using cached rects
34
- end
35
- end
36
- end
37
-
38
- private
39
-
40
- def calculate_layout
41
- # Single source of truth for layout geometry.
42
- # Calculated once per frame, then reused by render() and handle_input().
43
- full_area = RatatuiRuby::Rect.new(x: 0, y: 0, width: 80, height: 24)
44
-
45
- # First split: main content vs bottom controls.
46
- @main_area, @control_area = RatatuiRuby::Layout.split(
47
- full_area,
48
- direction: :vertical,
49
- constraints: [
50
- RatatuiRuby::Constraint.fill(1),
51
- RatatuiRuby::Constraint.length(7),
52
- ]
53
- )
54
-
55
- # Second split: within main content, left vs right panels.
56
- @left_rect, @right_rect = RatatuiRuby::Layout.split(
57
- @main_area,
58
- direction: :horizontal,
59
- constraints: [
60
- RatatuiRuby::Constraint.percentage(@left_ratio),
61
- RatatuiRuby::Constraint.percentage(100 - @left_ratio)
62
- ]
63
- )
64
- end
65
-
66
- def render
67
- # Build UI with the pre-calculated regions
68
- left_panel = build_panel("Left Panel", @left_rect, @last_click == :left)
69
- right_panel = build_panel("Right Panel", @right_rect, @last_click == :right)
70
-
71
- layout = RatatuiRuby::Layout.new(
72
- direction: :horizontal,
73
- constraints: [
74
- RatatuiRuby::Constraint.percentage(@left_ratio),
75
- RatatuiRuby::Constraint.percentage(100 - @left_ratio)
76
- ],
77
- children: [left_panel, right_panel]
78
- )
79
-
80
- # Bottom control panel
81
- control_panel = RatatuiRuby::Block.new(
82
- title: "Controls",
83
- borders: [:all],
84
- children: [
85
- RatatuiRuby::Paragraph.new(
86
- text: [
87
- RatatuiRuby::Text::Line.new(spans: [
88
- RatatuiRuby::Text::Span.new(content: "RATIO", style: RatatuiRuby::Style.new(modifiers: [:bold]))
89
- ]),
90
- RatatuiRuby::Text::Line.new(spans: [
91
- RatatuiRuby::Text::Span.new(content: "←", style: @hotkey_style),
92
- RatatuiRuby::Text::Span.new(content: ": Decrease (#{@left_ratio}%) "),
93
- RatatuiRuby::Text::Span.new(content: "→", style: @hotkey_style),
94
- RatatuiRuby::Text::Span.new(content: ": Increase (#{@left_ratio}%) "),
95
- RatatuiRuby::Text::Span.new(content: "q", style: @hotkey_style),
96
- RatatuiRuby::Text::Span.new(content: ": Quit")
97
- ]),
98
- RatatuiRuby::Text::Line.new(spans: [
99
- RatatuiRuby::Text::Span.new(content: "HIT TESTING", style: RatatuiRuby::Style.new(modifiers: [:bold]))
100
- ]),
101
- "Click panels above to detect hits.",
102
- "Last Click: #{@last_click || 'None'} - #{@message}"
103
- ]
104
- )
105
- ]
106
- )
107
-
108
- # Full layout with bottom controls
109
- full_layout = RatatuiRuby::Layout.new(
110
- direction: :vertical,
111
- constraints: [
112
- RatatuiRuby::Constraint.fill(1),
113
- RatatuiRuby::Constraint.length(7),
114
- ],
115
- children: [layout, control_panel]
116
- )
117
-
118
- RatatuiRuby.draw(full_layout)
119
- end
120
-
121
- def build_panel(title, rect, active)
122
- content = "#{title}\n\n" \
123
- "Width: #{rect.width}, Height: #{rect.height}\n" \
124
- "Position: (#{rect.x}, #{rect.y})"
125
-
126
- RatatuiRuby::Paragraph.new(
127
- text: content,
128
- alignment: :center,
129
- block: RatatuiRuby::Block.new(
130
- title: "#{title} (#{active ? 'CLICKED' : 'idle'})",
131
- borders: [:all],
132
- border_color: active ? "green" : "white"
133
- )
134
- )
135
- end
136
-
137
- def handle_input
138
- event = RatatuiRuby.poll_event
139
- return unless event
140
-
141
- case event
142
- in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
143
- return :quit
144
- in type: :key, code: "left"
145
- @left_ratio = [@left_ratio - 10, 10].max
146
- @message = "Ratio: #{@left_ratio}% / #{100 - @left_ratio}%"
147
- @last_click = nil
148
- in type: :key, code: "right"
149
- @left_ratio = [@left_ratio + 10, 90].min
150
- @message = "Ratio: #{@left_ratio}% / #{100 - @left_ratio}%"
151
- @last_click = nil
152
- in type: :mouse, kind: "down", x: click_x, y: click_y
153
- handle_click(click_x, click_y)
154
- else
155
- @message = "Unhandled: #{event.class} #{event.inspect}"
156
- nil
157
- end
158
- nil
159
- end
160
-
161
- def handle_click(x, y)
162
- if @left_rect.contains?(x, y)
163
- @last_click = :left
164
- @message = "Left Panel clicked at (#{x}, #{y})"
165
- elsif @right_rect.contains?(x, y)
166
- @last_click = :right
167
- @message = "Right Panel clicked at (#{x}, #{y})"
168
- else
169
- @last_click = nil
170
- @message = "Clicked outside panels at (#{x}, #{y})"
171
- end
172
- end
173
- end
174
-
175
- HitTestApp.new.run if __FILE__ == $0