ratatui_ruby 0.5.0 → 0.7.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 (311) 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 +10 -4
  7. data/CHANGELOG.md +79 -7
  8. data/README.md +37 -5
  9. data/REUSE.toml +2 -7
  10. data/doc/application_architecture.md +96 -22
  11. data/doc/application_testing.md +76 -30
  12. data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
  13. data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
  14. data/doc/contributors/architectural_overhaul/task.md +37 -0
  15. data/doc/contributors/design/ruby_frontend.md +288 -56
  16. data/doc/contributors/design/rust_backend.md +349 -54
  17. data/doc/contributors/developing_examples.md +134 -49
  18. data/doc/contributors/index.md +7 -5
  19. data/doc/contributors/v1.0.0_blockers.md +1729 -0
  20. data/doc/event_handling.md +11 -3
  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_canvas_demo.png +0 -0
  32. data/doc/images/widget_cell_demo.png +0 -0
  33. data/doc/images/widget_center_demo.png +0 -0
  34. data/doc/images/widget_chart_demo.png +0 -0
  35. data/doc/images/widget_list_demo.png +0 -0
  36. data/doc/images/widget_overlay_demo.png +0 -0
  37. data/doc/images/widget_render.png +0 -0
  38. data/doc/images/widget_rich_text.png +0 -0
  39. data/doc/images/widget_scroll_text.png +0 -0
  40. data/doc/images/widget_sparkline_demo.png +0 -0
  41. data/doc/images/widget_table_demo.png +0 -0
  42. data/doc/images/widget_tabs_demo.png +0 -0
  43. data/doc/images/widget_text_width.png +0 -0
  44. data/doc/index.md +11 -6
  45. data/doc/interactive_design.md +2 -2
  46. data/doc/quickstart.md +127 -165
  47. data/doc/terminal_limitations.md +92 -0
  48. data/doc/v0.7.0_migration.md +236 -0
  49. data/doc/why.md +93 -0
  50. data/examples/app_all_events/README.md +47 -27
  51. data/examples/app_all_events/app.rb +38 -35
  52. data/examples/app_all_events/model/app_model.rb +157 -0
  53. data/examples/app_all_events/model/event_entry.rb +17 -0
  54. data/examples/app_all_events/model/msg.rb +37 -0
  55. data/examples/app_all_events/update.rb +73 -0
  56. data/examples/app_all_events/view/app_view.rb +9 -9
  57. data/examples/app_all_events/view/controls_view.rb +9 -7
  58. data/examples/app_all_events/view/counts_view.rb +13 -9
  59. data/examples/app_all_events/view/live_view.rb +9 -8
  60. data/examples/app_all_events/view/log_view.rb +11 -16
  61. data/examples/app_color_picker/README.md +84 -42
  62. data/examples/app_color_picker/app.rb +24 -62
  63. data/examples/app_color_picker/controls.rb +90 -0
  64. data/examples/app_color_picker/copy_dialog.rb +45 -49
  65. data/examples/app_color_picker/export_pane.rb +126 -0
  66. data/examples/app_color_picker/input.rb +99 -67
  67. data/examples/app_color_picker/main_container.rb +178 -0
  68. data/examples/app_color_picker/palette.rb +55 -26
  69. data/examples/app_login_form/README.md +49 -0
  70. data/examples/app_login_form/app.rb +2 -3
  71. data/examples/app_stateful_interaction/README.md +33 -0
  72. data/examples/app_stateful_interaction/app.rb +272 -0
  73. data/examples/timeout_demo.rb +43 -0
  74. data/examples/verify_quickstart_dsl/README.md +49 -0
  75. data/examples/verify_quickstart_dsl/app.rb +2 -0
  76. data/examples/verify_quickstart_layout/README.md +71 -0
  77. data/examples/verify_quickstart_layout/app.rb +2 -0
  78. data/examples/verify_quickstart_lifecycle/README.md +56 -0
  79. data/examples/verify_quickstart_lifecycle/app.rb +10 -4
  80. data/examples/verify_readme_usage/README.md +43 -0
  81. data/examples/verify_readme_usage/app.rb +8 -2
  82. data/examples/widget_barchart_demo/README.md +50 -0
  83. data/examples/widget_barchart_demo/app.rb +5 -5
  84. data/examples/widget_block_demo/README.md +36 -0
  85. data/examples/widget_block_demo/app.rb +256 -0
  86. data/examples/widget_box_demo/README.md +45 -0
  87. data/examples/widget_calendar_demo/README.md +39 -0
  88. data/examples/widget_calendar_demo/app.rb +5 -1
  89. data/examples/widget_canvas_demo/README.md +27 -0
  90. data/examples/widget_canvas_demo/app.rb +123 -0
  91. data/examples/widget_cell_demo/README.md +36 -0
  92. data/examples/widget_cell_demo/app.rb +31 -24
  93. data/examples/widget_center_demo/README.md +29 -0
  94. data/examples/widget_center_demo/app.rb +116 -0
  95. data/examples/widget_chart_demo/README.md +41 -0
  96. data/examples/widget_chart_demo/app.rb +7 -2
  97. data/examples/widget_gauge_demo/README.md +41 -0
  98. data/examples/widget_layout_split/README.md +44 -0
  99. data/examples/widget_line_gauge_demo/README.md +41 -0
  100. data/examples/widget_list_demo/README.md +49 -0
  101. data/examples/widget_list_demo/app.rb +91 -107
  102. data/examples/widget_map_demo/README.md +39 -0
  103. data/examples/{app_map_demo → widget_map_demo}/app.rb +4 -4
  104. data/examples/widget_overlay_demo/README.md +36 -0
  105. data/examples/widget_overlay_demo/app.rb +248 -0
  106. data/examples/widget_popup_demo/README.md +36 -0
  107. data/examples/widget_ratatui_logo_demo/README.md +34 -0
  108. data/examples/widget_ratatui_logo_demo/app.rb +1 -1
  109. data/examples/widget_ratatui_mascot_demo/README.md +34 -0
  110. data/examples/widget_rect/README.md +38 -0
  111. data/examples/widget_render/README.md +37 -0
  112. data/examples/widget_render/app.rb +3 -3
  113. data/examples/widget_rich_text/README.md +35 -0
  114. data/examples/widget_rich_text/app.rb +62 -33
  115. data/examples/widget_scroll_text/README.md +37 -0
  116. data/examples/widget_scroll_text/app.rb +0 -1
  117. data/examples/widget_scrollbar_demo/README.md +37 -0
  118. data/examples/widget_sparkline_demo/README.md +42 -0
  119. data/examples/widget_sparkline_demo/app.rb +4 -3
  120. data/examples/widget_style_colors/README.md +34 -0
  121. data/examples/widget_table_demo/README.md +48 -0
  122. data/examples/{app_table_select → widget_table_demo}/app.rb +65 -12
  123. data/examples/widget_tabs_demo/README.md +41 -0
  124. data/examples/widget_tabs_demo/app.rb +15 -1
  125. data/examples/widget_text_width/README.md +35 -0
  126. data/examples/widget_text_width/app.rb +113 -0
  127. data/exe/.gitkeep +0 -0
  128. data/ext/ratatui_ruby/Cargo.lock +11 -4
  129. data/ext/ratatui_ruby/Cargo.toml +2 -1
  130. data/ext/ratatui_ruby/src/events.rs +238 -26
  131. data/ext/ratatui_ruby/src/frame.rs +116 -3
  132. data/ext/ratatui_ruby/src/lib.rs +37 -6
  133. data/ext/ratatui_ruby/src/rendering.rs +22 -21
  134. data/ext/ratatui_ruby/src/string_width.rs +101 -0
  135. data/ext/ratatui_ruby/src/terminal.rs +39 -15
  136. data/ext/ratatui_ruby/src/text.rs +13 -4
  137. data/ext/ratatui_ruby/src/widgets/barchart.rs +24 -6
  138. data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
  139. data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
  140. data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
  141. data/ext/ratatui_ruby/src/widgets/list.rs +179 -3
  142. data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
  143. data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
  144. data/ext/ratatui_ruby/src/widgets/scrollbar.rs +93 -1
  145. data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
  146. data/ext/ratatui_ruby/src/widgets/table.rs +191 -34
  147. data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
  148. data/lib/ratatui_ruby/buffer/cell.rb +168 -0
  149. data/lib/ratatui_ruby/buffer.rb +15 -0
  150. data/lib/ratatui_ruby/cell.rb +4 -4
  151. data/lib/ratatui_ruby/event/key/character.rb +35 -0
  152. data/lib/ratatui_ruby/event/key/media.rb +44 -0
  153. data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
  154. data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
  155. data/lib/ratatui_ruby/event/key/system.rb +45 -0
  156. data/lib/ratatui_ruby/event/key.rb +111 -51
  157. data/lib/ratatui_ruby/event/mouse.rb +3 -3
  158. data/lib/ratatui_ruby/event/paste.rb +1 -1
  159. data/lib/ratatui_ruby/frame.rb +100 -4
  160. data/lib/ratatui_ruby/layout/constraint.rb +95 -0
  161. data/lib/ratatui_ruby/layout/layout.rb +106 -0
  162. data/lib/ratatui_ruby/layout/rect.rb +118 -0
  163. data/lib/ratatui_ruby/layout.rb +19 -0
  164. data/lib/ratatui_ruby/list_state.rb +88 -0
  165. data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
  166. data/lib/ratatui_ruby/schema/cursor.rb +5 -0
  167. data/lib/ratatui_ruby/schema/gauge.rb +3 -1
  168. data/lib/ratatui_ruby/schema/layout.rb +1 -1
  169. data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
  170. data/lib/ratatui_ruby/schema/list.rb +25 -4
  171. data/lib/ratatui_ruby/schema/list_item.rb +41 -0
  172. data/lib/ratatui_ruby/schema/rect.rb +43 -0
  173. data/lib/ratatui_ruby/schema/row.rb +66 -0
  174. data/lib/ratatui_ruby/schema/style.rb +24 -4
  175. data/lib/ratatui_ruby/schema/table.rb +29 -11
  176. data/lib/ratatui_ruby/schema/text.rb +96 -3
  177. data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
  178. data/lib/ratatui_ruby/style/style.rb +81 -0
  179. data/lib/ratatui_ruby/style.rb +15 -0
  180. data/lib/ratatui_ruby/table_state.rb +90 -0
  181. data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
  182. data/lib/ratatui_ruby/test_helper/snapshot.rb +414 -0
  183. data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
  184. data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
  185. data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
  186. data/lib/ratatui_ruby/test_helper.rb +65 -358
  187. data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
  188. data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
  189. data/lib/ratatui_ruby/tui/core.rb +38 -0
  190. data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
  191. data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
  192. data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
  193. data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
  194. data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
  195. data/lib/ratatui_ruby/tui.rb +75 -0
  196. data/lib/ratatui_ruby/version.rb +1 -1
  197. data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
  198. data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
  199. data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
  200. data/lib/ratatui_ruby/widgets/block.rb +192 -0
  201. data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
  202. data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
  203. data/lib/ratatui_ruby/widgets/cell.rb +47 -0
  204. data/lib/ratatui_ruby/widgets/center.rb +59 -0
  205. data/lib/ratatui_ruby/widgets/chart.rb +185 -0
  206. data/lib/ratatui_ruby/widgets/clear.rb +54 -0
  207. data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
  208. data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
  209. data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
  210. data/lib/ratatui_ruby/widgets/list.rb +127 -0
  211. data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
  212. data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
  213. data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
  214. data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
  215. data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
  216. data/lib/ratatui_ruby/widgets/row.rb +68 -0
  217. data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
  218. data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
  219. data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
  220. data/lib/ratatui_ruby/widgets/table.rb +141 -0
  221. data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
  222. data/lib/ratatui_ruby/widgets.rb +40 -0
  223. data/lib/ratatui_ruby.rb +64 -57
  224. data/sig/examples/app_all_events/view.rbs +1 -1
  225. data/sig/examples/app_all_events/view_state.rbs +1 -1
  226. data/sig/examples/app_stateful_interaction/app.rbs +33 -0
  227. data/sig/examples/widget_block_demo/app.rbs +32 -0
  228. data/sig/examples/{app_map_demo → widget_map_demo}/app.rbs +2 -2
  229. data/sig/examples/{app_table_select → widget_table_demo}/app.rbs +2 -2
  230. data/sig/examples/{widget_table_flex → widget_text_width}/app.rbs +2 -3
  231. data/sig/ratatui_ruby/event.rbs +11 -1
  232. data/sig/ratatui_ruby/frame.rbs +2 -0
  233. data/sig/ratatui_ruby/list_state.rbs +13 -0
  234. data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -2
  235. data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
  236. data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
  237. data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
  238. data/sig/ratatui_ruby/schema/list.rbs +4 -2
  239. data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
  240. data/sig/ratatui_ruby/schema/rect.rbs +3 -0
  241. data/sig/ratatui_ruby/schema/row.rbs +22 -0
  242. data/sig/ratatui_ruby/schema/style.rbs +3 -3
  243. data/sig/ratatui_ruby/schema/table.rbs +3 -1
  244. data/sig/ratatui_ruby/schema/text.rbs +9 -6
  245. data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
  246. data/sig/ratatui_ruby/session.rbs +41 -48
  247. data/sig/ratatui_ruby/table_state.rbs +15 -0
  248. data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
  249. data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
  250. data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
  251. data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
  252. data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
  253. data/sig/ratatui_ruby/test_helper.rbs +5 -4
  254. data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
  255. data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
  256. data/sig/ratatui_ruby/tui/core.rbs +14 -0
  257. data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
  258. data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
  259. data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
  260. data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
  261. data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
  262. data/sig/ratatui_ruby/tui.rbs +19 -0
  263. data/tasks/autodoc/examples.rb +79 -0
  264. data/tasks/autodoc.rake +7 -35
  265. data/tasks/bump/changelog.rb +3 -3
  266. data/tasks/bump/links.rb +67 -0
  267. data/tasks/sourcehut.rake +64 -21
  268. data/tasks/terminal_preview/app_screenshot.rb +13 -3
  269. data/tasks/terminal_preview/saved_screenshot.rb +4 -3
  270. metadata +169 -48
  271. data/doc/contributors/dwim_dx.md +0 -366
  272. data/doc/images/app_analytics.png +0 -0
  273. data/doc/images/app_custom_widget.png +0 -0
  274. data/doc/images/app_mouse_events.png +0 -0
  275. data/doc/images/app_table_select.png +0 -0
  276. data/doc/images/widget_block_padding.png +0 -0
  277. data/doc/images/widget_block_titles.png +0 -0
  278. data/doc/images/widget_list_styles.png +0 -0
  279. data/doc/images/widget_table_flex.png +0 -0
  280. data/examples/app_all_events/model/events.rb +0 -180
  281. data/examples/app_all_events/model/highlight.rb +0 -57
  282. data/examples/app_all_events/test/snapshots/after_focus_lost.txt +0 -24
  283. data/examples/app_all_events/test/snapshots/after_focus_regained.txt +0 -24
  284. data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +0 -24
  285. data/examples/app_all_events/test/snapshots/after_key_a.txt +0 -24
  286. data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +0 -24
  287. data/examples/app_all_events/test/snapshots/after_mouse_click.txt +0 -24
  288. data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +0 -24
  289. data/examples/app_all_events/test/snapshots/after_multiple_events.txt +0 -24
  290. data/examples/app_all_events/test/snapshots/after_paste.txt +0 -24
  291. data/examples/app_all_events/test/snapshots/after_resize.txt +0 -24
  292. data/examples/app_all_events/test/snapshots/after_right_click.txt +0 -24
  293. data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +0 -24
  294. data/examples/app_all_events/test/snapshots/initial_state.txt +0 -24
  295. data/examples/app_all_events/view_state.rb +0 -42
  296. data/examples/app_color_picker/scene.rb +0 -201
  297. data/examples/widget_block_padding/app.rb +0 -67
  298. data/examples/widget_block_titles/app.rb +0 -69
  299. data/examples/widget_list_styles/app.rb +0 -141
  300. data/examples/widget_table_flex/app.rb +0 -95
  301. data/lib/ratatui_ruby/session/autodoc.rb +0 -417
  302. data/lib/ratatui_ruby/session.rb +0 -163
  303. data/sig/examples/widget_block_padding/app.rbs +0 -11
  304. data/sig/examples/widget_block_titles/app.rbs +0 -11
  305. data/sig/examples/widget_list_styles/app.rbs +0 -11
  306. data/tasks/autodoc/inventory.rb +0 -61
  307. data/tasks/autodoc/notice.rb +0 -26
  308. data/tasks/autodoc/rbs.rb +0 -38
  309. data/tasks/autodoc/rdoc.rb +0 -45
  310. data/tasks/bump/comparison_links.rb +0 -41
  311. /data/doc/images/{app_map_demo.png → widget_map_demo.png} +0 -0
@@ -23,11 +23,11 @@ require "ratatui_ruby"
23
23
  # rdoc-image:/doc/images/widget_barchart_demo.png
24
24
  class WidgetBarchartDemo
25
25
  def initialize
26
- @data_index = 0
26
+ @data_index = 2
27
27
  @styles = nil # Initialized in run
28
- @style_index = 0
29
- @label_style_index = 0
30
- @value_style_index = 0
28
+ @style_index = 3
29
+ @label_style_index = 3
30
+ @value_style_index = 3
31
31
  @bar_sets = [
32
32
  { name: "Default", set: nil },
33
33
  { name: "Numbers (Short)", set: { 8 => "8", 7 => "7", 6 => "6", 5 => "5", 4 => "4", 3 => "3", 2 => "2", 1 => "1", 0 => "0" } },
@@ -38,7 +38,7 @@ class WidgetBarchartDemo
38
38
  @direction = :vertical
39
39
  @bar_width = 8
40
40
  @bar_gap = 1
41
- @group_gap = 0
41
+ @group_gap = 2
42
42
  @height_mode = :full
43
43
  @hotkey_style = nil
44
44
  end
@@ -0,0 +1,36 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+ # Block Widget Demo
6
+
7
+ [![Block Demo](../../doc/images/widget_block_demo.png)](app.rb)
8
+
9
+ This example demonstrates the versatile `Block` widget, which provides the visual container, borders, and titles for almost every other widget in `ratatui_ruby`.
10
+
11
+ ## Key Concepts
12
+
13
+ - **Borders:** Choose which sides to show and apply different border types (plain, rounded, double, thick, etc.).
14
+ - **Titles:** Add a main title with alignment or multiple titles at the top and bottom with independent styles and positions.
15
+ - **Padding:** Define inner spacing using uniform or directional (L/R/T/B) values.
16
+ - **Styling:** Individually style the block's content area, its borders, and its titles.
17
+ - **Custom Border Sets:** Create entirely custom border appearances by defining each character in the border set.
18
+
19
+ ## Hotkeys
20
+
21
+ - `t`: Cycle **Title** (None, Main Title)
22
+ - `a`: Cycle **Title Alignment** (Left, Center, Right)
23
+ - `s`: Cycle **Title Style** (None, Cyan Bold, Yellow Italic)
24
+ - `e`: Cycle **Additional Titles** (None, Top+Bottom, Complex)
25
+ - `b`: Cycle **Borders** (All, Top/Bottom, Left/Right, None)
26
+ - `y`: Cycle **Border Type** (Rounded, Plain, Double, Thick, Quadrant, Custom)
27
+ - `c`: Cycle **Border Style** (Magenta Bold, None, Green, Blue on White)
28
+ - `p`: Cycle **Padding** (Uniform, None, Directional, Narrow)
29
+ - `f`: Cycle **Base Style** (Dark Gray, None, White on Black)
30
+ - `q`: **Quit**
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ ruby examples/widget_block_demo/app.rb
36
+ ```
@@ -0,0 +1,256 @@
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 the Block widget with interactive attribute cycling.
10
+ #
11
+ # Blocks are the foundation of terminal layouts, providing structure, borders, and titles.
12
+ # This demo showcases all available parameters, including advanced title positioning,
13
+ # directional padding, and custom border sets.
14
+ #
15
+ # === Examples
16
+ #
17
+ # Run the demo from the terminal:
18
+ #
19
+ # ruby examples/widget_block_demo/app.rb
20
+ #
21
+ # rdoc-image:/doc/images/widget_block_demo.png
22
+ class WidgetBlockDemo
23
+ def initialize
24
+ @title_configs = [
25
+ { name: "None", title: nil },
26
+ { name: "Main Title", title: "Main Title" },
27
+ ]
28
+ @title_index = 1
29
+
30
+ @titles_configs = [
31
+ { name: "None", titles: [] },
32
+ {
33
+ name: "Top + Bottom",
34
+ titles: [
35
+ { content: "Top Right", alignment: :right, position: :top },
36
+ { content: "Bottom Left", alignment: :left, position: :bottom },
37
+ ],
38
+ },
39
+ {
40
+ name: "Complex",
41
+ titles: [
42
+ { content: "★ Left ★", alignment: :left, position: :top },
43
+ { content: "Center", alignment: :center, position: :top },
44
+ { content: "Right", alignment: :right, position: :top },
45
+ { content: "Bottom Center", alignment: :center, position: :bottom },
46
+ ],
47
+ },
48
+ ]
49
+ @titles_index = 1
50
+
51
+ @alignment_configs = [
52
+ { name: "Left", alignment: :left },
53
+ { name: "Center", alignment: :center },
54
+ { name: "Right", alignment: :right },
55
+ ]
56
+ @alignment_index = 1 # Center
57
+
58
+ @border_configs = [
59
+ { name: "All", borders: [:all] },
60
+ { name: "Top/Bottom", borders: [:top, :bottom] },
61
+ { name: "Left/Right", borders: [:left, :right] },
62
+ { name: "None", borders: [] },
63
+ ]
64
+ @border_index = 0
65
+
66
+ @border_type_configs = [
67
+ { name: "Rounded", type: :rounded },
68
+ { name: "Plain", type: :plain },
69
+ { name: "Double", type: :double },
70
+ { name: "Thick", type: :thick },
71
+ { name: "Quadrant Inside", type: :quadrant_inside },
72
+ { name: "Quadrant Outside", type: :quadrant_outside },
73
+ {
74
+ name: "Custom Set",
75
+ type: nil,
76
+ set: {
77
+ top_left: "1",
78
+ top_right: "2",
79
+ bottom_left: "3",
80
+ bottom_right: "4",
81
+ vertical_left: "5",
82
+ vertical_right: "6",
83
+ horizontal_top: "7",
84
+ horizontal_bottom: "8",
85
+ },
86
+ },
87
+ ]
88
+ @border_type_index = 0
89
+
90
+ @padding_configs = [
91
+ { name: "Uniform (2)", padding: 2 },
92
+ { name: "None (0)", padding: 0 },
93
+ { name: "Directional (L:4, T:2)", padding: [4, 0, 2, 0] },
94
+ { name: "Narrow (H:1, V:0)", padding: [1, 1, 0, 0] },
95
+ ]
96
+ @padding_index = 0
97
+ end
98
+
99
+ def run
100
+ RatatuiRuby.run do |tui|
101
+ @tui = tui
102
+
103
+ @title_styles = [
104
+ { name: "None", style: nil },
105
+ { name: "Magenta Bold", style: @tui.style(fg: :magenta, modifiers: [:bold]) },
106
+ { name: "Cyan Bold", style: @tui.style(fg: :cyan, modifiers: [:bold]) },
107
+ { name: "Yellow Italic", style: @tui.style(fg: :yellow, modifiers: [:italic]) },
108
+ ]
109
+ @title_style_index = 1 # Magenta Bold
110
+
111
+ @border_styles = [
112
+ { name: "Cyan", style: @tui.style(fg: :cyan) },
113
+ { name: "Magenta Bold", style: @tui.style(fg: :magenta, modifiers: [:bold]) },
114
+ { name: "None", style: nil },
115
+ { name: "Blue on White", style: @tui.style(fg: :blue, bg: :white) },
116
+ ]
117
+ @border_style_index = 0
118
+
119
+ @base_styles = [
120
+ { name: "Dark Gray", style: @tui.style(fg: :dark_gray) },
121
+ { name: "None", style: nil },
122
+ { name: "White on Black", style: @tui.style(fg: :white, bg: :black) },
123
+ ]
124
+ @base_style_index = 1
125
+
126
+ @hotkey_style = @tui.style(modifiers: [:bold, :underlined])
127
+
128
+ loop do
129
+ render
130
+ break if handle_input == :quit
131
+ sleep 0.05
132
+ end
133
+ end
134
+ end
135
+
136
+ private def render
137
+ title_config = @title_configs[@title_index]
138
+ titles_config = @titles_configs[@titles_index]
139
+ alignment_config = @alignment_configs[@alignment_index]
140
+ title_style_config = @title_styles[@title_style_index]
141
+ border_config = @border_configs[@border_index]
142
+ border_type_config = @border_type_configs[@border_type_index]
143
+ border_style_config = @border_styles[@border_style_index]
144
+ base_style_config = @base_styles[@base_style_index]
145
+ padding_config = @padding_configs[@padding_index]
146
+
147
+ @tui.draw do |frame|
148
+ main_area, control_area = @tui.layout_split(
149
+ frame.area,
150
+ direction: :vertical,
151
+ constraints: [
152
+ @tui.constraint_fill(1),
153
+ @tui.constraint_length(10),
154
+ ]
155
+ )
156
+
157
+ # Render the demo block
158
+ demo_block = @tui.block(
159
+ title: title_config[:title],
160
+ titles: titles_config[:titles],
161
+ title_alignment: alignment_config[:alignment],
162
+ title_style: title_style_config[:style],
163
+ borders: border_config[:borders],
164
+ border_type: border_type_config[:type],
165
+ border_set: border_type_config[:set],
166
+ border_style: border_style_config[:style],
167
+ style: base_style_config[:style],
168
+ padding: padding_config[:padding]
169
+ )
170
+
171
+ # Paragraph inside the block to show padding/content interaction
172
+ content = @tui.paragraph(
173
+ text: "This paragraph is rendered inside the Block widget.\n" \
174
+ "You can see how padding and base style affect this content.\n\n" \
175
+ "Current State:\n" \
176
+ "• Padding: #{padding_config[:name]}\n" \
177
+ "• Borders: #{border_config[:name]}\n" \
178
+ "• Type: #{border_type_config[:name]}",
179
+ block: demo_block
180
+ )
181
+ frame.render_widget(content, main_area)
182
+
183
+ # Render control panel
184
+ control_panel = @tui.block(
185
+ title: "Controls",
186
+ borders: [:all],
187
+ children: [
188
+ @tui.paragraph(
189
+ text: [
190
+ @tui.text_line(spans: [
191
+ @tui.text_span(content: "t", style: @hotkey_style),
192
+ @tui.text_span(content: ": Title (#{title_config[:name]}) "),
193
+ @tui.text_span(content: "a", style: @hotkey_style),
194
+ @tui.text_span(content: ": Alignment (#{alignment_config[:name]}) "),
195
+ @tui.text_span(content: "s", style: @hotkey_style),
196
+ @tui.text_span(content: ": Title Style (#{title_style_config[:name]})"),
197
+ ]),
198
+ @tui.text_line(spans: [
199
+ @tui.text_span(content: "e", style: @hotkey_style),
200
+ @tui.text_span(content: ": Additional Titles (#{titles_config[:name]})"),
201
+ ]),
202
+ @tui.text_line(spans: [
203
+ @tui.text_span(content: "b", style: @hotkey_style),
204
+ @tui.text_span(content: ": Borders (#{border_config[:name]}) "),
205
+ @tui.text_span(content: "y", style: @hotkey_style),
206
+ @tui.text_span(content: ": Border Type (#{border_type_config[:name]})"),
207
+ ]),
208
+ @tui.text_line(spans: [
209
+ @tui.text_span(content: "c", style: @hotkey_style),
210
+ @tui.text_span(content: ": Border Style (#{border_style_config[:name]}) "),
211
+ @tui.text_span(content: "p", style: @hotkey_style),
212
+ @tui.text_span(content: ": Padding (#{padding_config[:name]})"),
213
+ ]),
214
+ @tui.text_line(spans: [
215
+ @tui.text_span(content: "f", style: @hotkey_style),
216
+ @tui.text_span(content: ": Base Style (#{base_style_config[:name]}) "),
217
+ @tui.text_span(content: "q", style: @hotkey_style),
218
+ @tui.text_span(content: ": Quit"),
219
+ ]),
220
+ ]
221
+ ),
222
+ ]
223
+ )
224
+ frame.render_widget(control_panel, control_area)
225
+ end
226
+ end
227
+
228
+ private def handle_input
229
+ case @tui.poll_event
230
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
231
+ :quit
232
+ in type: :key, code: "t"
233
+ @title_index = (@title_index + 1) % @title_configs.size
234
+ in type: :key, code: "e"
235
+ @titles_index = (@titles_index + 1) % @titles_configs.size
236
+ in type: :key, code: "a"
237
+ @alignment_index = (@alignment_index + 1) % @alignment_configs.size
238
+ in type: :key, code: "s"
239
+ @title_style_index = (@title_style_index + 1) % @title_styles.size
240
+ in type: :key, code: "b"
241
+ @border_index = (@border_index + 1) % @border_configs.size
242
+ in type: :key, code: "y"
243
+ @border_type_index = (@border_type_index + 1) % @border_type_configs.size
244
+ in type: :key, code: "c"
245
+ @border_style_index = (@border_style_index + 1) % @border_styles.size
246
+ in type: :key, code: "p"
247
+ @padding_index = (@padding_index + 1) % @padding_configs.size
248
+ in type: :key, code: "f"
249
+ @base_style_index = (@base_style_index + 1) % @base_styles.size
250
+ else
251
+ nil
252
+ end
253
+ end
254
+ end
255
+
256
+ WidgetBlockDemo.new.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,45 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Box (Block) Widget Example
7
+
8
+ [![widget_box_demo](../../doc/images/widget_box_demo.png)](app.rb)
9
+
10
+ Demonstrates visual container attributes with interactive cycling.
11
+
12
+ Widgets often float in a void. Without boundaries, interfaces become a chaotic mess of text. `Block` (often called a Box) provides the structure and visual hierarchy needed to organize information.
13
+
14
+ ## Features Demonstrated
15
+
16
+ - **Border Types**: Native styles like `:plain`, `:rounded`, `:double`, `:thick`.
17
+ - **Custom Borders**: defining completely custom character sets (e.g. using numbers or letters for borders).
18
+ - **Styling**: Independent control over border color, background style, and title style.
19
+ - **Positioning**: Placing titles on the `:top` or `:bottom` border (`position`).
20
+ - **Alignment**: Aligning titles to `:left`, `:center`, or `:right` (`alignment`).
21
+
22
+ ## Hotkeys
23
+
24
+ - **Arrow Keys**: Change Border/Content Color
25
+ - **Space**: Cycle Border Type (`border_type`)
26
+ - **c**: Cycle Custom Border Set (`border_set`)
27
+ - **Enter**: Cycle Title Alignment (`title_alignment`)
28
+ - **s**: Cycle Content Style (`style`)
29
+ - **t**: Cycle Title Style (`title_style`)
30
+ - **b**: Cycle Border Style (`border_style`)
31
+ - **q**: Quit
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ ruby examples/widget_box_demo/app.rb
37
+ ```
38
+
39
+ ## Learning Outcomes
40
+
41
+ Use this example if you need to...
42
+ - Group related widgets together.
43
+ - Create distinct "panels" or "cards" in your UI.
44
+ - Style borders to indicate state (e.g., Red border for error state).
45
+ - Understand the difference between `style` (content) and `border_style` (frame).
@@ -0,0 +1,39 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Calendar Widget Example
7
+
8
+ [![widget_calendar_demo](../../doc/images/widget_calendar_demo.png)](app.rb)
9
+
10
+ Demonstrates a monthly calendar with customizable headers and event highlighting.
11
+
12
+ Rendering dates in a grid involves complex calculations for leap years and weekday offsets. This widget handles that logic, letting you focus on displaying dates.
13
+
14
+ ## Features Demonstrated
15
+
16
+ - **Headers**: Toggling the Month name and Weekday labels.
17
+ - **Surrounding Days**: displaying (or hiding/dimming) days from the previous and next months to fill the grid.
18
+ - **Event Styling**: applying specific styles to individual `Time` or `Date` objects to highlight events.
19
+
20
+ ## Hotkeys
21
+
22
+ - **h**: Toggle Month Header (`show_month_header`)
23
+ - **w**: Toggle Weekday Labels (`show_weekdays_header`)
24
+ - **s**: Toggle Surrounding Days (`show_surrounding`)
25
+ - **e**: Toggle Example Events (`events`)
26
+ - **q**: Quit
27
+
28
+ ## Usage
29
+
30
+ ```bash
31
+ ruby examples/widget_calendar_demo/app.rb
32
+ ```
33
+
34
+ ## Learning Outcomes
35
+
36
+ Use this example if you need to...
37
+ - Display a date picker.
38
+ - Show a schedule or timeline view.
39
+ - Highlight specific dates (deadlines, holidays) on a calendar grid.
@@ -23,6 +23,10 @@ require "ratatui_ruby"
23
23
  #
24
24
  # rdoc-image:/doc/images/widget_calendar_demo.png
25
25
  class WidgetCalendarDemo
26
+ def initialize(date: nil)
27
+ @date = date
28
+ end
29
+
26
30
  def run
27
31
  RatatuiRuby.run do |tui|
28
32
  show_header = true
@@ -32,7 +36,7 @@ class WidgetCalendarDemo
32
36
  hotkey_style = tui.style(modifiers: [:bold])
33
37
 
34
38
  loop do
35
- now = Time.now
39
+ now = @date || Time.now
36
40
  surrounding_style = show_surrounding ? tui.style(fg: "gray", modifiers: [:dim]) : nil
37
41
 
38
42
  events_map = if show_events
@@ -0,0 +1,27 @@
1
+ # Canvas Widget Demo
2
+
3
+ [![widget_canvas_demo](../../doc/images/widget_canvas_demo.png)](app.rb)
4
+
5
+ <!--
6
+ SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
7
+ SPDX-License-Identifier: CC-BY-SA-4.0
8
+ -->
9
+
10
+ This example demonstrates the `Canvas` widget, which provides a high-resolution drawing surface using Braille characters.
11
+
12
+ ## Key Concepts
13
+
14
+ - **Shapes**: Supports `Point`, `Line`, `Rectangle`, `Circle`, and `Map`.
15
+ - **Coordinates**: Uses a float-based coordinate system independent of terminal cells.
16
+ - **Markers**: Can use Braille (high res) or block characters (lower res) for rendering.
17
+
18
+ ## Controls
19
+
20
+ | Key | Action |
21
+ | --- | --- |
22
+ | `q` | Quit |
23
+
24
+ ## Screenshot
25
+ ## Source Code
26
+
27
+ - [app.rb](app.rb)
@@ -0,0 +1,123 @@
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
+ # Demo: Canvas Widget
10
+ # Demonstrates how to draw geometric shapes (Points, Lines, Rects, Circles)
11
+ # on a high-resolution canvas.
12
+ class WidgetCanvasDemo
13
+ def initialize
14
+ @x_offset = 0.0
15
+ @y_offset = 0.0
16
+ @time = 0.0
17
+ end
18
+
19
+ def run
20
+ RatatuiRuby.run do |tui|
21
+ @tui = tui
22
+ loop do
23
+ # Animate
24
+ @time += 0.1
25
+ @x_offset = Math.sin(@time) * 20.0
26
+ @y_offset = Math.cos(@time) * 20.0
27
+
28
+ render
29
+ break if handle_input == :quit
30
+
31
+ sleep 0.05
32
+ end
33
+ end
34
+ end
35
+
36
+ private def render
37
+ @tui.draw do |frame|
38
+ # Define shapes
39
+ shapes = []
40
+
41
+ # 1. Static Grid (Lines)
42
+ (-100..100).step(20) do |i|
43
+ shapes << @tui.shape_line(x1: i.to_f, y1: -100.0, x2: i.to_f, y2: 100.0, color: :gray)
44
+ shapes << @tui.shape_line(x1: -100.0, y1: i.to_f, x2: 100.0, y2: i.to_f, color: :gray)
45
+ end
46
+
47
+ # 2. Moving Circle (The "Player")
48
+ shapes << @tui.shape_circle(
49
+ x: @x_offset,
50
+ y: @y_offset,
51
+ radius: 10.0,
52
+ color: :green
53
+ )
54
+
55
+ # 3. Static Rectangle (Target)
56
+ shapes << @tui.shape_rectangle(
57
+ x: 30.0,
58
+ y: 30.0,
59
+ width: 20.0,
60
+ height: 20.0,
61
+ color: :red
62
+ )
63
+
64
+ # 4. Points (Starfield)
65
+ # Deterministic "random" points
66
+ 10.times do |i|
67
+ shapes << @tui.shape_point(
68
+ x: ((i * 37) % 200) - 100.0,
69
+ y: ((i * 19) % 200) - 100.0
70
+ )
71
+ end
72
+
73
+ # 5. Label
74
+ shapes << @tui.shape_line(x1: 0.0, y1: 0.0, x2: @x_offset, y2: @y_offset, color: :yellow)
75
+
76
+ canvas = @tui.canvas(
77
+ shapes:,
78
+ x_bounds: [-100.0, 100.0],
79
+ y_bounds: [-100.0, 100.0],
80
+ marker: :braille,
81
+ block: @tui.block(title: "Canvas Demo", borders: [:all])
82
+ )
83
+
84
+ # Main area for canvas
85
+ layout = @tui.layout_split(
86
+ frame.area,
87
+ direction: :vertical,
88
+ constraints: [
89
+ @tui.constraint_fill(1),
90
+ @tui.constraint_length(3),
91
+ ]
92
+ )
93
+
94
+ frame.render_widget(canvas, layout[0])
95
+
96
+ # Controls
97
+ controls = @tui.paragraph(
98
+ text: [
99
+ @tui.text_line(spans: [
100
+ @tui.text_span(content: "Canvas auto-animates.", style: @tui.style(fg: :yellow)),
101
+ ]),
102
+ @tui.text_line(spans: [
103
+ @tui.text_span(content: "q", style: @tui.style(modifiers: [:bold, :underlined])),
104
+ @tui.text_span(content: ": Quit"),
105
+ ]),
106
+ ],
107
+ block: @tui.block(borders: [:top])
108
+ )
109
+ frame.render_widget(controls, layout[1])
110
+ end
111
+ end
112
+
113
+ def handle_input
114
+ case @tui.poll_event
115
+ in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
116
+ :quit
117
+ else
118
+ # Ignore other events
119
+ end
120
+ end
121
+ end
122
+
123
+ WidgetCanvasDemo.new.run if __FILE__ == $0
@@ -0,0 +1,36 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Cell Widget Example
7
+
8
+ [![widget_cell_demo](../../doc/images/widget_cell_demo.png)](app.rb)
9
+
10
+ Demonstrates using `Cell` objects for granular control over individual character grid units.
11
+
12
+ Sometimes you need to render specific characters with unique styles outside of standard widgets. The `Cell` primitive allows you to build custom widgets or inject styled content into Tables.
13
+
14
+ ## Features Demonstrated
15
+
16
+ - **Custom Widgets**: A `CheckeredBackground` widget built by rendering `Cell`s in a loop.
17
+ - **Table Integration**: Mixing simple Strings and rich `Cell` objects in a Table row.
18
+ - **Overlays**: Using `RatatuiRuby::Overlay` to stack widgets on top of each other.
19
+ - **Modifiers**: Using `rapid_blink`, `bold`, and `dim` on individual cells.
20
+
21
+ ## Hotkeys
22
+
23
+ - **q** / **Ctrl+c**: Quit
24
+
25
+ ## Usage
26
+
27
+ ```bash
28
+ ruby examples/widget_cell_demo/app.rb
29
+ ```
30
+
31
+ ## Learning Outcomes
32
+
33
+ Use this example if you need to...
34
+ - Create a custom widget (like a game board or specialized graph).
35
+ - Style specific cells in a Table (e.g., Green "OK", Red "FAIL").
36
+ - Understand how to position content precisely with `Cell`.