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.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/AGENTS.md +98 -176
- data/CHANGELOG.md +80 -6
- data/README.md +19 -7
- data/REUSE.toml +15 -0
- data/doc/application_architecture.md +179 -45
- data/doc/application_testing.md +80 -32
- data/doc/contributors/design/ruby_frontend.md +48 -8
- data/doc/contributors/design/rust_backend.md +1 -0
- data/doc/contributors/developing_examples.md +191 -48
- data/doc/contributors/documentation_style.md +7 -0
- data/doc/contributors/examples_audit/p1_high.md +21 -0
- data/doc/contributors/examples_audit/p2_moderate.md +81 -0
- data/doc/contributors/examples_audit.md +41 -0
- data/doc/contributors/index.md +2 -0
- data/doc/event_handling.md +21 -7
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart_demo.png +0 -0
- data/doc/images/widget_block_demo.png +0 -0
- data/doc/images/widget_box_demo.png +0 -0
- data/doc/images/widget_calendar_demo.png +0 -0
- data/doc/images/widget_canvas_demo.png +0 -0
- data/doc/images/widget_cell_demo.png +0 -0
- data/doc/images/widget_center_demo.png +0 -0
- data/doc/images/widget_chart_demo.png +0 -0
- data/doc/images/widget_gauge_demo.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge_demo.png +0 -0
- data/doc/images/widget_list_demo.png +0 -0
- data/doc/images/widget_overlay_demo.png +0 -0
- data/doc/images/widget_ratatui_logo_demo.png +0 -0
- data/doc/images/widget_ratatui_mascot_demo.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar_demo.png +0 -0
- data/doc/images/widget_sparkline_demo.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table_demo.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/interactive_design.md +25 -30
- data/doc/quickstart.md +150 -130
- data/doc/terminal_limitations.md +92 -0
- data/examples/app_all_events/README.md +99 -0
- data/examples/app_all_events/app.rb +96 -0
- data/examples/app_all_events/model/app_model.rb +157 -0
- data/examples/app_all_events/model/event_color_cycle.rb +41 -0
- data/examples/app_all_events/model/event_entry.rb +92 -0
- data/examples/app_all_events/model/msg.rb +37 -0
- data/examples/app_all_events/model/timestamp.rb +54 -0
- data/examples/app_all_events/update.rb +73 -0
- data/examples/app_all_events/view/app_view.rb +78 -0
- data/examples/app_all_events/view/controls_view.rb +52 -0
- data/examples/app_all_events/view/counts_view.rb +59 -0
- data/examples/app_all_events/view/live_view.rb +70 -0
- data/examples/app_all_events/view/log_view.rb +55 -0
- data/examples/app_all_events/view.rb +7 -0
- data/examples/app_color_picker/README.md +134 -0
- data/examples/app_color_picker/app.rb +74 -0
- data/examples/app_color_picker/clipboard.rb +84 -0
- data/examples/app_color_picker/color.rb +191 -0
- data/examples/app_color_picker/controls.rb +90 -0
- data/examples/app_color_picker/copy_dialog.rb +166 -0
- data/examples/app_color_picker/export_pane.rb +126 -0
- data/examples/app_color_picker/harmony.rb +56 -0
- data/examples/app_color_picker/input.rb +174 -0
- data/examples/app_color_picker/main_container.rb +178 -0
- data/examples/app_color_picker/palette.rb +109 -0
- data/examples/app_login_form/README.md +47 -0
- data/examples/{login_form → app_login_form}/app.rb +38 -42
- data/examples/app_stateful_interaction/README.md +31 -0
- data/examples/app_stateful_interaction/app.rb +272 -0
- data/examples/timeout_demo.rb +43 -0
- data/examples/verify_quickstart_dsl/README.md +48 -0
- data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +17 -6
- data/examples/verify_quickstart_layout/README.md +71 -0
- data/examples/verify_quickstart_layout/app.rb +71 -0
- data/examples/verify_quickstart_lifecycle/README.md +56 -0
- data/examples/verify_quickstart_lifecycle/app.rb +54 -0
- data/examples/verify_readme_usage/README.md +43 -0
- data/examples/verify_readme_usage/app.rb +40 -0
- data/examples/widget_barchart_demo/README.md +49 -0
- data/examples/widget_barchart_demo/app.rb +238 -0
- data/examples/widget_block_demo/README.md +34 -0
- data/examples/widget_block_demo/app.rb +256 -0
- data/examples/widget_box_demo/README.md +45 -0
- data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
- data/examples/widget_calendar_demo/README.md +39 -0
- data/examples/widget_calendar_demo/app.rb +109 -0
- data/examples/widget_canvas_demo/README.md +27 -0
- data/examples/widget_canvas_demo/app.rb +123 -0
- data/examples/widget_cell_demo/README.md +36 -0
- data/examples/widget_cell_demo/app.rb +111 -0
- data/examples/widget_center_demo/README.md +29 -0
- data/examples/widget_center_demo/app.rb +116 -0
- data/examples/widget_chart_demo/README.md +41 -0
- data/examples/widget_chart_demo/app.rb +218 -0
- data/examples/widget_gauge_demo/README.md +41 -0
- data/examples/widget_gauge_demo/app.rb +212 -0
- data/examples/widget_layout_split/README.md +44 -0
- data/examples/widget_layout_split/app.rb +246 -0
- data/examples/widget_line_gauge_demo/README.md +41 -0
- data/examples/widget_line_gauge_demo/app.rb +217 -0
- data/examples/widget_list_demo/README.md +49 -0
- data/examples/widget_list_demo/app.rb +366 -0
- data/examples/widget_map_demo/README.md +39 -0
- data/examples/{map_demo → widget_map_demo}/app.rb +24 -21
- data/examples/widget_overlay_demo/app.rb +248 -0
- data/examples/widget_popup_demo/README.md +36 -0
- data/examples/widget_popup_demo/app.rb +104 -0
- data/examples/widget_ratatui_logo_demo/README.md +34 -0
- data/examples/widget_ratatui_logo_demo/app.rb +103 -0
- data/examples/widget_ratatui_mascot_demo/README.md +34 -0
- data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
- data/examples/widget_rect/README.md +38 -0
- data/examples/widget_rect/app.rb +205 -0
- data/examples/widget_render/README.md +37 -0
- data/examples/widget_render/app.rb +184 -0
- data/examples/widget_rich_text/README.md +35 -0
- data/examples/widget_rich_text/app.rb +166 -0
- data/examples/widget_scroll_text/README.md +37 -0
- data/examples/widget_scroll_text/app.rb +107 -0
- data/examples/widget_scrollbar_demo/README.md +37 -0
- data/examples/widget_scrollbar_demo/app.rb +153 -0
- data/examples/widget_sparkline_demo/README.md +42 -0
- data/examples/widget_sparkline_demo/app.rb +275 -0
- data/examples/widget_style_colors/README.md +34 -0
- data/examples/widget_style_colors/app.rb +19 -21
- data/examples/widget_table_demo/README.md +48 -0
- data/examples/widget_table_demo/app.rb +239 -0
- data/examples/widget_tabs_demo/README.md +41 -0
- data/examples/widget_tabs_demo/app.rb +181 -0
- data/examples/widget_text_width/README.md +35 -0
- data/examples/widget_text_width/app.rb +106 -0
- data/ext/ratatui_ruby/Cargo.lock +11 -4
- data/ext/ratatui_ruby/Cargo.toml +2 -1
- data/ext/ratatui_ruby/src/events.rs +359 -62
- data/ext/ratatui_ruby/src/frame.rs +227 -0
- data/ext/ratatui_ruby/src/lib.rs +110 -27
- data/ext/ratatui_ruby/src/rendering.rs +8 -4
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/style.rs +138 -57
- data/ext/ratatui_ruby/src/terminal.rs +42 -22
- data/ext/ratatui_ruby/src/text.rs +14 -7
- data/ext/ratatui_ruby/src/widgets/barchart.rs +74 -54
- data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
- data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
- data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
- data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
- data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
- data/ext/ratatui_ruby/src/widgets/list.rs +211 -12
- data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +97 -3
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
- data/ext/ratatui_ruby/src/widgets/table.rs +121 -5
- data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
- data/lib/ratatui_ruby/cell.rb +7 -7
- data/lib/ratatui_ruby/event/key/character.rb +35 -0
- data/lib/ratatui_ruby/event/key/media.rb +44 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
- data/lib/ratatui_ruby/event/key/system.rb +45 -0
- data/lib/ratatui_ruby/event/key.rb +112 -52
- data/lib/ratatui_ruby/event/mouse.rb +3 -3
- data/lib/ratatui_ruby/event/none.rb +43 -0
- data/lib/ratatui_ruby/event/paste.rb +1 -1
- data/lib/ratatui_ruby/event.rb +56 -4
- data/lib/ratatui_ruby/frame.rb +183 -0
- data/lib/ratatui_ruby/list_state.rb +88 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +13 -13
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
- data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
- data/lib/ratatui_ruby/schema/block.rb +163 -168
- data/lib/ratatui_ruby/schema/calendar.rb +66 -67
- data/lib/ratatui_ruby/schema/canvas.rb +63 -63
- data/lib/ratatui_ruby/schema/center.rb +46 -46
- data/lib/ratatui_ruby/schema/chart.rb +135 -143
- data/lib/ratatui_ruby/schema/clear.rb +42 -42
- data/lib/ratatui_ruby/schema/constraint.rb +76 -76
- data/lib/ratatui_ruby/schema/cursor.rb +30 -25
- data/lib/ratatui_ruby/schema/gauge.rb +54 -52
- data/lib/ratatui_ruby/schema/layout.rb +87 -87
- data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
- data/lib/ratatui_ruby/schema/list.rb +103 -80
- data/lib/ratatui_ruby/schema/list_item.rb +41 -0
- data/lib/ratatui_ruby/schema/overlay.rb +31 -31
- data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
- data/lib/ratatui_ruby/schema/rect.rb +99 -56
- data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
- data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
- data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
- data/lib/ratatui_ruby/schema/style.rb +66 -46
- data/lib/ratatui_ruby/schema/table.rb +126 -115
- data/lib/ratatui_ruby/schema/tabs.rb +66 -67
- data/lib/ratatui_ruby/schema/text.rb +69 -1
- data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
- data/lib/ratatui_ruby/session/autodoc.rb +482 -0
- data/lib/ratatui_ruby/session.rb +55 -23
- data/lib/ratatui_ruby/table_state.rb +90 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +390 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
- data/lib/ratatui_ruby/test_helper.rb +66 -193
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +100 -51
- data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
- data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
- data/sig/examples/app_all_events/model/events.rbs +15 -0
- data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
- data/sig/examples/app_all_events/view/app_view.rbs +8 -0
- data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
- data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
- data/sig/examples/app_all_events/view/live_view.rbs +6 -0
- data/sig/examples/app_all_events/view/log_view.rbs +6 -0
- data/sig/examples/app_all_events/view.rbs +8 -0
- data/sig/examples/app_all_events/view_state.rbs +15 -0
- data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_stateful_interaction/app.rbs +33 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
- data/sig/examples/verify_readme_usage/app.rbs +11 -0
- data/sig/examples/widget_block_demo/app.rbs +32 -0
- data/sig/examples/widget_box_demo/app.rbs +11 -0
- data/sig/examples/widget_calendar_demo/app.rbs +11 -0
- data/sig/examples/widget_cell_demo/app.rbs +11 -0
- data/sig/examples/widget_chart_demo/app.rbs +11 -0
- data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
- data/sig/examples/widget_layout_split/app.rbs +10 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
- data/sig/examples/widget_list_demo/app.rbs +12 -0
- data/sig/examples/widget_map_demo/app.rbs +11 -0
- data/sig/examples/widget_popup_demo/app.rbs +11 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
- data/sig/examples/widget_rect/app.rbs +12 -0
- data/sig/examples/widget_render/app.rbs +10 -0
- data/sig/examples/widget_rich_text/app.rbs +11 -0
- data/sig/examples/widget_scroll_text/app.rbs +11 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
- data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
- data/sig/examples/widget_table_demo/app.rbs +11 -0
- data/sig/examples/widget_text_width/app.rbs +10 -0
- data/sig/ratatui_ruby/event.rbs +11 -1
- data/sig/ratatui_ruby/frame.rbs +11 -0
- data/sig/ratatui_ruby/list_state.rbs +13 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -4
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
- data/sig/ratatui_ruby/schema/draw.rbs +4 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
- data/sig/ratatui_ruby/schema/layout.rbs +1 -1
- data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
- data/sig/ratatui_ruby/schema/list.rbs +4 -2
- data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
- data/sig/ratatui_ruby/schema/rect.rbs +3 -0
- data/sig/ratatui_ruby/schema/style.rbs +3 -3
- data/sig/ratatui_ruby/schema/table.rbs +3 -1
- data/sig/ratatui_ruby/schema/text.rbs +8 -6
- data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
- data/sig/ratatui_ruby/session.rbs +107 -0
- data/sig/ratatui_ruby/table_state.rbs +15 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
- data/sig/ratatui_ruby/test_helper.rbs +5 -4
- data/tasks/autodoc/examples.rb +79 -0
- data/tasks/autodoc/inventory.rb +63 -0
- data/tasks/autodoc/member.rb +56 -0
- data/tasks/autodoc/name.rb +19 -0
- data/tasks/autodoc/notice.rb +26 -0
- data/tasks/autodoc/rbs.rb +38 -0
- data/tasks/autodoc/rdoc.rb +45 -0
- data/tasks/autodoc.rake +53 -0
- data/tasks/bump/changelog.rb +3 -3
- data/tasks/bump/history.rb +2 -2
- data/tasks/bump/links.rb +67 -0
- data/tasks/doc.rake +600 -6
- data/tasks/example_viewer.html.erb +172 -0
- data/tasks/lint.rake +8 -4
- data/tasks/resources/index.html.erb +6 -0
- data/tasks/sourcehut.rake +70 -30
- data/tasks/terminal_preview/app_screenshot.rb +14 -6
- data/tasks/terminal_preview/crash_report.rb +7 -9
- data/tasks/terminal_preview/launcher_script.rb +4 -6
- data/tasks/terminal_preview/preview_collection.rb +4 -6
- data/tasks/terminal_preview/safety_confirmation.rb +3 -5
- data/tasks/terminal_preview/saved_screenshot.rb +10 -11
- data/tasks/terminal_preview/terminal_window.rb +7 -9
- data/tasks/test.rake +1 -1
- data/tasks/website/index_page.rb +3 -3
- data/tasks/website/version.rb +10 -10
- data/tasks/website/version_menu.rb +10 -12
- data/tasks/website/versioned_documentation.rb +49 -17
- data/tasks/website/website.rb +6 -8
- data/tasks/website.rake +4 -4
- metadata +232 -127
- data/LICENSES/BSD-2-Clause.txt +0 -9
- data/doc/contributors/better_dx.md +0 -543
- data/doc/contributors/example_analysis.md +0 -82
- data/doc/images/all_events.png +0 -0
- data/doc/images/block_padding.png +0 -0
- data/doc/images/block_titles.png +0 -0
- data/doc/images/box_demo.png +0 -0
- data/doc/images/calendar_demo.png +0 -0
- data/doc/images/cell_demo.png +0 -0
- data/doc/images/chart_demo.png +0 -0
- data/doc/images/flex_layout.png +0 -0
- data/doc/images/gauge_demo.png +0 -0
- data/doc/images/line_gauge_demo.png +0 -0
- data/doc/images/list_demo.png +0 -0
- data/doc/images/list_styles.png +0 -0
- data/doc/images/login_form.png +0 -0
- data/doc/images/quickstart_dsl.png +0 -0
- data/doc/images/quickstart_lifecycle.png +0 -0
- data/doc/images/readme_usage.png +0 -0
- data/doc/images/rich_text.png +0 -0
- data/doc/images/scroll_text.png +0 -0
- data/doc/images/scrollbar_demo.png +0 -0
- data/doc/images/sparkline_demo.png +0 -0
- data/doc/images/table_flex.png +0 -0
- data/doc/images/table_select.png +0 -0
- data/examples/all_events/app.rb +0 -169
- data/examples/all_events/app.rbs +0 -7
- data/examples/all_events/test_app.rb +0 -139
- data/examples/analytics/app.rb +0 -258
- data/examples/analytics/app.rbs +0 -7
- data/examples/analytics/test_app.rb +0 -132
- data/examples/block_padding/app.rb +0 -63
- data/examples/block_padding/app.rbs +0 -7
- data/examples/block_padding/test_app.rb +0 -31
- data/examples/block_titles/app.rb +0 -61
- data/examples/block_titles/app.rbs +0 -7
- data/examples/block_titles/test_app.rb +0 -34
- data/examples/box_demo/app.rbs +0 -7
- data/examples/box_demo/test_app.rb +0 -88
- data/examples/calendar_demo/app.rb +0 -101
- data/examples/calendar_demo/app.rbs +0 -7
- data/examples/calendar_demo/test_app.rb +0 -108
- data/examples/cell_demo/app.rb +0 -108
- data/examples/cell_demo/app.rbs +0 -7
- data/examples/cell_demo/test_app.rb +0 -36
- data/examples/chart_demo/app.rb +0 -203
- data/examples/chart_demo/app.rbs +0 -7
- data/examples/chart_demo/test_app.rb +0 -102
- data/examples/custom_widget/app.rb +0 -51
- data/examples/custom_widget/app.rbs +0 -7
- data/examples/custom_widget/test_app.rb +0 -30
- data/examples/flex_layout/app.rb +0 -156
- data/examples/flex_layout/app.rbs +0 -7
- data/examples/flex_layout/test_app.rb +0 -65
- data/examples/gauge_demo/app.rb +0 -182
- data/examples/gauge_demo/test_app.rb +0 -120
- data/examples/hit_test/app.rb +0 -175
- data/examples/hit_test/app.rbs +0 -7
- data/examples/hit_test/test_app.rb +0 -102
- data/examples/line_gauge_demo/app.rb +0 -190
- data/examples/line_gauge_demo/app.rbs +0 -7
- data/examples/line_gauge_demo/test_app.rb +0 -129
- data/examples/list_demo/app.rb +0 -253
- data/examples/list_demo/test_app.rb +0 -237
- data/examples/list_styles/app.rb +0 -140
- data/examples/list_styles/app.rbs +0 -7
- data/examples/list_styles/test_app.rb +0 -157
- data/examples/login_form/app.rbs +0 -7
- data/examples/login_form/test_app.rb +0 -51
- data/examples/map_demo/app.rbs +0 -7
- data/examples/map_demo/test_app.rb +0 -149
- data/examples/mouse_events/app.rb +0 -97
- data/examples/mouse_events/app.rbs +0 -7
- data/examples/mouse_events/test_app.rb +0 -53
- data/examples/popup_demo/app.rb +0 -103
- data/examples/popup_demo/app.rbs +0 -7
- data/examples/popup_demo/test_app.rb +0 -54
- data/examples/quickstart_dsl/app.rbs +0 -7
- data/examples/quickstart_dsl/test_app.rb +0 -29
- data/examples/quickstart_lifecycle/app.rb +0 -39
- data/examples/quickstart_lifecycle/app.rbs +0 -7
- data/examples/quickstart_lifecycle/test_app.rb +0 -29
- data/examples/ratatui_logo_demo/app.rb +0 -79
- data/examples/ratatui_logo_demo/app.rbs +0 -7
- data/examples/ratatui_logo_demo/test_app.rb +0 -51
- data/examples/ratatui_mascot_demo/app.rb +0 -84
- data/examples/ratatui_mascot_demo/app.rbs +0 -7
- data/examples/ratatui_mascot_demo/test_app.rb +0 -47
- data/examples/readme_usage/app.rb +0 -29
- data/examples/readme_usage/app.rbs +0 -7
- data/examples/readme_usage/test_app.rb +0 -29
- data/examples/rich_text/app.rb +0 -141
- data/examples/rich_text/app.rbs +0 -7
- data/examples/rich_text/test_app.rb +0 -166
- data/examples/scroll_text/app.rb +0 -103
- data/examples/scroll_text/app.rbs +0 -7
- data/examples/scroll_text/test_app.rb +0 -110
- data/examples/scrollbar_demo/app.rb +0 -143
- data/examples/scrollbar_demo/app.rbs +0 -7
- data/examples/scrollbar_demo/test_app.rb +0 -77
- data/examples/sparkline_demo/app.rb +0 -240
- data/examples/sparkline_demo/test_app.rb +0 -107
- data/examples/table_flex/app.rb +0 -65
- data/examples/table_flex/app.rbs +0 -7
- data/examples/table_flex/test_app.rb +0 -36
- data/examples/table_select/app.rb +0 -198
- data/examples/table_select/app.rbs +0 -7
- data/examples/table_select/test_app.rb +0 -180
- data/examples/widget_style_colors/test_app.rb +0 -48
- data/tasks/bump/comparison_links.rb +0 -41
- /data/doc/images/{analytics.png → app_analytics.png} +0 -0
- /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
- /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
- /data/doc/images/{map_demo.png → widget_map_demo.png} +0 -0
- /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
- /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
- /data/{doc/images/ratatui_logo_demo.png → exe/.gitkeep} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: CC-BY-SA-4.0
|
|
5
|
+
|
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
# Demonstrates personality and charm with the project mascot.
|
|
10
|
+
#
|
|
11
|
+
# Interfaces without personality feel clinical and dry. Users appreciate a friendly face in their terminal.
|
|
12
|
+
#
|
|
13
|
+
# This demo showcases the <tt>RatatuiMascot</tt> widget. It provides an interactive playground where you can toggle the surrounding block.
|
|
14
|
+
#
|
|
15
|
+
# Use it to understand how to add a playful touch to your terminal dashboards or about screens.
|
|
16
|
+
#
|
|
17
|
+
# === Example
|
|
18
|
+
#
|
|
19
|
+
# Run the demo from the terminal:
|
|
20
|
+
#
|
|
21
|
+
# ruby examples/widget_ratatui_mascot_demo/app.rb
|
|
22
|
+
#
|
|
23
|
+
# rdoc-image:/doc/images/widget_ratatui_mascot_demo.png
|
|
24
|
+
class WidgetRatatuiMascotDemo
|
|
25
|
+
def initialize
|
|
26
|
+
@show_block = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def run
|
|
30
|
+
RatatuiRuby.run do |tui|
|
|
31
|
+
loop do
|
|
32
|
+
tui.draw do |frame|
|
|
33
|
+
# Layout: Top (Mascot), Bottom (Controls)
|
|
34
|
+
layout = tui.layout_split(
|
|
35
|
+
frame.area,
|
|
36
|
+
direction: :vertical,
|
|
37
|
+
constraints: [
|
|
38
|
+
tui.constraint_fill(1),
|
|
39
|
+
tui.constraint_length(4),
|
|
40
|
+
]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
mascot_area = layout[0]
|
|
44
|
+
controls_area = layout[1]
|
|
45
|
+
|
|
46
|
+
# Mascot Widget
|
|
47
|
+
block = if @show_block
|
|
48
|
+
tui.block(
|
|
49
|
+
title: "Ratatui Mascot",
|
|
50
|
+
borders: [:all],
|
|
51
|
+
border_type: :rounded,
|
|
52
|
+
border_style: { fg: :green }
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
mascot = tui.ratatui_mascot(block:)
|
|
57
|
+
frame.render_widget(mascot, mascot_area)
|
|
58
|
+
|
|
59
|
+
# Controls
|
|
60
|
+
controls_text = [
|
|
61
|
+
tui.text_span(content: "q", style: tui.style(modifiers: [:bold, :underlined])),
|
|
62
|
+
tui.text_span(content: " Quit"),
|
|
63
|
+
tui.text_span(content: " "),
|
|
64
|
+
tui.text_span(content: "b", style: tui.style(modifiers: [:bold, :underlined])),
|
|
65
|
+
tui.text_span(content: " Toggle Block #{@show_block ? '(On)' : '(Off)'}"),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
controls_paragraph = tui.paragraph(
|
|
69
|
+
text: tui.text_line(spans: controls_text),
|
|
70
|
+
block: tui.block(borders: [:top], title: "Controls")
|
|
71
|
+
)
|
|
72
|
+
frame.render_widget(controls_paragraph, controls_area)
|
|
73
|
+
end
|
|
74
|
+
break if handle_input(tui) == :quit
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private def handle_input(tui)
|
|
80
|
+
event = tui.poll_event
|
|
81
|
+
|
|
82
|
+
if event.key?
|
|
83
|
+
case event.char
|
|
84
|
+
when "q" then :quit
|
|
85
|
+
when "b" then @show_block = !@show_block
|
|
86
|
+
end
|
|
87
|
+
elsif event.ctrl_c?
|
|
88
|
+
:quit
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
WidgetRatatuiMascotDemo.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Rect (Geometry) Widget Example
|
|
7
|
+
|
|
8
|
+
Demonstrates the Rect geometry primitive and hit-testing patterns.
|
|
9
|
+
|
|
10
|
+
TUI layouts are composed of rectangles. Understanding how to manipulate `Rect` objects, reuse them from the layout phase, and use them for mouse interaction is critical for building interactive apps.
|
|
11
|
+
|
|
12
|
+
## Features Demonstrated
|
|
13
|
+
|
|
14
|
+
- **Rect Attributes**: Investigating x, y, width, and height.
|
|
15
|
+
- **Cached Layout Pattern**: Computing constraints in the render loop and reusing the resulting `Rect`s in the event loop for logic.
|
|
16
|
+
- **Hit Testing**: Using `Rect#contains?(x, y)` to determine if a mouse click happened inside a specific panel.
|
|
17
|
+
|
|
18
|
+
## Hotkeys
|
|
19
|
+
|
|
20
|
+
- **Arrows (←/→)**: Expand/Shrink Sidebar Width (Layout Constraint)
|
|
21
|
+
- **Arrows (↑/↓)**: Navigate Menu Selection (`selected_index`)
|
|
22
|
+
- **Mouse Click**: Click anywhere to see which Rect detects the hit (`contains?`)
|
|
23
|
+
- **q**: Quit
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
ruby examples/widget_rect/app.rb
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Learning Outcomes
|
|
32
|
+
|
|
33
|
+
Use this example if you need to...
|
|
34
|
+
- Handle mouse clicks on specific buttons or areas.
|
|
35
|
+
- Create resizable panes (like a split pane in an IDE).
|
|
36
|
+
- Debug layout issues by inspecting Rect coordinates.
|
|
37
|
+
|
|
38
|
+

|
|
@@ -0,0 +1,205 @@
|
|
|
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
|
+
# Rect Widget Showcase
|
|
10
|
+
#
|
|
11
|
+
# Demonstrates the Rect class and the Cached Layout Pattern.
|
|
12
|
+
#
|
|
13
|
+
# Rect is the fundamental geometry primitive for TUI layout. This example shows:
|
|
14
|
+
# - Rect attributes: x, y, width, height
|
|
15
|
+
# - Rect#contains? for hit testing mouse clicks
|
|
16
|
+
# - Layout.split returning cached rects for reuse
|
|
17
|
+
# - The layout caching pattern: compute in draw, reuse in handle_input
|
|
18
|
+
#
|
|
19
|
+
# Controls:
|
|
20
|
+
# ←/→: Adjust sidebar width
|
|
21
|
+
# ↑/↓: Navigate menu items
|
|
22
|
+
# Mouse: Click panels to test Rect#contains?
|
|
23
|
+
# q: Quit
|
|
24
|
+
class WidgetRect
|
|
25
|
+
MENU_ITEMS = ["Dashboard", "Analytics", "Settings", "Logs", "Help"].freeze
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@sidebar_width = 20
|
|
29
|
+
@selected_index = 0
|
|
30
|
+
@last_action = "Click any panel to test Rect#contains?"
|
|
31
|
+
@click_count = 0
|
|
32
|
+
@sidebar_rect = nil
|
|
33
|
+
@main_rect = nil
|
|
34
|
+
@controls_rect = nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def run
|
|
38
|
+
RatatuiRuby.run do |tui|
|
|
39
|
+
@tui = tui
|
|
40
|
+
@hotkey_style = @tui.style(modifiers: [:bold, :underlined])
|
|
41
|
+
@label_style = @tui.style(modifiers: [:bold])
|
|
42
|
+
@dim_style = @tui.style(fg: :dark_gray)
|
|
43
|
+
|
|
44
|
+
loop do
|
|
45
|
+
render
|
|
46
|
+
break if handle_input == :quit
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private def render
|
|
52
|
+
@tui.draw do |frame|
|
|
53
|
+
@main_rect, @controls_rect = @tui.layout_split(
|
|
54
|
+
frame.area,
|
|
55
|
+
direction: :vertical,
|
|
56
|
+
constraints: [
|
|
57
|
+
@tui.constraint_fill(1),
|
|
58
|
+
@tui.constraint_length(8),
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
@sidebar_rect, @content_rect = @tui.layout_split(
|
|
63
|
+
@main_rect,
|
|
64
|
+
direction: :horizontal,
|
|
65
|
+
constraints: [
|
|
66
|
+
@tui.constraint_length(@sidebar_width),
|
|
67
|
+
@tui.constraint_fill(1),
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
render_sidebar(frame)
|
|
72
|
+
render_content(frame)
|
|
73
|
+
render_controls(frame)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private def render_sidebar(frame)
|
|
78
|
+
sidebar = @tui.list(
|
|
79
|
+
items: MENU_ITEMS,
|
|
80
|
+
selected_index: @selected_index,
|
|
81
|
+
highlight_style: @tui.style(fg: :black, bg: :white, modifiers: [:bold]),
|
|
82
|
+
highlight_symbol: "> ",
|
|
83
|
+
block: @tui.block(title: "Menu", borders: [:all])
|
|
84
|
+
)
|
|
85
|
+
frame.render_widget(sidebar, @sidebar_rect)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private def render_content(frame)
|
|
89
|
+
text_content = [
|
|
90
|
+
@tui.text_line(spans: [
|
|
91
|
+
@tui.text_span(content: "Active View: ", style: @label_style),
|
|
92
|
+
@tui.text_span(content: MENU_ITEMS[@selected_index], style: @tui.style(fg: :green)),
|
|
93
|
+
]),
|
|
94
|
+
"",
|
|
95
|
+
@tui.text_line(spans: [
|
|
96
|
+
@tui.text_span(content: "Rect Attributes ", style: @label_style),
|
|
97
|
+
@tui.text_span(content: "(from Layout.split):", style: @dim_style),
|
|
98
|
+
]),
|
|
99
|
+
" Sidebar: Rect(x:#{@sidebar_rect.x}, y:#{@sidebar_rect.y}, " \
|
|
100
|
+
"width:#{@sidebar_rect.width}, height:#{@sidebar_rect.height})",
|
|
101
|
+
" Content: Rect(x:#{@content_rect.x}, y:#{@content_rect.y}, " \
|
|
102
|
+
"width:#{@content_rect.width}, height:#{@content_rect.height})",
|
|
103
|
+
"",
|
|
104
|
+
@tui.text_line(spans: [
|
|
105
|
+
@tui.text_span(content: "Hit Testing ", style: @label_style),
|
|
106
|
+
@tui.text_span(content: "(Rect#contains?):", style: @dim_style),
|
|
107
|
+
]),
|
|
108
|
+
" Clicks: #{@click_count} | #{@last_action}",
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
paragraph = @tui.paragraph(
|
|
112
|
+
text: text_content,
|
|
113
|
+
block: @tui.block(title: "Content", borders: [:all])
|
|
114
|
+
)
|
|
115
|
+
frame.render_widget(paragraph, @content_rect)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private def render_controls(frame)
|
|
119
|
+
controls = @tui.block(
|
|
120
|
+
title: "Controls",
|
|
121
|
+
borders: [:all],
|
|
122
|
+
children: [
|
|
123
|
+
@tui.paragraph(
|
|
124
|
+
text: [
|
|
125
|
+
@tui.text_line(spans: [
|
|
126
|
+
@tui.text_span(content: "LAYOUT", style: @label_style),
|
|
127
|
+
@tui.text_span(content: " "),
|
|
128
|
+
@tui.text_span(content: "←", style: @hotkey_style),
|
|
129
|
+
@tui.text_span(content: ": Shrink sidebar "),
|
|
130
|
+
@tui.text_span(content: "→", style: @hotkey_style),
|
|
131
|
+
@tui.text_span(content: ": Expand sidebar "),
|
|
132
|
+
@tui.text_span(content: "(width: #{@sidebar_width})"),
|
|
133
|
+
]),
|
|
134
|
+
@tui.text_line(spans: [
|
|
135
|
+
@tui.text_span(content: "NAVIGATION", style: @label_style),
|
|
136
|
+
@tui.text_span(content: " "),
|
|
137
|
+
@tui.text_span(content: "↑↓", style: @hotkey_style),
|
|
138
|
+
@tui.text_span(content: ": Select menu item "),
|
|
139
|
+
@tui.text_span(content: "q", style: @hotkey_style),
|
|
140
|
+
@tui.text_span(content: ": Quit"),
|
|
141
|
+
]),
|
|
142
|
+
"",
|
|
143
|
+
@tui.text_line(spans: [
|
|
144
|
+
@tui.text_span(content: "HIT TESTING", style: @label_style),
|
|
145
|
+
@tui.text_span(content: " Click any panel → Rect#contains?(x, y) determines which rect was hit."),
|
|
146
|
+
]),
|
|
147
|
+
]
|
|
148
|
+
),
|
|
149
|
+
]
|
|
150
|
+
)
|
|
151
|
+
frame.render_widget(controls, @controls_rect)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private def handle_input
|
|
155
|
+
case @tui.poll_event
|
|
156
|
+
in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
|
|
157
|
+
:quit
|
|
158
|
+
in type: :key, code: "left"
|
|
159
|
+
@sidebar_width = [@sidebar_width - 2, 10].max
|
|
160
|
+
@last_action = "Layout changed: sidebar_width=#{@sidebar_width}"
|
|
161
|
+
nil
|
|
162
|
+
in type: :key, code: "right"
|
|
163
|
+
@sidebar_width = [@sidebar_width + 2, 40].min
|
|
164
|
+
@last_action = "Layout changed: sidebar_width=#{@sidebar_width}"
|
|
165
|
+
nil
|
|
166
|
+
in type: :key, code: "up"
|
|
167
|
+
@selected_index = (@selected_index - 1) % MENU_ITEMS.size
|
|
168
|
+
@last_action = "Selected: #{MENU_ITEMS[@selected_index]}"
|
|
169
|
+
nil
|
|
170
|
+
in type: :key, code: "down"
|
|
171
|
+
@selected_index = (@selected_index + 1) % MENU_ITEMS.size
|
|
172
|
+
@last_action = "Selected: #{MENU_ITEMS[@selected_index]}"
|
|
173
|
+
nil
|
|
174
|
+
in type: :mouse, kind: "down", x: click_x, y: click_y
|
|
175
|
+
handle_click(click_x, click_y)
|
|
176
|
+
nil
|
|
177
|
+
else
|
|
178
|
+
nil
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private def handle_click(x, y)
|
|
183
|
+
@click_count += 1
|
|
184
|
+
|
|
185
|
+
if @sidebar_rect&.contains?(x, y)
|
|
186
|
+
relative_y = y - @sidebar_rect.y - 1
|
|
187
|
+
if relative_y >= 0 && relative_y < MENU_ITEMS.size
|
|
188
|
+
old_item = MENU_ITEMS[@selected_index]
|
|
189
|
+
@selected_index = relative_y
|
|
190
|
+
new_item = MENU_ITEMS[@selected_index]
|
|
191
|
+
@last_action = "sidebar.contains?(#{x},#{y})=true → #{old_item}→#{new_item}"
|
|
192
|
+
else
|
|
193
|
+
@last_action = "sidebar.contains?(#{x},#{y})=true (empty area)"
|
|
194
|
+
end
|
|
195
|
+
elsif @content_rect&.contains?(x, y)
|
|
196
|
+
@last_action = "content.contains?(#{x},#{y})=true"
|
|
197
|
+
elsif @controls_rect&.contains?(x, y)
|
|
198
|
+
@last_action = "controls.contains?(#{x},#{y})=true"
|
|
199
|
+
else
|
|
200
|
+
@last_action = "No rect contains (#{x},#{y})"
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
WidgetRect.new.run if __FILE__ == $0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Render (Custom Widget) Example
|
|
7
|
+
|
|
8
|
+
Demonstrates how to build Custom Widgets using absolute coordinates.
|
|
9
|
+
|
|
10
|
+
Sometimes standard widgets aren't enough. You need to draw custom shapes, games, or graphs. This example shows how to implement the `render(area)` contract to draw anything you want while respecting layout boundaries.
|
|
11
|
+
|
|
12
|
+
## Features Demonstrated
|
|
13
|
+
|
|
14
|
+
- **Custom Widget Contract**: Implementing a class with `render(area)`.
|
|
15
|
+
- **Coordinate Offsets**: Creating drawing logic that works regardless of where the widget is placed on screen (using `area.x + offset`).
|
|
16
|
+
- **Composability**: Wrapping custom widgets in standard `Block`s with borders.
|
|
17
|
+
|
|
18
|
+
## Hotkeys
|
|
19
|
+
|
|
20
|
+
- **n**: Next Widget (Diagonal -> Checkerboard -> Border)
|
|
21
|
+
- **p**: Previous Widget
|
|
22
|
+
- **q**: Quit
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ruby examples/widget_render/app.rb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Learning Outcomes
|
|
31
|
+
|
|
32
|
+
Use this example if you need to...
|
|
33
|
+
- Build a game (Snake, Tetris) inside the terminal.
|
|
34
|
+
- Create a specialized visualization (Network topology graph).
|
|
35
|
+
- Draw custom UI elements not provided by the library.
|
|
36
|
+
|
|
37
|
+

|
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
# Custom widget that draws a diagonal line.
|
|
10
|
+
#
|
|
11
|
+
# Demonstrates absolute coordinate rendering respecting the given area bounds.
|
|
12
|
+
# This pattern is essential when custom widgets need to coexist with bordered blocks.
|
|
13
|
+
class DiagonalWidget
|
|
14
|
+
def render(area)
|
|
15
|
+
# Draw a diagonal line within the area's bounds.
|
|
16
|
+
# The area parameter respects parent block borders and padding automatically.
|
|
17
|
+
(0..10).filter_map do |i|
|
|
18
|
+
next if i >= area.width || i >= area.height
|
|
19
|
+
|
|
20
|
+
RatatuiRuby::Draw.string(
|
|
21
|
+
area.x + i,
|
|
22
|
+
area.y + i,
|
|
23
|
+
"\\",
|
|
24
|
+
RatatuiRuby::Style.new(fg: :red, modifiers: [:bold])
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Custom widget that draws a checkerboard pattern.
|
|
31
|
+
#
|
|
32
|
+
# This pattern shows using the area's x, y offset correctly when rendering
|
|
33
|
+
# absolute coordinates. The area parameter may have x, y > 0 when rendered
|
|
34
|
+
# inside a positioned block. Always use area.x and area.y as offsets.
|
|
35
|
+
class CheckerboardWidget
|
|
36
|
+
def initialize(char = "□")
|
|
37
|
+
@char = char
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def render(area)
|
|
41
|
+
result = []
|
|
42
|
+
(0...area.height).each do |row| # rubocop:disable Lint/AmbiguousRange
|
|
43
|
+
(0...area.width).each do |col| # rubocop:disable Lint/AmbiguousRange
|
|
44
|
+
next if (row + col).even?
|
|
45
|
+
|
|
46
|
+
result << RatatuiRuby::Draw.string(
|
|
47
|
+
area.x + col,
|
|
48
|
+
area.y + row,
|
|
49
|
+
@char,
|
|
50
|
+
RatatuiRuby::Style.new(fg: :cyan)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Custom widget that draws a border inside the area.
|
|
59
|
+
#
|
|
60
|
+
# Demonstrates that custom widgets can compose complex shapes using the area's bounds.
|
|
61
|
+
# Here we draw a complete box (corners and edges) that fits within the area,
|
|
62
|
+
# respecting width and height constraints automatically.
|
|
63
|
+
class BorderWidget
|
|
64
|
+
def render(area)
|
|
65
|
+
result = []
|
|
66
|
+
style = RatatuiRuby::Style.new(fg: :green)
|
|
67
|
+
|
|
68
|
+
# Top and bottom
|
|
69
|
+
(0...area.width).each do |x| # rubocop:disable Lint/AmbiguousRange
|
|
70
|
+
result << RatatuiRuby::Draw.string(area.x + x, area.y, "─", style)
|
|
71
|
+
result << RatatuiRuby::Draw.string(area.x + x, area.y + area.height - 1, "─", style)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Left and right
|
|
75
|
+
(0...area.height).each do |y| # rubocop:disable Lint/AmbiguousRange
|
|
76
|
+
result << RatatuiRuby::Draw.string(area.x, area.y + y, "│", style)
|
|
77
|
+
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y + y, "│", style)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Corners
|
|
81
|
+
result << RatatuiRuby::Draw.string(area.x, area.y, "┌", style)
|
|
82
|
+
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y, "┐", style)
|
|
83
|
+
result << RatatuiRuby::Draw.string(area.x, area.y + area.height - 1, "└", style)
|
|
84
|
+
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y + area.height - 1, "┘", style)
|
|
85
|
+
|
|
86
|
+
result
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class WidgetRender
|
|
91
|
+
def initialize
|
|
92
|
+
@widget_index = 0
|
|
93
|
+
@widgets = [
|
|
94
|
+
{ name: "Diagonal", widget: DiagonalWidget.new },
|
|
95
|
+
{ name: "Checkerboard", widget: CheckerboardWidget.new("□") },
|
|
96
|
+
{ name: "Border", widget: BorderWidget.new },
|
|
97
|
+
]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def run
|
|
101
|
+
RatatuiRuby.run do |tui|
|
|
102
|
+
@tui = tui
|
|
103
|
+
loop do
|
|
104
|
+
render
|
|
105
|
+
break if handle_input == :quit
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private def render
|
|
111
|
+
@tui.draw do |frame|
|
|
112
|
+
layout = @tui.layout_split(
|
|
113
|
+
frame.area,
|
|
114
|
+
direction: :vertical,
|
|
115
|
+
constraints: [
|
|
116
|
+
@tui.constraint_fill(1),
|
|
117
|
+
@tui.constraint_length(4),
|
|
118
|
+
]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Render a border block to frame widget area
|
|
122
|
+
current_name = @widgets[@widget_index][:name]
|
|
123
|
+
widget_block = @tui.block(
|
|
124
|
+
title: "Custom Widget: #{current_name}",
|
|
125
|
+
borders: [:all]
|
|
126
|
+
)
|
|
127
|
+
frame.render_widget(widget_block, layout[0])
|
|
128
|
+
|
|
129
|
+
# Calculate the inner area, accounting for the block's 1-character border on all sides.
|
|
130
|
+
# This is the key pattern: compute the available space INSIDE the block before
|
|
131
|
+
# passing it to the custom widget's render method.
|
|
132
|
+
# When the custom widget receives this area, all its absolute coordinates will
|
|
133
|
+
# respect the block's boundaries automatically.
|
|
134
|
+
inner_area = @tui.rect(
|
|
135
|
+
x: layout[0].x + 1,
|
|
136
|
+
y: layout[0].y + 1,
|
|
137
|
+
width: [layout[0].width - 2, 0].max,
|
|
138
|
+
height: [layout[0].height - 2, 0].max
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Render the custom widget inside the bordered area.
|
|
142
|
+
# The widget's render method receives the inner_area and draws within it.
|
|
143
|
+
frame.render_widget(@widgets[@widget_index][:widget], inner_area)
|
|
144
|
+
|
|
145
|
+
# Render control panel with current widget info
|
|
146
|
+
control_lines = [
|
|
147
|
+
@tui.text_line(
|
|
148
|
+
spans: [
|
|
149
|
+
@tui.text_span(content: "n", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
150
|
+
@tui.text_span(content: ": Next "),
|
|
151
|
+
@tui.text_span(content: "p", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
152
|
+
@tui.text_span(content: ": Previous "),
|
|
153
|
+
@tui.text_span(content: "q", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
154
|
+
@tui.text_span(content: ": Quit"),
|
|
155
|
+
]
|
|
156
|
+
),
|
|
157
|
+
]
|
|
158
|
+
controls = @tui.paragraph(
|
|
159
|
+
text: control_lines,
|
|
160
|
+
block: @tui.block(
|
|
161
|
+
title: "Controls",
|
|
162
|
+
borders: [:all]
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
frame.render_widget(controls, layout[1])
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private def handle_input
|
|
170
|
+
event = @tui.poll_event
|
|
171
|
+
case event
|
|
172
|
+
in { type: :key, code: "q" }
|
|
173
|
+
:quit
|
|
174
|
+
in { type: :key, code: "n" }
|
|
175
|
+
@widget_index = (@widget_index + 1) % @widgets.length
|
|
176
|
+
in { type: :key, code: "p" }
|
|
177
|
+
@widget_index = (@widget_index - 1) % @widgets.length
|
|
178
|
+
else
|
|
179
|
+
# Ignore other events
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
WidgetRender.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Rich Text Example
|
|
7
|
+
|
|
8
|
+
Demonstrates styling individual words and characters.
|
|
9
|
+
|
|
10
|
+
Standard strings are monochromatic. "Rich Text" is composed of `Lines` containing multiple `Spans`, where each Span has its own style. This allows for multi-colored, multi-styled text blocks.
|
|
11
|
+
|
|
12
|
+
## Features Demonstrated
|
|
13
|
+
|
|
14
|
+
- **Spans**: Chunks of text with a specific style (e.g., "Bold Red Word").
|
|
15
|
+
- **Lines**: ordered collections of Spans that form a single row of text.
|
|
16
|
+
- **Paragraphs**: Rendering lines of rich text.
|
|
17
|
+
|
|
18
|
+
## Hotkeys
|
|
19
|
+
|
|
20
|
+
- **q**: Quit
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ruby examples/widget_rich_text/app.rb
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Learning Outcomes
|
|
29
|
+
|
|
30
|
+
Use this example if you need to...
|
|
31
|
+
- Highlight keywords in code (Syntax highlighting).
|
|
32
|
+
- Create status lines with icons (e.g., "✔ Success" where the checkmark is green).
|
|
33
|
+
- Emphasize specific data points in a paragraph.
|
|
34
|
+
|
|
35
|
+

|