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,44 @@
|
|
|
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
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
class Key < Event
|
|
9
|
+
# Methods and logic for media keys.
|
|
10
|
+
module Media
|
|
11
|
+
# Returns true if this is a media key.
|
|
12
|
+
#
|
|
13
|
+
# Media keys include: play, pause, stop, track controls, volume controls.
|
|
14
|
+
# These are only available in terminals supporting the Kitty keyboard protocol.
|
|
15
|
+
#
|
|
16
|
+
# event.media? # => true for media_play, media_pause, etc.
|
|
17
|
+
def media?
|
|
18
|
+
@kind == :media
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Handles media-specific DWIM logic for method_missing.
|
|
22
|
+
private def match_media_dwim?(key_name)
|
|
23
|
+
return false unless @kind == :media
|
|
24
|
+
|
|
25
|
+
# Allow unprefixed predicate
|
|
26
|
+
# e.g., pause? returns true for media_pause
|
|
27
|
+
if @code.start_with?("media_")
|
|
28
|
+
base_code = @code.delete_prefix("media_")
|
|
29
|
+
return true if key_name == base_code
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Bidirectional media overlaps
|
|
33
|
+
# e.g., play? and pause? both match media_play_pause
|
|
34
|
+
return true if @code == "media_play_pause" && (key_name == "play" || key_name == "pause")
|
|
35
|
+
|
|
36
|
+
# e.g., play_pause? matches media_play or media_pause
|
|
37
|
+
return true if key_name == "play_pause" && (@code == "media_play" || @code == "media_pause")
|
|
38
|
+
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
class Key < Event
|
|
9
|
+
# Methods and logic for modifier keys.
|
|
10
|
+
module Modifier
|
|
11
|
+
# Returns true if CTRL is held OR if this is a left_control/right_control key event.
|
|
12
|
+
def ctrl?
|
|
13
|
+
@modifiers.include?("ctrl") || @code == "left_control" || @code == "right_control"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Alias for {#ctrl?}.
|
|
17
|
+
alias control? ctrl?
|
|
18
|
+
|
|
19
|
+
# Returns true if ALT is held OR if this is a left_alt/right_alt key event.
|
|
20
|
+
def alt?
|
|
21
|
+
@modifiers.include?("alt") || @code == "left_alt" || @code == "right_alt"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Alias for {#alt?}.
|
|
25
|
+
alias option? alt?
|
|
26
|
+
|
|
27
|
+
# Returns true if SHIFT is held OR if this is a left_shift/right_shift key event.
|
|
28
|
+
def shift?
|
|
29
|
+
@modifiers.include?("shift") || @code == "left_shift" || @code == "right_shift"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns true if SUPER is held OR if this is a left_super/right_super key event.
|
|
33
|
+
# Also responds to platform aliases: win?, command?, cmd?, tux?
|
|
34
|
+
def super?
|
|
35
|
+
@modifiers.include?("super") || @code == "left_super" || @code == "right_super"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Alias for {#super?}.
|
|
39
|
+
alias win? super?
|
|
40
|
+
# Alias for {#super?}.
|
|
41
|
+
alias command? super?
|
|
42
|
+
# Alias for {#super?}.
|
|
43
|
+
alias cmd? super?
|
|
44
|
+
# Alias for {#super?}.
|
|
45
|
+
alias tux? super?
|
|
46
|
+
|
|
47
|
+
# Returns true if HYPER is held OR if this is a left_hyper/right_hyper key event.
|
|
48
|
+
def hyper?
|
|
49
|
+
@modifiers.include?("hyper") || @code == "left_hyper" || @code == "right_hyper"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns true if META is held OR if this is a left_meta/right_meta key event.
|
|
53
|
+
def meta?
|
|
54
|
+
@modifiers.include?("meta") || @code == "left_meta" || @code == "right_meta"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns true if this is a modifier key event.
|
|
58
|
+
#
|
|
59
|
+
# Some applications need to know if an event represents a generic key
|
|
60
|
+
# press or a specific modifier key (like CTRL or ALT) being pressed on
|
|
61
|
+
# its own.
|
|
62
|
+
#
|
|
63
|
+
# This method identifies if the key event itself is a modifier key.
|
|
64
|
+
#
|
|
65
|
+
# === Example
|
|
66
|
+
#
|
|
67
|
+
# if event.modifier?
|
|
68
|
+
# # Handle solo modifier key press
|
|
69
|
+
# end
|
|
70
|
+
def modifier?
|
|
71
|
+
@kind == :modifier
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Handles modifier-specific DWIM logic for method_missing.
|
|
75
|
+
private def match_modifier_dwim?(key_name, key_sym)
|
|
76
|
+
# Platform modifier aliases
|
|
77
|
+
modifier_aliases = {
|
|
78
|
+
win: "super",
|
|
79
|
+
command: "super",
|
|
80
|
+
cmd: "super",
|
|
81
|
+
tux: "super",
|
|
82
|
+
}.freeze
|
|
83
|
+
|
|
84
|
+
target_modifier = modifier_aliases[key_sym]
|
|
85
|
+
if target_modifier
|
|
86
|
+
return true if @modifiers.include?(target_modifier)
|
|
87
|
+
return true if @code == "left_#{target_modifier}" || @code == "right_#{target_modifier}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
false
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
class Key < Event
|
|
9
|
+
# Methods and logic for navigation keys.
|
|
10
|
+
module Navigation
|
|
11
|
+
# Returns true if this is a standard key.
|
|
12
|
+
#
|
|
13
|
+
# Standard keys include: characters, Enter, Tab, arrow keys, navigation keys.
|
|
14
|
+
#
|
|
15
|
+
# event.standard? # => true for "a", "enter", "up", etc.
|
|
16
|
+
def standard?
|
|
17
|
+
@kind == :standard
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Alias for {#standard?}.
|
|
21
|
+
#
|
|
22
|
+
# Provided for semantic clarity when checking if a key has no special category.
|
|
23
|
+
#
|
|
24
|
+
# event.unmodified? # => true for standard keys like "a", "enter", "up"
|
|
25
|
+
alias unmodified? standard?
|
|
26
|
+
|
|
27
|
+
# Handles navigation-specific DWIM logic for method_missing.
|
|
28
|
+
private def match_navigation_dwim?(key_name, key_sym)
|
|
29
|
+
# DWIM: reverse_tab? matches both BackTab key and Shift+Tab combo
|
|
30
|
+
if key_name == "reverse_tab"
|
|
31
|
+
return true if @code == "back_tab"
|
|
32
|
+
return true if @code == "tab" && @modifiers.include?("shift")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# DWIM: Check explicit aliases
|
|
36
|
+
navigation_aliases = {
|
|
37
|
+
return: "enter",
|
|
38
|
+
back: "backspace",
|
|
39
|
+
del: "delete",
|
|
40
|
+
ins: "insert",
|
|
41
|
+
pgup: "page_up",
|
|
42
|
+
pageup: "page_up",
|
|
43
|
+
pgdn: "page_down",
|
|
44
|
+
pagedown: "page_down",
|
|
45
|
+
}.freeze
|
|
46
|
+
|
|
47
|
+
target_code = navigation_aliases[key_sym]
|
|
48
|
+
return true if target_code && @code == target_code && @modifiers.empty?
|
|
49
|
+
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
class Key < Event
|
|
9
|
+
# Methods and logic for system and function keys.
|
|
10
|
+
module System
|
|
11
|
+
# Returns true if this is a system key.
|
|
12
|
+
#
|
|
13
|
+
# System keys include: Esc, CapsLock, ScrollLock, NumLock, PrintScreen, Pause, Menu, KeypadBegin.
|
|
14
|
+
#
|
|
15
|
+
# event.system? # => true for pause, esc, caps_lock, etc.
|
|
16
|
+
def system?
|
|
17
|
+
@kind == :system
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns true if this is a function key (F1-F24).
|
|
21
|
+
#
|
|
22
|
+
# event.function? # => true for f1, f2, ..., f24
|
|
23
|
+
def function?
|
|
24
|
+
@kind == :function
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Handles system-specific DWIM logic for method_missing.
|
|
28
|
+
private def match_system_dwim?(key_name, key_sym)
|
|
29
|
+
system_aliases = {
|
|
30
|
+
scrlk: "scroll_lock",
|
|
31
|
+
scroll: "scroll_lock",
|
|
32
|
+
prtsc: "print_screen",
|
|
33
|
+
print: "print_screen",
|
|
34
|
+
escape: "esc",
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
target_code = system_aliases[key_sym]
|
|
38
|
+
return true if target_code && @code == target_code && @modifiers.empty?
|
|
39
|
+
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
4
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
5
|
|
|
6
|
+
require_relative "key/character"
|
|
7
|
+
require_relative "key/media"
|
|
8
|
+
require_relative "key/modifier"
|
|
9
|
+
require_relative "key/navigation"
|
|
10
|
+
require_relative "key/system"
|
|
11
|
+
|
|
6
12
|
module RatatuiRuby
|
|
7
13
|
class Event
|
|
8
14
|
# Captures a keyboard interaction.
|
|
@@ -33,7 +39,34 @@ module RatatuiRuby
|
|
|
33
39
|
# in type: :key, code: "c", modifiers: ["ctrl"]
|
|
34
40
|
# exit
|
|
35
41
|
# end
|
|
42
|
+
#
|
|
43
|
+
# === Terminal Compatibility
|
|
44
|
+
#
|
|
45
|
+
# Some key combinations never reach your application. Terminal emulators intercept them for
|
|
46
|
+
# built-in features like tab switching. Common culprits:
|
|
47
|
+
#
|
|
48
|
+
# * Ctrl+PageUp/PageDown (tab switching in Terminal.app, iTerm2)
|
|
49
|
+
# * Ctrl+Tab (tab switching)
|
|
50
|
+
# * Cmd+key combinations (macOS system shortcuts)
|
|
51
|
+
#
|
|
52
|
+
# If modifiers appear missing, test with a different terminal. Kitty, WezTerm, and Alacritty
|
|
53
|
+
# pass more keys through. See <tt>doc/terminal_limitations.md</tt> for details.
|
|
54
|
+
#
|
|
55
|
+
# === Enhanced Keys (Kitty Protocol)
|
|
56
|
+
#
|
|
57
|
+
# Terminals supporting the Kitty keyboard protocol report additional keys:
|
|
58
|
+
#
|
|
59
|
+
# * Media keys: <tt>:play</tt>, <tt>:play_pause</tt>, <tt>:track_next</tt>, <tt>:mute_volume</tt>
|
|
60
|
+
# * Individual modifiers: <tt>:left_shift</tt>, <tt>:right_control</tt>, <tt>:left_super</tt>
|
|
61
|
+
#
|
|
62
|
+
# These keys will not work in Terminal.app, iTerm2, or GNOME Terminal.
|
|
36
63
|
class Key < Event
|
|
64
|
+
include Character
|
|
65
|
+
include Media
|
|
66
|
+
include Modifier
|
|
67
|
+
include Navigation
|
|
68
|
+
include System
|
|
69
|
+
|
|
37
70
|
# The key code (e.g., <tt>"a"</tt>, <tt>"enter"</tt>, <tt>"up"</tt>).
|
|
38
71
|
#
|
|
39
72
|
# puts event.code # => "enter"
|
|
@@ -44,6 +77,15 @@ module RatatuiRuby
|
|
|
44
77
|
# puts event.modifiers # => ["ctrl", "shift"]
|
|
45
78
|
attr_reader :modifiers
|
|
46
79
|
|
|
80
|
+
# The category of the key.
|
|
81
|
+
#
|
|
82
|
+
# One of: <tt>:standard</tt>, <tt>:function</tt>, <tt>:media</tt>, <tt>:modifier</tt>, <tt>:system</tt>.
|
|
83
|
+
#
|
|
84
|
+
# This allows grouping keys by their logical type without parsing the code string.
|
|
85
|
+
#
|
|
86
|
+
# event.kind # => :media
|
|
87
|
+
attr_reader :kind
|
|
88
|
+
|
|
47
89
|
# Returns true for Key events.
|
|
48
90
|
#
|
|
49
91
|
# event.key? # => true
|
|
@@ -59,17 +101,21 @@ module RatatuiRuby
|
|
|
59
101
|
# The key code (String).
|
|
60
102
|
# [modifiers]
|
|
61
103
|
# List of modifiers (Array<String>).
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
104
|
+
# [kind]
|
|
105
|
+
# The key category (Symbol). One of: <tt>:standard</tt>, <tt>:function</tt>,
|
|
106
|
+
# <tt>:media</tt>, <tt>:modifier</tt>, <tt>:system</tt>. Defaults to <tt>:standard</tt>.
|
|
107
|
+
def initialize(code:, modifiers: [], kind: :standard)
|
|
108
|
+
@code = code.freeze
|
|
109
|
+
@modifiers = modifiers.map(&:freeze).sort.freeze
|
|
110
|
+
@kind = kind
|
|
65
111
|
end
|
|
66
112
|
|
|
67
113
|
# Compares the event with another object.
|
|
68
114
|
#
|
|
69
115
|
# - If +other+ is a +Symbol+, compares against #to_sym.
|
|
70
116
|
# - If +other+ is a +String+, compares against #to_s.
|
|
71
|
-
# -
|
|
72
|
-
# - Otherwise, compares
|
|
117
|
+
# - If +other+ is a +Key+, compares as a value object.
|
|
118
|
+
# - Otherwise, compares using standard equality.
|
|
73
119
|
def ==(other)
|
|
74
120
|
case other
|
|
75
121
|
when Symbol
|
|
@@ -91,9 +137,25 @@ module RatatuiRuby
|
|
|
91
137
|
# === Supported Keys
|
|
92
138
|
#
|
|
93
139
|
# [Standard]
|
|
94
|
-
# <tt>:enter</tt>, <tt>:backspace</tt>, <tt>:tab</tt>, <tt>:
|
|
140
|
+
# <tt>:enter</tt>, <tt>:backspace</tt>, <tt>:tab</tt>, <tt>:back_tab</tt>, <tt>:esc</tt>, <tt>:null</tt>
|
|
95
141
|
# [Navigation]
|
|
96
|
-
# <tt>:up</tt>, <tt>:down</tt>, <tt>:left</tt>, <tt>:right</tt
|
|
142
|
+
# <tt>:up</tt>, <tt>:down</tt>, <tt>:left</tt>, <tt>:right</tt>, <tt>:home</tt>, <tt>:end</tt>,
|
|
143
|
+
# <tt>:page_up</tt>, <tt>:page_down</tt>, <tt>:insert</tt>, <tt>:delete</tt>
|
|
144
|
+
# [Function Keys]
|
|
145
|
+
# <tt>:f1</tt> through <tt>:f12</tt> (and beyond, e.g. <tt>:f24</tt>)
|
|
146
|
+
# [Lock Keys]
|
|
147
|
+
# <tt>:caps_lock</tt>, <tt>:scroll_lock</tt>, <tt>:num_lock</tt>
|
|
148
|
+
# [System Keys]
|
|
149
|
+
# <tt>:print_screen</tt>, <tt>:pause</tt>, <tt>:menu</tt>, <tt>:keypad_begin</tt>
|
|
150
|
+
# [Media Keys]
|
|
151
|
+
# <tt>:play</tt>, <tt>:media_pause</tt>, <tt>:play_pause</tt>, <tt>:reverse</tt>, <tt>:stop</tt>,
|
|
152
|
+
# <tt>:fast_forward</tt>, <tt>:rewind</tt>, <tt>:track_next</tt>, <tt>:track_previous</tt>,
|
|
153
|
+
# <tt>:record</tt>, <tt>:lower_volume</tt>, <tt>:raise_volume</tt>, <tt>:mute_volume</tt>
|
|
154
|
+
# [Modifier Keys]
|
|
155
|
+
# <tt>:left_shift</tt>, <tt>:left_control</tt>, <tt>:left_alt</tt>, <tt>:left_super</tt>,
|
|
156
|
+
# <tt>:left_hyper</tt>, <tt>:left_meta</tt>, <tt>:right_shift</tt>, <tt>:right_control</tt>,
|
|
157
|
+
# <tt>:right_alt</tt>, <tt>:right_super</tt>, <tt>:right_hyper</tt>, <tt>:right_meta</tt>,
|
|
158
|
+
# <tt>:iso_level3_shift</tt>, <tt>:iso_level5_shift</tt>
|
|
97
159
|
# [Characters]
|
|
98
160
|
# <tt>:a</tt>, <tt>:b</tt>, <tt>:1</tt>, <tt>:space</tt>, etc.
|
|
99
161
|
#
|
|
@@ -108,7 +170,7 @@ module RatatuiRuby
|
|
|
108
170
|
if mods.empty?
|
|
109
171
|
@code.to_sym
|
|
110
172
|
else
|
|
111
|
-
"#{mods}_#{@code}"
|
|
173
|
+
:"#{mods}_#{@code}"
|
|
112
174
|
end
|
|
113
175
|
end
|
|
114
176
|
|
|
@@ -131,46 +193,7 @@ module RatatuiRuby
|
|
|
131
193
|
|
|
132
194
|
# Returns inspection string.
|
|
133
195
|
def inspect
|
|
134
|
-
"#<#{self.class} code=#{@code.inspect} modifiers=#{@modifiers.inspect}>"
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Returns true if CTRL is held.
|
|
138
|
-
def ctrl?
|
|
139
|
-
@modifiers.include?("ctrl")
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Returns true if ALT is held.
|
|
143
|
-
def alt?
|
|
144
|
-
@modifiers.include?("alt")
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# Returns true if SHIFT is held.
|
|
148
|
-
def shift?
|
|
149
|
-
@modifiers.include?("shift")
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Returns true if the key represents a single printable character.
|
|
153
|
-
#
|
|
154
|
-
# RatatuiRuby::Event::Key.new(code: "a").text? # => true
|
|
155
|
-
# RatatuiRuby::Event::Key.new(code: "enter").text? # => false
|
|
156
|
-
# RatatuiRuby::Event::Key.new(code: "space").text? # => false ("space" is not 1 char, " " is)
|
|
157
|
-
def text?
|
|
158
|
-
@code.length == 1
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Returns the key as a printable character (if applicable).
|
|
162
|
-
#
|
|
163
|
-
# [Printable Characters]
|
|
164
|
-
# Returns the character itself (e.g., <tt>"a"</tt>, <tt>"1"</tt>, <tt>" "</tt>).
|
|
165
|
-
# [Special Keys]
|
|
166
|
-
# Returns an empty string (e.g., <tt>"enter"</tt>, <tt>"up"</tt>, <tt>"f1"</tt>).
|
|
167
|
-
#
|
|
168
|
-
# This is equivalent to +to_s+.
|
|
169
|
-
#
|
|
170
|
-
# RatatuiRuby::Event::Key.new(code: "a").char # => "a"
|
|
171
|
-
# RatatuiRuby::Event::Key.new(code: "enter").char # => ""
|
|
172
|
-
def char
|
|
173
|
-
to_s
|
|
196
|
+
"#<#{self.class} code=#{@code.inspect} modifiers=#{@modifiers.inspect} kind=#{@kind.inspect}>"
|
|
174
197
|
end
|
|
175
198
|
|
|
176
199
|
# Supports dynamic key predicate methods via method_missing.
|
|
@@ -184,12 +207,47 @@ module RatatuiRuby
|
|
|
184
207
|
#
|
|
185
208
|
# The method name is converted to a symbol and compared against the event.
|
|
186
209
|
# This works for any key code or modifier+key combination.
|
|
210
|
+
#
|
|
211
|
+
# === Smart Predicates (DWIM)
|
|
212
|
+
#
|
|
213
|
+
# For convenience, generic predicates match both system and media variants:
|
|
214
|
+
#
|
|
215
|
+
# event.pause? # => true for BOTH system "pause" AND "media_pause"
|
|
216
|
+
# event.play? # => true for "media_play"
|
|
217
|
+
# event.stop? # => true for "media_stop"
|
|
218
|
+
#
|
|
219
|
+
# This "Do What I Mean" behavior reduces boilerplate when you just want to
|
|
220
|
+
# respond to a conceptual action (e.g., "pause the playback") regardless of
|
|
221
|
+
# whether the user pressed a keyboard key or a media button.
|
|
222
|
+
#
|
|
223
|
+
# For strict matching, use the full predicate or compare the code directly:
|
|
224
|
+
#
|
|
225
|
+
# event.media_pause? # => true ONLY for media pause
|
|
226
|
+
# event.code == "pause" # => true ONLY for system pause
|
|
187
227
|
def method_missing(name, *args, &block)
|
|
188
228
|
if name.to_s.end_with?("?")
|
|
189
|
-
|
|
190
|
-
|
|
229
|
+
key_name = name.to_s[0...-1]
|
|
230
|
+
key_sym = key_name.to_sym
|
|
231
|
+
|
|
232
|
+
# Fast path: Exact match (e.g., media_pause? for media_pause)
|
|
233
|
+
return true if self == key_sym
|
|
234
|
+
|
|
235
|
+
# Delegate category-specific DWIM logic to mixins
|
|
236
|
+
return true if match_media_dwim?(key_name)
|
|
237
|
+
return true if match_modifier_dwim?(key_name, key_sym)
|
|
238
|
+
return true if match_navigation_dwim?(key_name, key_sym)
|
|
239
|
+
return true if match_system_dwim?(key_name, key_sym)
|
|
240
|
+
|
|
241
|
+
# DWIM: Universal underscore-insensitivity
|
|
242
|
+
# Normalize both predicate and code by stripping underscores
|
|
243
|
+
normalized_predicate = key_name.delete("_")
|
|
244
|
+
normalized_code = @code.delete("_")
|
|
245
|
+
return true if normalized_predicate == normalized_code && @modifiers.empty?
|
|
246
|
+
|
|
247
|
+
false
|
|
248
|
+
else
|
|
249
|
+
super
|
|
191
250
|
end
|
|
192
|
-
super
|
|
193
251
|
end
|
|
194
252
|
|
|
195
253
|
# Declares that this class responds to dynamic predicate methods.
|
|
@@ -202,9 +260,11 @@ module RatatuiRuby
|
|
|
202
260
|
# case event
|
|
203
261
|
# in type: :key, code: "c", modifiers: ["ctrl"]
|
|
204
262
|
# puts "Ctrl+C pressed"
|
|
263
|
+
# in type: :key, kind: :media
|
|
264
|
+
# puts "Media key pressed"
|
|
205
265
|
# end
|
|
206
266
|
def deconstruct_keys(keys)
|
|
207
|
-
{ type: :key, code: @code, modifiers: @modifiers }
|
|
267
|
+
{ type: :key, code: @code, modifiers: @modifiers, kind: @kind }
|
|
208
268
|
end
|
|
209
269
|
end
|
|
210
270
|
end
|
|
@@ -67,11 +67,11 @@ module RatatuiRuby
|
|
|
67
67
|
# [modifiers]
|
|
68
68
|
# List of modifiers (Array<String>).
|
|
69
69
|
def initialize(kind:, x:, y:, button:, modifiers: [])
|
|
70
|
-
@kind = kind
|
|
70
|
+
@kind = kind.freeze
|
|
71
71
|
@x = x
|
|
72
72
|
@y = y
|
|
73
|
-
@button = button || "none"
|
|
74
|
-
@modifiers = modifiers.sort
|
|
73
|
+
@button = (button || "none").freeze
|
|
74
|
+
@modifiers = modifiers.map(&:freeze).sort.freeze
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Returns true if mouse button was pressed down.
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# {Null object}[https://en.wikipedia.org/wiki/Null_object_pattern] for absent events.
|
|
9
|
+
#
|
|
10
|
+
# Event loops poll for input 60 times per second. Usually nothing is happening.
|
|
11
|
+
# If <tt>RatatuiRuby.poll_event</tt> returned <tt>nil</tt>, you would need
|
|
12
|
+
# nil-checks: <tt>event&.key?</tt>, <tt>next unless event</tt>.
|
|
13
|
+
#
|
|
14
|
+
# This class eliminates that friction. It responds to every predicate with
|
|
15
|
+
# <tt>false</tt>. Call <tt>none?</tt> to detect it explicitly. Pattern-match on
|
|
16
|
+
# <tt>type: :none</tt> for exhaustive dispatch.
|
|
17
|
+
#
|
|
18
|
+
# Use it to simplify your event loop. No guards. Optional `else` clauses.
|
|
19
|
+
#
|
|
20
|
+
# See Martin Fowler's {Special Case}[https://martinfowler.com/eaaCatalog/specialCase.html] pattern.
|
|
21
|
+
#
|
|
22
|
+
# === Predicate Example
|
|
23
|
+
#
|
|
24
|
+
# event = RatatuiRuby.poll_event
|
|
25
|
+
# break if event.ctrl_c?
|
|
26
|
+
# redraw if event.none?
|
|
27
|
+
#
|
|
28
|
+
# === Pattern Matching Example
|
|
29
|
+
#
|
|
30
|
+
# redraw if RatatuiRuby.poll_event in type: :none
|
|
31
|
+
class None < Event
|
|
32
|
+
# Returns true for None events.
|
|
33
|
+
def none?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Deconstructs the event for pattern matching.
|
|
38
|
+
def deconstruct_keys(keys)
|
|
39
|
+
{ type: :none }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/ratatui_ruby/event.rb
CHANGED
|
@@ -6,11 +6,63 @@
|
|
|
6
6
|
module RatatuiRuby
|
|
7
7
|
# Base class for all RatatuiRuby events.
|
|
8
8
|
#
|
|
9
|
-
# Events
|
|
10
|
-
# All events support Ruby 3.0+ pattern matching
|
|
9
|
+
# Events represent terminal input: keyboard, mouse, resize, paste, focus changes.
|
|
10
|
+
# Returned by RatatuiRuby.poll_event. All events support Ruby 3.0+ pattern matching.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
12
|
+
# == Event Types
|
|
13
|
+
#
|
|
14
|
+
# * <tt>Key</tt> — keyboard input
|
|
15
|
+
# * <tt>Mouse</tt> — mouse clicks, movement, wheel
|
|
16
|
+
# * <tt>Resize</tt> — terminal resized
|
|
17
|
+
# * <tt>Paste</tt> — clipboard paste
|
|
18
|
+
# * <tt>FocusGained</tt> — terminal gained focus
|
|
19
|
+
# * <tt>FocusLost</tt> — terminal lost focus
|
|
20
|
+
# * <tt>None</tt> — no event available (Null Object)
|
|
21
|
+
#
|
|
22
|
+
# == Pattern Matching (Exhaustive)
|
|
23
|
+
#
|
|
24
|
+
# Use <tt>case...in</tt> to dispatch on every possible event type. This ensures
|
|
25
|
+
# you handle every case without needing an +else+ clause:
|
|
26
|
+
#
|
|
27
|
+
# case RatatuiRuby.poll_event
|
|
28
|
+
# in { type: :key, code: "q" }
|
|
29
|
+
# break
|
|
30
|
+
# in { type: :key, code: code, modifiers: }
|
|
31
|
+
# handle_key(code, modifiers)
|
|
32
|
+
# in { type: :mouse, kind: "down", x:, y: }
|
|
33
|
+
# handle_click(x, y)
|
|
34
|
+
# in { type: :mouse, kind:, x:, y: }
|
|
35
|
+
# # handle other mouse activities
|
|
36
|
+
# in { type: :resize, width:, height: }
|
|
37
|
+
# handle_resize(width, height)
|
|
38
|
+
# in { type: :paste, content: }
|
|
39
|
+
# handle_paste(content)
|
|
40
|
+
# in { type: :focus_gained }
|
|
41
|
+
# handle_focus_gain
|
|
42
|
+
# in { type: :focus_lost }
|
|
43
|
+
# handle_focus_loss
|
|
44
|
+
# in { type: :none }
|
|
45
|
+
# # Idle
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# == Predicates
|
|
49
|
+
#
|
|
50
|
+
# Check event types with predicates without pattern matching:
|
|
51
|
+
#
|
|
52
|
+
# event = RatatuiRuby.poll_event
|
|
53
|
+
# if event.key?
|
|
54
|
+
# puts "Key pressed"
|
|
55
|
+
# elsif event.none?
|
|
56
|
+
# # Idle
|
|
57
|
+
# elsif event.mouse?
|
|
58
|
+
# puts "Mouse event"
|
|
59
|
+
# end
|
|
13
60
|
class Event
|
|
61
|
+
# Returns true if this is a None event.
|
|
62
|
+
def none?
|
|
63
|
+
false
|
|
64
|
+
end
|
|
65
|
+
|
|
14
66
|
# Returns true if this is a Key event.
|
|
15
67
|
def key?
|
|
16
68
|
false
|
|
@@ -67,10 +119,10 @@ module RatatuiRuby
|
|
|
67
119
|
def deconstruct_keys(keys)
|
|
68
120
|
{}
|
|
69
121
|
end
|
|
70
|
-
|
|
71
122
|
end
|
|
72
123
|
end
|
|
73
124
|
|
|
125
|
+
require_relative "event/none"
|
|
74
126
|
require_relative "event/key"
|
|
75
127
|
require_relative "event/mouse"
|
|
76
128
|
require_relative "event/resize"
|