ratatui_ruby 0.4.0 → 0.5.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 (351) 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 +87 -171
  7. data/CHANGELOG.md +38 -1
  8. data/README.md +8 -3
  9. data/REUSE.toml +20 -0
  10. data/doc/application_architecture.md +105 -45
  11. data/doc/application_testing.md +5 -3
  12. data/doc/contributors/design/ruby_frontend.md +9 -5
  13. data/doc/contributors/developing_examples.md +76 -18
  14. data/doc/contributors/documentation_style.md +7 -0
  15. data/doc/contributors/index.md +2 -0
  16. data/doc/event_handling.md +10 -4
  17. data/doc/images/app_all_events.png +0 -0
  18. data/doc/images/app_color_picker.png +0 -0
  19. data/doc/images/verify_readme_usage.png +0 -0
  20. data/doc/images/widget_barchart_demo.png +0 -0
  21. data/doc/images/widget_block_padding.png +0 -0
  22. data/doc/images/widget_block_titles.png +0 -0
  23. data/doc/images/widget_box_demo.png +0 -0
  24. data/doc/images/widget_calendar_demo.png +0 -0
  25. data/doc/images/widget_cell_demo.png +0 -0
  26. data/doc/images/widget_chart_demo.png +0 -0
  27. data/doc/images/widget_gauge_demo.png +0 -0
  28. data/doc/images/widget_layout_split.png +0 -0
  29. data/doc/images/widget_line_gauge_demo.png +0 -0
  30. data/doc/images/widget_list_demo.png +0 -0
  31. data/doc/images/widget_ratatui_logo_demo.png +0 -0
  32. data/doc/images/widget_ratatui_mascot_demo.png +0 -0
  33. data/doc/images/widget_render.png +0 -0
  34. data/doc/images/widget_scrollbar_demo.png +0 -0
  35. data/doc/images/widget_sparkline_demo.png +0 -0
  36. data/doc/images/widget_style_colors.png +0 -0
  37. data/doc/images/widget_table_flex.png +0 -0
  38. data/doc/images/widget_tabs_demo.png +0 -0
  39. data/doc/interactive_design.md +25 -30
  40. data/doc/quickstart.md +147 -120
  41. data/examples/app_all_events/README.md +81 -0
  42. data/examples/app_all_events/app.rb +93 -0
  43. data/examples/app_all_events/model/event_color_cycle.rb +41 -0
  44. data/examples/app_all_events/model/event_entry.rb +75 -0
  45. data/examples/app_all_events/model/events.rb +180 -0
  46. data/examples/app_all_events/model/highlight.rb +57 -0
  47. data/examples/app_all_events/model/timestamp.rb +54 -0
  48. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
  49. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
  50. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
  51. data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
  52. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
  53. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
  54. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
  55. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
  56. data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
  57. data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
  58. data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
  59. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
  60. data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
  61. data/examples/app_all_events/view/app_view.rb +78 -0
  62. data/examples/app_all_events/view/controls_view.rb +50 -0
  63. data/examples/app_all_events/view/counts_view.rb +55 -0
  64. data/examples/app_all_events/view/live_view.rb +69 -0
  65. data/examples/app_all_events/view/log_view.rb +60 -0
  66. data/examples/app_all_events/view.rb +7 -0
  67. data/examples/app_all_events/view_state.rb +42 -0
  68. data/examples/app_color_picker/README.md +94 -0
  69. data/examples/app_color_picker/app.rb +112 -0
  70. data/examples/app_color_picker/clipboard.rb +84 -0
  71. data/examples/app_color_picker/color.rb +191 -0
  72. data/examples/app_color_picker/copy_dialog.rb +170 -0
  73. data/examples/app_color_picker/harmony.rb +56 -0
  74. data/examples/app_color_picker/input.rb +142 -0
  75. data/examples/app_color_picker/palette.rb +80 -0
  76. data/examples/app_color_picker/scene.rb +201 -0
  77. data/examples/{login_form → app_login_form}/app.rb +39 -42
  78. data/examples/{map_demo → app_map_demo}/app.rb +24 -21
  79. data/examples/{table_select → app_table_select}/app.rb +68 -65
  80. data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
  81. data/examples/verify_quickstart_layout/app.rb +69 -0
  82. data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
  83. data/examples/verify_readme_usage/app.rb +34 -0
  84. data/examples/widget_barchart_demo/app.rb +238 -0
  85. data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
  86. data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
  87. data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
  88. data/examples/widget_calendar_demo/app.rb +109 -0
  89. data/examples/widget_cell_demo/app.rb +104 -0
  90. data/examples/widget_chart_demo/app.rb +213 -0
  91. data/examples/widget_gauge_demo/app.rb +212 -0
  92. data/examples/widget_layout_split/app.rb +246 -0
  93. data/examples/widget_line_gauge_demo/app.rb +217 -0
  94. data/examples/widget_list_demo/app.rb +382 -0
  95. data/examples/widget_list_styles/app.rb +141 -0
  96. data/examples/widget_popup_demo/app.rb +104 -0
  97. data/examples/widget_ratatui_logo_demo/app.rb +103 -0
  98. data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
  99. data/examples/widget_rect/app.rb +205 -0
  100. data/examples/widget_render/app.rb +184 -0
  101. data/examples/widget_rich_text/app.rb +137 -0
  102. data/examples/widget_scroll_text/app.rb +108 -0
  103. data/examples/widget_scrollbar_demo/app.rb +153 -0
  104. data/examples/widget_sparkline_demo/app.rb +274 -0
  105. data/examples/widget_style_colors/app.rb +19 -21
  106. data/examples/widget_table_flex/app.rb +95 -0
  107. data/examples/widget_tabs_demo/app.rb +167 -0
  108. data/ext/ratatui_ruby/Cargo.lock +1 -1
  109. data/ext/ratatui_ruby/Cargo.toml +1 -1
  110. data/ext/ratatui_ruby/src/events.rs +121 -36
  111. data/ext/ratatui_ruby/src/frame.rs +115 -0
  112. data/ext/ratatui_ruby/src/lib.rs +79 -26
  113. data/ext/ratatui_ruby/src/rendering.rs +8 -4
  114. data/ext/ratatui_ruby/src/style.rs +138 -57
  115. data/ext/ratatui_ruby/src/terminal.rs +5 -9
  116. data/ext/ratatui_ruby/src/text.rs +13 -6
  117. data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -54
  118. data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
  119. data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
  120. data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
  121. data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
  122. data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
  123. data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
  124. data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
  125. data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
  126. data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
  127. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +4 -2
  128. data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
  129. data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
  130. data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
  131. data/lib/ratatui_ruby/cell.rb +3 -3
  132. data/lib/ratatui_ruby/event/key.rb +1 -1
  133. data/lib/ratatui_ruby/event/none.rb +43 -0
  134. data/lib/ratatui_ruby/event.rb +56 -4
  135. data/lib/ratatui_ruby/frame.rb +87 -0
  136. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
  137. data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
  138. data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
  139. data/lib/ratatui_ruby/schema/block.rb +163 -168
  140. data/lib/ratatui_ruby/schema/calendar.rb +66 -67
  141. data/lib/ratatui_ruby/schema/canvas.rb +63 -63
  142. data/lib/ratatui_ruby/schema/center.rb +46 -46
  143. data/lib/ratatui_ruby/schema/chart.rb +135 -143
  144. data/lib/ratatui_ruby/schema/clear.rb +42 -42
  145. data/lib/ratatui_ruby/schema/constraint.rb +76 -76
  146. data/lib/ratatui_ruby/schema/cursor.rb +25 -25
  147. data/lib/ratatui_ruby/schema/gauge.rb +53 -53
  148. data/lib/ratatui_ruby/schema/layout.rb +87 -87
  149. data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
  150. data/lib/ratatui_ruby/schema/list.rb +86 -84
  151. data/lib/ratatui_ruby/schema/overlay.rb +31 -31
  152. data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
  153. data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
  154. data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
  155. data/lib/ratatui_ruby/schema/rect.rb +60 -60
  156. data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
  157. data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
  158. data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
  159. data/lib/ratatui_ruby/schema/style.rb +46 -46
  160. data/lib/ratatui_ruby/schema/table.rb +112 -119
  161. data/lib/ratatui_ruby/schema/tabs.rb +66 -67
  162. data/lib/ratatui_ruby/session/autodoc.rb +417 -0
  163. data/lib/ratatui_ruby/session.rb +40 -23
  164. data/lib/ratatui_ruby/test_helper.rb +185 -19
  165. data/lib/ratatui_ruby/version.rb +1 -1
  166. data/lib/ratatui_ruby.rb +65 -39
  167. data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
  168. data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
  169. data/sig/examples/app_all_events/model/events.rbs +15 -0
  170. data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
  171. data/sig/examples/app_all_events/view/app_view.rbs +8 -0
  172. data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
  173. data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
  174. data/sig/examples/app_all_events/view/live_view.rbs +6 -0
  175. data/sig/examples/app_all_events/view/log_view.rbs +6 -0
  176. data/sig/examples/app_all_events/view.rbs +8 -0
  177. data/sig/examples/app_all_events/view_state.rbs +15 -0
  178. data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
  179. data/sig/examples/app_login_form/app.rbs +11 -0
  180. data/sig/examples/app_map_demo/app.rbs +11 -0
  181. data/sig/examples/app_table_select/app.rbs +11 -0
  182. data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
  183. data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
  184. data/sig/examples/verify_readme_usage/app.rbs +11 -0
  185. data/sig/examples/widget_block_padding/app.rbs +11 -0
  186. data/sig/examples/widget_block_titles/app.rbs +11 -0
  187. data/sig/examples/widget_box_demo/app.rbs +11 -0
  188. data/sig/examples/widget_calendar_demo/app.rbs +11 -0
  189. data/sig/examples/widget_cell_demo/app.rbs +11 -0
  190. data/sig/examples/widget_chart_demo/app.rbs +11 -0
  191. data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
  192. data/sig/examples/widget_layout_split/app.rbs +10 -0
  193. data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
  194. data/sig/examples/widget_list_demo/app.rbs +12 -0
  195. data/sig/examples/widget_list_styles/app.rbs +11 -0
  196. data/sig/examples/widget_popup_demo/app.rbs +11 -0
  197. data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
  198. data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
  199. data/sig/examples/widget_rect/app.rbs +12 -0
  200. data/sig/examples/widget_render/app.rbs +10 -0
  201. data/sig/examples/widget_rich_text/app.rbs +11 -0
  202. data/sig/examples/widget_scroll_text/app.rbs +11 -0
  203. data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
  204. data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
  205. data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
  206. data/sig/examples/widget_table_flex/app.rbs +11 -0
  207. data/sig/ratatui_ruby/frame.rbs +9 -0
  208. data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
  209. data/sig/ratatui_ruby/schema/draw.rbs +4 -0
  210. data/sig/ratatui_ruby/schema/layout.rbs +1 -1
  211. data/sig/ratatui_ruby/session.rbs +94 -0
  212. data/tasks/autodoc/inventory.rb +61 -0
  213. data/tasks/autodoc/member.rb +56 -0
  214. data/tasks/autodoc/name.rb +19 -0
  215. data/tasks/autodoc/notice.rb +26 -0
  216. data/tasks/autodoc/rbs.rb +38 -0
  217. data/tasks/autodoc/rdoc.rb +45 -0
  218. data/tasks/autodoc.rake +47 -0
  219. data/tasks/bump/history.rb +2 -2
  220. data/tasks/doc.rake +600 -6
  221. data/tasks/example_viewer.html.erb +172 -0
  222. data/tasks/lint.rake +8 -4
  223. data/tasks/resources/index.html.erb +6 -0
  224. data/tasks/sourcehut.rake +4 -4
  225. data/tasks/terminal_preview/app_screenshot.rb +1 -3
  226. data/tasks/terminal_preview/crash_report.rb +7 -9
  227. data/tasks/terminal_preview/launcher_script.rb +4 -6
  228. data/tasks/terminal_preview/preview_collection.rb +4 -6
  229. data/tasks/terminal_preview/safety_confirmation.rb +3 -5
  230. data/tasks/terminal_preview/saved_screenshot.rb +7 -9
  231. data/tasks/terminal_preview/terminal_window.rb +7 -9
  232. data/tasks/test.rake +1 -1
  233. data/tasks/website/index_page.rb +3 -3
  234. data/tasks/website/version.rb +10 -10
  235. data/tasks/website/version_menu.rb +10 -12
  236. data/tasks/website/versioned_documentation.rb +49 -17
  237. data/tasks/website/website.rb +6 -8
  238. data/tasks/website.rake +4 -4
  239. metadata +156 -125
  240. data/LICENSES/BSD-2-Clause.txt +0 -9
  241. data/doc/contributors/better_dx.md +0 -543
  242. data/doc/contributors/example_analysis.md +0 -82
  243. data/doc/images/all_events.png +0 -0
  244. data/doc/images/block_padding.png +0 -0
  245. data/doc/images/block_titles.png +0 -0
  246. data/doc/images/box_demo.png +0 -0
  247. data/doc/images/calendar_demo.png +0 -0
  248. data/doc/images/cell_demo.png +0 -0
  249. data/doc/images/chart_demo.png +0 -0
  250. data/doc/images/flex_layout.png +0 -0
  251. data/doc/images/gauge_demo.png +0 -0
  252. data/doc/images/line_gauge_demo.png +0 -0
  253. data/doc/images/list_demo.png +0 -0
  254. data/doc/images/readme_usage.png +0 -0
  255. data/doc/images/scrollbar_demo.png +0 -0
  256. data/doc/images/sparkline_demo.png +0 -0
  257. data/doc/images/table_flex.png +0 -0
  258. data/examples/all_events/app.rb +0 -169
  259. data/examples/all_events/app.rbs +0 -7
  260. data/examples/all_events/test_app.rb +0 -139
  261. data/examples/analytics/app.rb +0 -258
  262. data/examples/analytics/app.rbs +0 -7
  263. data/examples/analytics/test_app.rb +0 -132
  264. data/examples/block_padding/app.rbs +0 -7
  265. data/examples/block_padding/test_app.rb +0 -31
  266. data/examples/block_titles/app.rbs +0 -7
  267. data/examples/block_titles/test_app.rb +0 -34
  268. data/examples/box_demo/app.rbs +0 -7
  269. data/examples/box_demo/test_app.rb +0 -88
  270. data/examples/calendar_demo/app.rb +0 -101
  271. data/examples/calendar_demo/app.rbs +0 -7
  272. data/examples/calendar_demo/test_app.rb +0 -108
  273. data/examples/cell_demo/app.rb +0 -108
  274. data/examples/cell_demo/app.rbs +0 -7
  275. data/examples/cell_demo/test_app.rb +0 -36
  276. data/examples/chart_demo/app.rb +0 -203
  277. data/examples/chart_demo/app.rbs +0 -7
  278. data/examples/chart_demo/test_app.rb +0 -102
  279. data/examples/custom_widget/app.rb +0 -51
  280. data/examples/custom_widget/app.rbs +0 -7
  281. data/examples/custom_widget/test_app.rb +0 -30
  282. data/examples/flex_layout/app.rb +0 -156
  283. data/examples/flex_layout/app.rbs +0 -7
  284. data/examples/flex_layout/test_app.rb +0 -65
  285. data/examples/gauge_demo/app.rb +0 -182
  286. data/examples/gauge_demo/test_app.rb +0 -120
  287. data/examples/hit_test/app.rb +0 -175
  288. data/examples/hit_test/app.rbs +0 -7
  289. data/examples/hit_test/test_app.rb +0 -102
  290. data/examples/line_gauge_demo/app.rb +0 -190
  291. data/examples/line_gauge_demo/app.rbs +0 -7
  292. data/examples/line_gauge_demo/test_app.rb +0 -129
  293. data/examples/list_demo/app.rb +0 -253
  294. data/examples/list_demo/test_app.rb +0 -237
  295. data/examples/list_styles/app.rb +0 -140
  296. data/examples/list_styles/app.rbs +0 -7
  297. data/examples/list_styles/test_app.rb +0 -157
  298. data/examples/login_form/app.rbs +0 -7
  299. data/examples/login_form/test_app.rb +0 -51
  300. data/examples/map_demo/app.rbs +0 -7
  301. data/examples/map_demo/test_app.rb +0 -149
  302. data/examples/mouse_events/app.rb +0 -97
  303. data/examples/mouse_events/app.rbs +0 -7
  304. data/examples/mouse_events/test_app.rb +0 -53
  305. data/examples/popup_demo/app.rb +0 -103
  306. data/examples/popup_demo/app.rbs +0 -7
  307. data/examples/popup_demo/test_app.rb +0 -54
  308. data/examples/quickstart_dsl/app.rbs +0 -7
  309. data/examples/quickstart_dsl/test_app.rb +0 -29
  310. data/examples/quickstart_lifecycle/app.rbs +0 -7
  311. data/examples/quickstart_lifecycle/test_app.rb +0 -29
  312. data/examples/ratatui_logo_demo/app.rb +0 -79
  313. data/examples/ratatui_logo_demo/app.rbs +0 -7
  314. data/examples/ratatui_logo_demo/test_app.rb +0 -51
  315. data/examples/ratatui_mascot_demo/app.rb +0 -84
  316. data/examples/ratatui_mascot_demo/app.rbs +0 -7
  317. data/examples/ratatui_mascot_demo/test_app.rb +0 -47
  318. data/examples/readme_usage/app.rb +0 -29
  319. data/examples/readme_usage/app.rbs +0 -7
  320. data/examples/readme_usage/test_app.rb +0 -29
  321. data/examples/rich_text/app.rb +0 -141
  322. data/examples/rich_text/app.rbs +0 -7
  323. data/examples/rich_text/test_app.rb +0 -166
  324. data/examples/scroll_text/app.rb +0 -103
  325. data/examples/scroll_text/app.rbs +0 -7
  326. data/examples/scroll_text/test_app.rb +0 -110
  327. data/examples/scrollbar_demo/app.rb +0 -143
  328. data/examples/scrollbar_demo/app.rbs +0 -7
  329. data/examples/scrollbar_demo/test_app.rb +0 -77
  330. data/examples/sparkline_demo/app.rb +0 -240
  331. data/examples/sparkline_demo/test_app.rb +0 -107
  332. data/examples/table_flex/app.rb +0 -65
  333. data/examples/table_flex/app.rbs +0 -7
  334. data/examples/table_flex/test_app.rb +0 -36
  335. data/examples/table_select/app.rbs +0 -7
  336. data/examples/table_select/test_app.rb +0 -180
  337. data/examples/widget_style_colors/test_app.rb +0 -48
  338. /data/doc/images/{analytics.png → app_analytics.png} +0 -0
  339. /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
  340. /data/doc/images/{login_form.png → app_login_form.png} +0 -0
  341. /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
  342. /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
  343. /data/doc/images/{table_select.png → app_table_select.png} +0 -0
  344. /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
  345. /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
  346. /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
  347. /data/doc/images/{list_styles.png → widget_list_styles.png} +0 -0
  348. /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
  349. /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
  350. /data/doc/images/{rich_text.png → widget_rich_text.png} +0 -0
  351. /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
@@ -4,90 +4,90 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Defines the sizing rule for a layout section.
7
+ # Defines the sizing rule for a layout section.
8
+ #
9
+ # Flexible layouts need rules. You can't just place widgets at absolute coordinates; they must adapt to changing terminal sizes.
10
+ #
11
+ # This class defines the rules of engagement. It tells the layout engine exactly how much space a section requires relative to others.
12
+ #
13
+ # Mix and match fixed lengths, percentages, ratios, and minimums. Build layouts that breathe.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Constraint.length(5) # Exactly 5 cells
18
+ # Constraint.percentage(50) # Half the available space
19
+ # Constraint.min(10) # At least 10 cells, maybe more
20
+ # Constraint.fill(1) # Fill remaining space (weight 1)
21
+ class Constraint < Data.define(:type, :value)
22
+ ##
23
+ # :attr_reader: type
24
+ # The type of constraint.
8
25
  #
9
- # Flexible layouts need rules. You can't just place widgets at absolute coordinates; they must adapt to changing terminal sizes.
26
+ # <tt>:length</tt>, <tt>:percentage</tt>, <tt>:min</tt>, <tt>:max</tt>, <tt>:fill</tt>, or <tt>:ratio</tt>.
27
+
28
+ ##
29
+ # :attr_reader: value
30
+ # The numeric value (or array for ratio) associated with the rule.
31
+
32
+ # Requests a fixed size.
10
33
  #
11
- # This class defines the rules of engagement. It tells the layout engine exactly how much space a section requires relative to others.
34
+ # Constraint.length(10) # 10 characters wide/high
12
35
  #
13
- # Mix and match fixed lengths, percentages, ratios, and minimums. Build layouts that breathe.
36
+ # [v] Number of cells (Integer).
37
+ def self.length(v)
38
+ new(type: :length, value: Integer(v))
39
+ end
40
+
41
+ # Requests a percentage of available space.
14
42
  #
15
- # === Examples
43
+ # Constraint.percentage(25) # 25% of the area
16
44
  #
17
- # Constraint.length(5) # Exactly 5 cells
18
- # Constraint.percentage(50) # Half the available space
19
- # Constraint.min(10) # At least 10 cells, maybe more
20
- # Constraint.fill(1) # Fill remaining space (weight 1)
21
- class Constraint < Data.define(:type, :value)
22
- ##
23
- # :attr_reader: type
24
- # The type of constraint.
25
- #
26
- # <tt>:length</tt>, <tt>:percentage</tt>, <tt>:min</tt>, <tt>:max</tt>, <tt>:fill</tt>, or <tt>:ratio</tt>.
27
-
28
- ##
29
- # :attr_reader: value
30
- # The numeric value (or array for ratio) associated with the rule.
31
-
32
- # Requests a fixed size.
33
- #
34
- # Constraint.length(10) # 10 characters wide/high
35
- #
36
- # [v] Number of cells (Integer).
37
- def self.length(v)
38
- new(type: :length, value: Integer(v))
39
- end
40
-
41
- # Requests a percentage of available space.
42
- #
43
- # Constraint.percentage(25) # 25% of the area
44
- #
45
- # [v] Percentage 0-100 (Integer).
46
- def self.percentage(v)
47
- new(type: :percentage, value: Integer(v))
48
- end
45
+ # [v] Percentage 0-100 (Integer).
46
+ def self.percentage(v)
47
+ new(type: :percentage, value: Integer(v))
48
+ end
49
49
 
50
- # Enforces a minimum size.
51
- #
52
- # Constraint.min(5) # At least 5 cells
53
- #
54
- # This section will grow if space permits, but never shrink below +v+.
55
- #
56
- # [v] Minimum cells (Integer).
57
- def self.min(v)
58
- new(type: :min, value: Integer(v))
59
- end
50
+ # Enforces a minimum size.
51
+ #
52
+ # Constraint.min(5) # At least 5 cells
53
+ #
54
+ # This section will grow if space permits, but never shrink below +v+.
55
+ #
56
+ # [v] Minimum cells (Integer).
57
+ def self.min(v)
58
+ new(type: :min, value: Integer(v))
59
+ end
60
60
 
61
- # Enforces a maximum size.
62
- #
63
- # Constraint.max(10) # At most 10 cells
64
- #
65
- # [v] Maximum cells (Integer).
66
- def self.max(v)
67
- new(type: :max, value: Integer(v))
68
- end
61
+ # Enforces a maximum size.
62
+ #
63
+ # Constraint.max(10) # At most 10 cells
64
+ #
65
+ # [v] Maximum cells (Integer).
66
+ def self.max(v)
67
+ new(type: :max, value: Integer(v))
68
+ end
69
69
 
70
- # Fills remaining space proportionally.
71
- #
72
- # Constraint.fill(1) # Equal share
73
- # Constraint.fill(2) # Double share
74
- #
75
- # Fill constraints distribute any space left after satisfying strict rules.
76
- # They behave like flex-grow. A fill(2) takes twice as much space as a fill(1).
77
- #
78
- # [v] Proportional weight (Integer, default: 1).
79
- def self.fill(v = 1)
80
- new(type: :fill, value: Integer(v))
81
- end
70
+ # Fills remaining space proportionally.
71
+ #
72
+ # Constraint.fill(1) # Equal share
73
+ # Constraint.fill(2) # Double share
74
+ #
75
+ # Fill constraints distribute any space left after satisfying strict rules.
76
+ # They behave like flex-grow. A fill(2) takes twice as much space as a fill(1).
77
+ #
78
+ # [v] Proportional weight (Integer, default: 1).
79
+ def self.fill(v = 1)
80
+ new(type: :fill, value: Integer(v))
81
+ end
82
82
 
83
- # Requests a specific ratio of the total space.
84
- #
85
- # Constraint.ratio(1, 3) # 1/3rd of the area
86
- #
87
- # [numerator] Top part of fraction (Integer).
88
- # [denominator] Bottom part of fraction (Integer).
89
- def self.ratio(numerator, denominator)
90
- new(type: :ratio, value: [Integer(numerator), Integer(denominator)])
91
- end
83
+ # Requests a specific ratio of the total space.
84
+ #
85
+ # Constraint.ratio(1, 3) # 1/3rd of the area
86
+ #
87
+ # [numerator] Top part of fraction (Integer).
88
+ # [denominator] Bottom part of fraction (Integer).
89
+ def self.ratio(numerator, denominator)
90
+ new(type: :ratio, value: [Integer(numerator), Integer(denominator)])
92
91
  end
92
+ end
93
93
  end
@@ -4,32 +4,32 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Controls the terminal cursor position.
8
- #
9
- # 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.
10
- #
11
- # This widget renders a ghost. It does not draw a character but instructs the terminal to place the hardware cursor at specific coordinates.
12
- #
13
- # Use it for text editors, input fields, or command prompts.
14
- #
15
- # === Examples
16
- #
17
- # Cursor.new(x: 10, y: 5)
18
- class Cursor < Data.define(:x, :y)
19
- ##
20
- # :attr_reader: x
21
- # X coordinate (column).
7
+ # Controls the terminal cursor position.
8
+ #
9
+ # 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.
10
+ #
11
+ # This widget renders a ghost. It does not draw a character but instructs the terminal to place the hardware cursor at specific coordinates.
12
+ #
13
+ # Use it for text editors, input fields, or command prompts.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Cursor.new(x: 10, y: 5)
18
+ class Cursor < Data.define(:x, :y)
19
+ ##
20
+ # :attr_reader: x
21
+ # X coordinate (column).
22
22
 
23
- ##
24
- # :attr_reader: y
25
- # Y coordinate (row).
23
+ ##
24
+ # :attr_reader: y
25
+ # Y coordinate (row).
26
26
 
27
- # Creates a new Cursor.
28
- #
29
- # [x] Integer.
30
- # [y] Integer.
31
- def initialize(x:, y:)
32
- super(x: Integer(x), y: Integer(y))
33
- end
27
+ # Creates a new Cursor.
28
+ #
29
+ # [x] Integer.
30
+ # [y] Integer.
31
+ def initialize(x:, y:)
32
+ super(x: Integer(x), y: Integer(y))
34
33
  end
34
+ end
35
35
  end
@@ -4,65 +4,65 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Displays a standard progress bar.
8
- #
9
- # Long-running tasks create anxiety. Users need to know that the system is working and how much is left to do.
10
- #
11
- # This widget visualizes completion. It fills a bar based on a percentage.
12
- #
13
- # Use it for downloads, installations, or processing jobs.
14
- #
15
- # === Examples
16
- #
17
- # Gauge.new(
18
- # ratio: 0.75,
19
- # label: "75%",
20
- # gauge_style: Style.new(fg: :green)
21
- # )
22
- class Gauge < Data.define(:ratio, :label, :style, :gauge_style, :block, :use_unicode)
23
- ##
24
- # :attr_reader: ratio
25
- # Progress ratio from 0.0 to 1.0.
7
+ # Displays a standard progress bar.
8
+ #
9
+ # Long-running tasks create anxiety. Users need to know that the system is working and how much is left to do.
10
+ #
11
+ # This widget visualizes completion. It fills a bar based on a percentage.
12
+ #
13
+ # Use it for downloads, installations, or processing jobs.
14
+ #
15
+ # {rdoc-image:/doc/images/widget_gauge_demo.png}[link:/examples/widget_gauge_demo/app_rb.html]
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the interactive demo from the terminal:
20
+ #
21
+ # ruby examples/widget_gauge_demo/app.rb
22
+ class Gauge < Data.define(:ratio, :label, :style, :gauge_style, :block, :use_unicode)
23
+ ##
24
+ # :attr_reader: ratio
25
+ # Progress ratio from 0.0 to 1.0.
26
26
 
27
- ##
28
- # :attr_reader: label
29
- # Text label to display (optional).
30
- #
31
- # If nil, it often displays the percentage automatically depending on renderer logic,
32
- # but explicit labels are preferred.
27
+ ##
28
+ # :attr_reader: label
29
+ # Text label to display (optional).
30
+ #
31
+ # If nil, it often displays the percentage automatically depending on renderer logic,
32
+ # but explicit labels are preferred.
33
33
 
34
- ##
35
- # :attr_reader: style
36
- # Base style applied to the entire gauge background (optional).
34
+ ##
35
+ # :attr_reader: style
36
+ # Base style applied to the entire gauge background (optional).
37
37
 
38
- ##
39
- # :attr_reader: gauge_style
40
- # Style applied specifically to the filled bar portion (optional).
38
+ ##
39
+ # :attr_reader: gauge_style
40
+ # Style applied specifically to the filled bar portion (optional).
41
41
 
42
- ##
43
- # :attr_reader: block
44
- # Optional wrapping block.
42
+ ##
43
+ # :attr_reader: block
44
+ # Optional wrapping block.
45
45
 
46
- ##
47
- # :attr_reader: use_unicode
48
- # Whether to use Unicode block characters (true) or ASCII characters (false).
49
- # Default is false (ASCII) to be conservative, though Ratatui defaults to true.
46
+ ##
47
+ # :attr_reader: use_unicode
48
+ # Whether to use Unicode block characters (true) or ASCII characters (false).
49
+ # Default is false (ASCII) to be conservative, though Ratatui defaults to true.
50
50
 
51
- # Creates a new Gauge.
52
- #
53
- # [ratio] Float (0.0 - 1.0).
54
- # [percent] Integer (0 - 100), alternative to ratio.
55
- # [label] String (optional).
56
- # [style] Style object for the background (optional).
57
- # [gauge_style] Style object for the filled bar (optional).
58
- # [block] Block widget (optional).
59
- # [use_unicode] Boolean (default: true).
60
- def initialize(ratio: nil, percent: nil, label: nil, style: nil, gauge_style: nil, block: nil, use_unicode: true)
61
- if percent
62
- ratio = Float(percent) / 100.0
63
- end
64
- ratio = Float(ratio || 0.0)
65
- super(ratio:, label:, style:, gauge_style:, block:, use_unicode:)
51
+ # Creates a new Gauge.
52
+ #
53
+ # [ratio] Float (0.0 - 1.0).
54
+ # [percent] Integer (0 - 100), alternative to ratio.
55
+ # [label] String (optional).
56
+ # [style] Style object for the background (optional).
57
+ # [gauge_style] Style object for the filled bar (optional).
58
+ # [block] Block widget (optional).
59
+ # [use_unicode] Boolean (default: true).
60
+ def initialize(ratio: nil, percent: nil, label: nil, style: nil, gauge_style: nil, block: nil, use_unicode: true)
61
+ if percent
62
+ ratio = Float(percent) / 100.0
66
63
  end
64
+ ratio = Float(ratio || 0.0)
65
+ super(ratio:, label:, style:, gauge_style:, block:, use_unicode:)
67
66
  end
67
+ end
68
68
  end
@@ -4,101 +4,101 @@
4
4
  # SPDX-License-Identifier: AGPL-3.0-or-later
5
5
 
6
6
  module RatatuiRuby
7
- # Divides an area into smaller chunks.
7
+ # Divides an area into smaller chunks.
8
+ #
9
+ # Terminal screens vary in size. Hardcoded positions break when the window resizes. You need a way to organize space dynamically.
10
+ #
11
+ # This class manages geometry. It splits a given area into multiple sections based on a list of constraints.
12
+ #
13
+ # Use layouts to build responsive grids. Stack sections vertically for a sidebar-main structure. Partition them horizontally for headers and footers. Let the layout engine do the math.
14
+ #
15
+ # {rdoc-image:/doc/images/widget_layout_split.png}[link:/examples/widget_layout_split/app_rb.html]
16
+ #
17
+ # === Example
18
+ #
19
+ # Run the interactive demo from the terminal:
20
+ #
21
+ # ruby examples/widget_layout_split/app.rb
22
+ class Layout < Data.define(:direction, :constraints, :children, :flex)
23
+ ##
24
+ # :attr_reader: direction
25
+ # Direction of the split.
8
26
  #
9
- # Terminal screens vary in size. Hardcoded positions break when the window resizes. You need a way to organize space dynamically.
27
+ # Either <tt>:vertical</tt> (top to bottom) or <tt>:horizontal</tt> (left to right).
10
28
  #
11
- # This class manages geometry. It splits a given area into multiple sections based on a list of constraints.
12
- #
13
- # Use layouts to build responsive grids. Stack sections vertically for a sidebar-main structure. Partition them horizontally for headers and footers. Let the layout engine do the math.
14
- #
15
- # === Examples
16
- #
17
- # # A simple vertical split (Sidebar / Main)
18
- # Layout.new(
19
- # direction: :horizontal,
20
- # constraints: [
21
- # Constraint.length(20), # Sidebar
22
- # Constraint.min(0) # Main content
23
- # ]
24
- # )
25
- #
26
- # # Flex layout (Centering a block)
27
- # Layout.new(
28
- # direction: :vertical,
29
- # flex: :center,
30
- # constraints: [Constraint.length(10)], # A 10-row block centered vertically
31
- # children: [modal_block]
32
- # )
33
- class Layout < Data.define(:direction, :constraints, :children, :flex)
34
- ##
35
- # :attr_reader: direction
36
- # Direction of the split.
37
- #
38
- # Either <tt>:vertical</tt> (top to bottom) or <tt>:horizontal</tt> (left to right).
39
- #
40
- # layout.direction # => :vertical
29
+ # layout.direction # => :vertical
41
30
 
42
- ##
43
- # :attr_reader: constraints
44
- # Array of rules defining section sizes.
45
- #
46
- # See RatatuiRuby::Constraint.
31
+ ##
32
+ # :attr_reader: constraints
33
+ # Array of rules defining section sizes.
34
+ #
35
+ # See RatatuiRuby::Constraint.
47
36
 
48
- ##
49
- # :attr_reader: children
50
- # Widgets to render in each section (optional).
51
- #
52
- # If provided, `children[i]` is rendered into the area defined by `constraints[i]`.
37
+ ##
38
+ # :attr_reader: children
39
+ # Widgets to render in each section (optional).
40
+ #
41
+ # If provided, `children[i]` is rendered into the area defined by `constraints[i]`.
53
42
 
54
- ##
55
- # :attr_reader: flex
56
- # Strategy for distributing extra space.
57
- #
58
- # One of <tt>:legacy</tt>, <tt>:start</tt>, <tt>:center</tt>, <tt>:end</tt>, <tt>:space_between</tt>, <tt>:space_around</tt>.
43
+ ##
44
+ # :attr_reader: flex
45
+ # Strategy for distributing extra space.
46
+ #
47
+ # One of <tt>:legacy</tt>, <tt>:start</tt>, <tt>:center</tt>, <tt>:end</tt>, <tt>:space_between</tt>, <tt>:space_around</tt>.
59
48
 
60
- # :nodoc:
61
- FLEX_MODES = %i[legacy start center end space_between space_around space_evenly].freeze
49
+ # :nodoc:
50
+ FLEX_MODES = %i[legacy start center end space_between space_around space_evenly].freeze
62
51
 
63
- # Creates a new Layout.
64
- #
65
- # [direction]
66
- # <tt>:vertical</tt> or <tt>:horizontal</tt> (default: <tt>:vertical</tt>).
67
- # [constraints]
68
- # list of Constraint objects.
69
- # [children]
70
- # List of widgets to render (optional).
71
- # [flex]
72
- # Flex mode for spacing (default: <tt>:legacy</tt>).
73
- def initialize(direction: :vertical, constraints: [], children: [], flex: :legacy)
74
- super
75
- end
52
+ # Creates a new Layout.
53
+ #
54
+ # [direction]
55
+ # <tt>:vertical</tt> or <tt>:horizontal</tt> (default: <tt>:vertical</tt>).
56
+ # [constraints]
57
+ # list of Constraint objects.
58
+ # [children]
59
+ # List of widgets to render (optional).
60
+ # [flex]
61
+ # Flex mode for spacing (default: <tt>:legacy</tt>).
62
+ def initialize(direction: :vertical, constraints: [], children: [], flex: :legacy)
63
+ super
64
+ end
76
65
 
77
- # Splits an area into multiple rectangles.
78
- #
79
- # This is a pure calculation helper for hit testing. It computes where
80
- # widgets *would* be placed without actually rendering them.
81
- #
82
- # rects = Layout.split(
83
- # area,
84
- # direction: :horizontal,
85
- # constraints: [Constraint.percentage(50), Constraint.percentage(50)]
86
- # )
87
- # left, right = rects
88
- #
89
- # [area]
90
- # The area to split (a Rect or any object responding to x, y, width, height).
91
- # [direction]
92
- # <tt>:vertical</tt> or <tt>:horizontal</tt> (default: <tt>:vertical</tt>).
93
- # [constraints]
94
- # Array of Constraint objects defining section sizes.
95
- # [flex]
96
- # Flex mode for spacing (default: <tt>:legacy</tt>).
97
- #
98
- # Returns an Array of Rect objects.
99
- def self.split(area, direction: :vertical, constraints:, flex: :legacy)
100
- raw_rects = _split(area, direction, constraints, flex)
101
- raw_rects.map { |r| Rect.new(x: r[:x], y: r[:y], width: r[:width], height: r[:height]) }
66
+ # Splits an area into multiple rectangles.
67
+ #
68
+ # This is a pure calculation helper for hit testing. It computes where
69
+ # widgets *would* be placed without actually rendering them.
70
+ #
71
+ # rects = Layout.split(
72
+ # area,
73
+ # direction: :horizontal,
74
+ # constraints: [Constraint.percentage(50), Constraint.percentage(50)]
75
+ # )
76
+ # left, right = rects
77
+ #
78
+ # [area]
79
+ # The area to split. Can be a <tt>Rect</tt> or a <tt>Hash</tt> containing <tt>:x</tt>, <tt>:y</tt>, <tt>:width</tt>, and <tt>:height</tt>.
80
+ # [direction]
81
+ # <tt>:vertical</tt> or <tt>:horizontal</tt> (default: <tt>:vertical</tt>).
82
+ # [constraints]
83
+ # Array of <tt>Constraint</tt> objects defining section sizes.
84
+ # [flex]
85
+ # Flex mode for spacing (default: <tt>:legacy</tt>).
86
+ #
87
+ # Returns an Array of <tt>Rect</tt> objects.
88
+ def self.split(area, direction: :vertical, constraints:, flex: :legacy)
89
+ # Duck-typing: If it lacks geometry methods but can be a Hash, convert it.
90
+ if !area.respond_to?(:x) && area.respond_to?(:to_h)
91
+ # Assume it's a Hash-like object with :x, :y, etc.
92
+ hash = area.to_h
93
+ area = Rect.new(
94
+ x: hash.fetch(:x, 0),
95
+ y: hash.fetch(:y, 0),
96
+ width: hash.fetch(:width, 0),
97
+ height: hash.fetch(:height, 0)
98
+ )
102
99
  end
100
+ raw_rects = _split(area, direction, constraints, flex)
101
+ raw_rects.map { |r| Rect.new(x: r[:x], y: r[:y], width: r[:width], height: r[:height]) }
103
102
  end
103
+ end
104
104
  end