ratatui_ruby 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +87 -171
- data/CHANGELOG.md +38 -1
- data/README.md +8 -3
- data/REUSE.toml +20 -0
- data/doc/application_architecture.md +105 -45
- data/doc/application_testing.md +5 -3
- data/doc/contributors/design/ruby_frontend.md +9 -5
- data/doc/contributors/developing_examples.md +76 -18
- data/doc/contributors/documentation_style.md +7 -0
- data/doc/contributors/index.md +2 -0
- data/doc/event_handling.md +10 -4
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.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_padding.png +0 -0
- data/doc/images/widget_block_titles.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_cell_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_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_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_flex.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/interactive_design.md +25 -30
- data/doc/quickstart.md +147 -120
- data/examples/app_all_events/README.md +81 -0
- data/examples/app_all_events/app.rb +93 -0
- data/examples/app_all_events/model/event_color_cycle.rb +41 -0
- data/examples/app_all_events/model/event_entry.rb +75 -0
- data/examples/app_all_events/model/events.rb +180 -0
- data/examples/app_all_events/model/highlight.rb +57 -0
- data/examples/app_all_events/model/timestamp.rb +54 -0
- data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
- data/examples/app_all_events/view/app_view.rb +78 -0
- data/examples/app_all_events/view/controls_view.rb +50 -0
- data/examples/app_all_events/view/counts_view.rb +55 -0
- data/examples/app_all_events/view/live_view.rb +69 -0
- data/examples/app_all_events/view/log_view.rb +60 -0
- data/examples/app_all_events/view.rb +7 -0
- data/examples/app_all_events/view_state.rb +42 -0
- data/examples/app_color_picker/README.md +94 -0
- data/examples/app_color_picker/app.rb +112 -0
- data/examples/app_color_picker/clipboard.rb +84 -0
- data/examples/app_color_picker/color.rb +191 -0
- data/examples/app_color_picker/copy_dialog.rb +170 -0
- data/examples/app_color_picker/harmony.rb +56 -0
- data/examples/app_color_picker/input.rb +142 -0
- data/examples/app_color_picker/palette.rb +80 -0
- data/examples/app_color_picker/scene.rb +201 -0
- data/examples/{login_form → app_login_form}/app.rb +39 -42
- data/examples/{map_demo → app_map_demo}/app.rb +24 -21
- data/examples/{table_select → app_table_select}/app.rb +68 -65
- data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
- data/examples/verify_quickstart_layout/app.rb +69 -0
- data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
- data/examples/verify_readme_usage/app.rb +34 -0
- data/examples/widget_barchart_demo/app.rb +238 -0
- data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
- data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
- data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
- data/examples/widget_calendar_demo/app.rb +109 -0
- data/examples/widget_cell_demo/app.rb +104 -0
- data/examples/widget_chart_demo/app.rb +213 -0
- data/examples/widget_gauge_demo/app.rb +212 -0
- data/examples/widget_layout_split/app.rb +246 -0
- data/examples/widget_line_gauge_demo/app.rb +217 -0
- data/examples/widget_list_demo/app.rb +382 -0
- data/examples/widget_list_styles/app.rb +141 -0
- data/examples/widget_popup_demo/app.rb +104 -0
- data/examples/widget_ratatui_logo_demo/app.rb +103 -0
- data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
- data/examples/widget_rect/app.rb +205 -0
- data/examples/widget_render/app.rb +184 -0
- data/examples/widget_rich_text/app.rb +137 -0
- data/examples/widget_scroll_text/app.rb +108 -0
- data/examples/widget_scrollbar_demo/app.rb +153 -0
- data/examples/widget_sparkline_demo/app.rb +274 -0
- data/examples/widget_style_colors/app.rb +19 -21
- data/examples/widget_table_flex/app.rb +95 -0
- data/examples/widget_tabs_demo/app.rb +167 -0
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/src/events.rs +121 -36
- data/ext/ratatui_ruby/src/frame.rs +115 -0
- data/ext/ratatui_ruby/src/lib.rs +79 -26
- data/ext/ratatui_ruby/src/rendering.rs +8 -4
- data/ext/ratatui_ruby/src/style.rs +138 -57
- data/ext/ratatui_ruby/src/terminal.rs +5 -9
- data/ext/ratatui_ruby/src/text.rs +13 -6
- data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -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/layout.rs +9 -4
- data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
- 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 +4 -2
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
- data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
- data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
- data/lib/ratatui_ruby/cell.rb +3 -3
- data/lib/ratatui_ruby/event/key.rb +1 -1
- data/lib/ratatui_ruby/event/none.rb +43 -0
- data/lib/ratatui_ruby/event.rb +56 -4
- data/lib/ratatui_ruby/frame.rb +87 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
- 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 +25 -25
- data/lib/ratatui_ruby/schema/gauge.rb +53 -53
- 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 +86 -84
- 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 +60 -60
- 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 +46 -46
- data/lib/ratatui_ruby/schema/table.rb +112 -119
- data/lib/ratatui_ruby/schema/tabs.rb +66 -67
- data/lib/ratatui_ruby/session/autodoc.rb +417 -0
- data/lib/ratatui_ruby/session.rb +40 -23
- data/lib/ratatui_ruby/test_helper.rb +185 -19
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +65 -39
- 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_map_demo/app.rbs +11 -0
- data/sig/examples/app_table_select/app.rbs +11 -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_padding/app.rbs +11 -0
- data/sig/examples/widget_block_titles/app.rbs +11 -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_list_styles/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_flex/app.rbs +11 -0
- data/sig/ratatui_ruby/frame.rbs +9 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
- data/sig/ratatui_ruby/schema/draw.rbs +4 -0
- data/sig/ratatui_ruby/schema/layout.rbs +1 -1
- data/sig/ratatui_ruby/session.rbs +94 -0
- data/tasks/autodoc/inventory.rb +61 -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 +47 -0
- data/tasks/bump/history.rb +2 -2
- 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 +4 -4
- data/tasks/terminal_preview/app_screenshot.rb +1 -3
- 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 +7 -9
- 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 +156 -125
- 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/readme_usage.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/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.rbs +0 -7
- data/examples/block_padding/test_app.rb +0 -31
- 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.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.rbs +0 -7
- data/examples/table_select/test_app.rb +0 -180
- data/examples/widget_style_colors/test_app.rb +0 -48
- /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/{login_form.png → app_login_form.png} +0 -0
- /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
- /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
- /data/doc/images/{table_select.png → app_table_select.png} +0 -0
- /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
- /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
- /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
- /data/doc/images/{list_styles.png → widget_list_styles.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/{rich_text.png → widget_rich_text.png} +0 -0
- /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
data/doc/quickstart.md
CHANGED
|
@@ -29,7 +29,9 @@ gem install ratatui_ruby
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## Tutorials
|
|
33
|
+
|
|
34
|
+
### Basic Application
|
|
33
35
|
|
|
34
36
|
Here is a "Hello World" application that demonstrates the core lifecycle of a **ratatui_ruby** app.
|
|
35
37
|
|
|
@@ -44,7 +46,6 @@ begin
|
|
|
44
46
|
loop do
|
|
45
47
|
# 2. Create your UI (Immediate Mode)
|
|
46
48
|
# We define a Paragraph widget inside a Block with a title and borders.
|
|
47
|
-
# Other widgets include RatatuiRuby::RatatuiMascot, RatatuiRuby::RatatuiLogo, etc.
|
|
48
49
|
view = RatatuiRuby::Paragraph.new(
|
|
49
50
|
text: "Hello, Ratatui! Press 'q' to quit.",
|
|
50
51
|
alignment: :center,
|
|
@@ -58,11 +59,13 @@ begin
|
|
|
58
59
|
)
|
|
59
60
|
|
|
60
61
|
# 3. Draw the UI
|
|
61
|
-
RatatuiRuby.draw
|
|
62
|
+
RatatuiRuby.draw do |frame|
|
|
63
|
+
frame.render_widget(view, frame.area)
|
|
64
|
+
end
|
|
62
65
|
|
|
63
66
|
# 4. Poll for events
|
|
64
67
|
event = RatatuiRuby.poll_event
|
|
65
|
-
break if event == "q"
|
|
68
|
+
break if event.key? && event.code == "q"
|
|
66
69
|
end
|
|
67
70
|
ensure
|
|
68
71
|
# 5. Restore the terminal to its original state
|
|
@@ -70,15 +73,15 @@ ensure
|
|
|
70
73
|
end
|
|
71
74
|
```
|
|
72
75
|
|
|
73
|
-

|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
#### How it works
|
|
76
79
|
|
|
77
80
|
1. **`RatatuiRuby.init_terminal`**: Enters raw mode and switches to the alternate screen.
|
|
78
|
-
2. **Immediate Mode UI**: On every iteration
|
|
79
|
-
3. **`RatatuiRuby.draw
|
|
80
|
-
4. **`RatatuiRuby.poll_event`**:
|
|
81
|
-
5. **`RatatuiRuby.restore_terminal`**:
|
|
81
|
+
2. **Immediate Mode UI**: On every iteration, describe your UI by creating `Data` objects (e.g., `Paragraph`, `Block`).
|
|
82
|
+
3. **`RatatuiRuby.draw { |frame| ... }`**: The block receives a `Frame` object as a canvas. Render widgets onto specific areas. Nothing is drawn until the block finishes, ensuring flicker-free updates.
|
|
83
|
+
4. **`RatatuiRuby.poll_event`**: Returns a typed `Event` object with predicates like `key?`, `mouse?`, `resize?`, etc. Returns `RatatuiRuby::Event::None` if no events are pending. Use predicates to check event type without pattern matching.
|
|
84
|
+
5. **`RatatuiRuby.restore_terminal`**: Essential for leaving raw mode and returning to the shell. Always wrap your loop in `begin...ensure` to guarantee this runs.
|
|
82
85
|
|
|
83
86
|
### Idiomatic Session
|
|
84
87
|
|
|
@@ -104,168 +107,192 @@ RatatuiRuby.run do |tui|
|
|
|
104
107
|
)
|
|
105
108
|
|
|
106
109
|
# 3. Use RatatuiRuby methods, too.
|
|
107
|
-
tui.draw
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
tui.draw do |frame|
|
|
111
|
+
frame.render_widget(view, frame.area)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# 4. Poll for events with pattern matching
|
|
115
|
+
case tui.poll_event
|
|
116
|
+
in { type: :key, code: "q" }
|
|
117
|
+
break
|
|
118
|
+
else
|
|
119
|
+
# Ignore other events
|
|
120
|
+
end
|
|
111
121
|
end
|
|
112
122
|
end
|
|
113
|
-
|
|
123
|
+
```
|
|
114
124
|
|
|
115
125
|
#### How it works
|
|
116
126
|
|
|
117
127
|
1. **`RatatuiRuby.run`**: This context manager initializes the terminal before the block starts and ensures `restore_terminal` is called when the block exits (even if an error occurs).
|
|
118
128
|
2. **Widget Shorthand**: The block yields a `Session` object (here named `tui`). This object provides factory methods for every widget, allowing you to write `tui.paragraph(...)` instead of the more verbose `RatatuiRuby::Paragraph.new(...)`.
|
|
119
|
-
3. **Method Shorthand**: The session object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby
|
|
129
|
+
3. **Method Shorthand**: The session object also provides aliases for module functions of `RatatuiRuby`, allowing you to write `tui.draw(...)` instead of the more verbose `RatatuiRuby.draw(...)`.
|
|
130
|
+
4. **Pattern Matching for Events**: Use `case...in` with pattern matching for elegant event dispatch. Always include an `else` clause at the end to catch unmatched event types (mouse, resize, paste, focus, etc.), otherwise Ruby raises `NoMatchingPatternError`.
|
|
120
131
|
|
|
121
132
|
For a deeper dive into the available application architectures (Manual vs Managed), see [Application Architecture](./application_architecture.md).
|
|
122
133
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
To see more complex layouts and widget usage, check out the `examples/` directory in the repository.
|
|
134
|
+
### Adding Layouts
|
|
126
135
|
|
|
127
|
-
|
|
136
|
+
Real-world applications often need to split the screen into multiple areas. `RatatuiRuby::Layout` lets you do this easily.
|
|
128
137
|
|
|
129
|
-
|
|
138
|
+
```ruby
|
|
139
|
+
require "ratatui_ruby"
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
RatatuiRuby.run do |tui|
|
|
142
|
+
loop do
|
|
143
|
+
tui.draw do |frame|
|
|
144
|
+
# 1. Split the screen
|
|
145
|
+
top, bottom = tui.layout_split(
|
|
146
|
+
frame.area,
|
|
147
|
+
direction: :vertical,
|
|
148
|
+
constraints: [
|
|
149
|
+
tui.constraint_percentage(75),
|
|
150
|
+
tui.constraint_percentage(25),
|
|
151
|
+
]
|
|
152
|
+
)
|
|
132
153
|
|
|
133
|
-
|
|
154
|
+
# 2. Render Top Widget
|
|
155
|
+
frame.render_widget(
|
|
156
|
+
tui.paragraph(
|
|
157
|
+
text: "Hello, Ratatui!",
|
|
158
|
+
alignment: :center,
|
|
159
|
+
block: tui.block(title: "Content", borders: [:all], border_color: "cyan")
|
|
160
|
+
),
|
|
161
|
+
top
|
|
162
|
+
)
|
|
134
163
|
|
|
135
|
-
|
|
164
|
+
# 3. Render Bottom Widget with Styled Text
|
|
165
|
+
# We use a Line of Spans to style specific characters
|
|
166
|
+
text_line = tui.text_line(
|
|
167
|
+
spans: [
|
|
168
|
+
tui.text_span(content: "Press '"),
|
|
169
|
+
tui.text_span(
|
|
170
|
+
content: "q",
|
|
171
|
+
style: tui.style(modifiers: [:bold, :underlined])
|
|
172
|
+
),
|
|
173
|
+
tui.text_span(content: "' to quit."),
|
|
174
|
+
],
|
|
175
|
+
alignment: :center
|
|
176
|
+
)
|
|
136
177
|
|
|
137
|
-
|
|
178
|
+
frame.render_widget(
|
|
179
|
+
tui.paragraph(
|
|
180
|
+
text: text_line,
|
|
181
|
+
block: tui.block(title: "Controls", borders: [:all])
|
|
182
|
+
),
|
|
183
|
+
bottom
|
|
184
|
+
)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
case tui.poll_event
|
|
188
|
+
in { type: :key, code: "q" }
|
|
189
|
+
break
|
|
190
|
+
else
|
|
191
|
+
# Ignore other events
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
```
|
|
138
196
|
|
|
139
|
-
|
|
197
|
+
#### How it works
|
|
140
198
|
|
|
141
|
-
|
|
199
|
+
1. **`tui.layout_split` (`RatatuiRuby::Layout.split`)**: Takes an area (like `frame.area`) and splits it into multiple sub-areas based on constraints.
|
|
200
|
+
2. **`tui.constraint_*` (`RatatuiRuby::Constraint`)**: Defines how space is distributed (e.g., `percentage`, `length`, `min`, `max`).
|
|
201
|
+
3. **`Frame#render_widget(widget, rect)`**: You pass the specific area (like `top` or `bottom`) to render the widget into that exact region.
|
|
202
|
+
4. **`tui.text_span` (`RatatuiRuby::Text::Span`)**: Allows for rich styling within a single line of text.
|
|
142
203
|
|
|
143
|
-
|
|
204
|
+
## Examples
|
|
144
205
|
|
|
145
|
-
|
|
206
|
+
These examples showcase the full power of **ratatui_ruby**. You can find their source code in the [examples directory](../examples).
|
|
146
207
|
|
|
147
|
-
|
|
208
|
+
### Sample Applications
|
|
148
209
|
|
|
149
|
-
|
|
210
|
+
Full-featured examples demonstrating complex layouts and real-world TUI patterns.
|
|
150
211
|
|
|
151
|
-
### [Box Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/box_demo/app.rb)
|
|
152
212
|
|
|
153
|
-
A simple demonstration of `Block` and `Paragraph` widgets, reacting to key presses to change colors, border types, border styles, and title styling. Features the new `border_style` parameter for applying colors and modifiers (bold, italic) to borders independently of the content background.
|
|
154
213
|
|
|
155
|
-
|
|
214
|
+
#### [All Events](../examples/app_all_events/app.rb)
|
|
156
215
|
|
|
157
|
-
|
|
216
|
+
Handling terminal events is unpredictable. Developers need to know exactly what the terminal sends for `Ctrl+C` or a mouse drag.
|
|
158
217
|
|
|
159
|
-
|
|
218
|
+
This app captures and visualizes every event—keys, mouse, resize, paste, and focus.
|
|
160
219
|
|
|
161
|
-
|
|
220
|
+
Use it to debug your input handling or verify terminal behavior.
|
|
162
221
|
|
|
163
|
-
|
|
222
|
+
**What you'll learn:**
|
|
164
223
|
|
|
165
|
-
|
|
224
|
+
* **MVVM Architecture**: Separates logic (Model), state (ViewModel), and rendering (View) for clean, testable code.
|
|
225
|
+
* **Event Handling**: Captures and distinguishes all input types, including modifiers (`Ctrl+C`) and focus changes.
|
|
226
|
+
* **Scalable Structure**: Organizes a non-trivial application into small, focused classes instead of a monolithic script.
|
|
166
227
|
|
|
167
|
-

|
|
168
229
|
|
|
169
|
-
|
|
230
|
+
#### [Color Picker](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_color_picker/app.rb)
|
|
170
231
|
|
|
171
|
-
|
|
232
|
+
Interactive tools require complex state. Mapping mouse clicks to widgets and handling modal dialogs creates messy code if handled in the main loop.
|
|
172
233
|
|
|
173
|
-
|
|
234
|
+
This app implements a full Color Picker using a "Scene-Orchestrated" pattern. The Scene calculates layout and exposes cached rectangles for hit testing.
|
|
174
235
|
|
|
175
|
-
|
|
236
|
+
Use it to build forms, editors, and mouse-driven tools.
|
|
176
237
|
|
|
177
|
-
|
|
238
|
+
**What you'll learn:**
|
|
178
239
|
|
|
179
|
-
|
|
240
|
+
* **Scene-Orchestrated MVC**: Separates the View (layout/rendering) from the Controller (event loop) and Model (business logic).
|
|
241
|
+
* **Hit Testing**: Caches layout rectangles during the render pass to handle mouse clicks on specific elements.
|
|
242
|
+
* **Modal Dialogs**: Implements overlay patterns that intercept input.
|
|
180
243
|
|
|
181
|
-
|
|
244
|
+
#### [Custom Widget (Escape Hatch)](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_custom_widget/app.rb)
|
|
182
245
|
|
|
183
|
-
Demonstrates
|
|
246
|
+
Demonstrates how to define a custom widget in pure Ruby using the `render(area, buffer)` escape hatch for low-level drawing.
|
|
184
247
|
|
|
185
|
-

|
|
186
249
|
|
|
187
|
-
|
|
250
|
+
#### [Layout Split Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/widget_layout_split/app.rb)
|
|
188
251
|
|
|
189
|
-
Demonstrates
|
|
252
|
+
Demonstrates `Layout.split` with interactive attribute cycling. Features hotkey controls For direction (vertical/horizontal), all 7 flex modes (legacy, start, center, end, space_between, space_around, space_evenly), and constraint types (fill, length, percentage, min, ratio).
|
|
190
253
|
|
|
191
|
-

|
|
192
255
|
|
|
193
|
-
|
|
256
|
+
#### [Login Form](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_login_form/app.rb)
|
|
194
257
|
|
|
195
258
|
Shows how to use `Overlay`, `Center`, and `Cursor` to build a modal login form with text input.
|
|
196
259
|
|
|
197
|
-

|
|
198
261
|
|
|
199
|
-
|
|
262
|
+
#### [Map Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_map_demo/app.rb)
|
|
200
263
|
|
|
201
264
|
Exhibits the `Canvas` widget's power, rendering a world map with city labels, animated circles, and lines.
|
|
202
265
|
|
|
203
|
-

|
|
206
|
-
|
|
207
|
-
Detailed plumbing of mouse events, including clicks, drags, and movement tracking.
|
|
208
|
-
|
|
209
|
-

|
|
210
|
-
|
|
211
|
-
### [Popup Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/popup_demo/app.rb)
|
|
212
|
-
|
|
213
|
-
Demonstrates the `Clear` widget and how to prevent "style bleed" when rendering opaque popups over colored backgrounds.
|
|
214
|
-
|
|
215
|
-

|
|
266
|
+

|
|
216
267
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
Demonstrates the `RatatuiLogo` widget.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-

|
|
223
|
-
|
|
224
|
-
### [Rich Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/rich_text/app.rb)
|
|
225
|
-
|
|
226
|
-
Demonstrates `Text::Span` and `Text::Line` for creating styled text with inline formatting, enabling word-level control over colors and text modifiers.
|
|
227
|
-
|
|
228
|
-

|
|
229
|
-
|
|
230
|
-
### [Scrollbar Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scrollbar_demo/app.rb)
|
|
231
|
-
|
|
232
|
-
A demonstration of the `Scrollbar` widget, featuring mouse wheel scrolling and extensive customization of symbols and styles.
|
|
233
|
-
|
|
234
|
-

|
|
235
|
-
|
|
236
|
-
### [Scroll Text](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/scroll_text/app.rb)
|
|
237
|
-
|
|
238
|
-
Demonstrates the `Paragraph` widget's scroll functionality, allowing navigation through long text content using arrow keys for both horizontal and vertical scrolling.
|
|
239
|
-
|
|
240
|
-

|
|
241
|
-
|
|
242
|
-
### [Sparkline Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/sparkline_demo/app.rb)
|
|
243
|
-
|
|
244
|
-
Demonstrates the `Sparkline` widget with interactive attribute cycling. Features multiple data sets with different patterns (steady growth, gaps, random, sawtooth, peaks), and explores all `Sparkline` options including direction, color, the new `absent_value_symbol` and `absent_value_style` parameters for distinguishing zero/absent values from low data, and the new `bar_set` parameter for custom bar characters.
|
|
245
|
-
|
|
246
|
-

|
|
247
|
-
|
|
248
|
-
### [Gauge Demo](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/gauge_demo/app.rb)
|
|
249
|
-
|
|
250
|
-
Demonstrates the `Gauge` widget with interactive attribute cycling. Features multiple gauge instances with customizable ratio, gauge color, background style, Unicode toggle, and label modes. The sidebar provides hotkey documentation for exploring all Gauge options, including the distinction between `style` (background) and `gauge_style` (filled bar).
|
|
251
|
-
|
|
252
|
-

|
|
253
|
-
|
|
254
|
-
### [Table Select](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/table_select/app.rb)
|
|
268
|
+
#### [Table Select](https://git.sr.ht/~kerrick/ratatui_ruby/tree/main/item/examples/app_table_select/app.rb)
|
|
255
269
|
|
|
256
270
|
Demonstrates interactive row selection in the `Table` widget with keyboard navigation, highlighting selected rows with custom styles and symbols, applying a base style, and dynamically adjusting `column_spacing`. Also demonstrates `column_highlight_style` and the new `cell_highlight_style` for precise selection visualization.
|
|
257
271
|
|
|
258
|
-

|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
### Widget Demos
|
|
276
|
+
|
|
277
|
+
These smaller, focused examples demonstrate specific widgets and their configuration options.
|
|
278
|
+
|
|
279
|
+
* [Bar Chart](../examples/widget_barchart_demo/app.rb)
|
|
280
|
+
* [Block Padding](../examples/widget_block_padding/app.rb)
|
|
281
|
+
* [Block Titles](../examples/widget_block_titles/app.rb)
|
|
282
|
+
* [Box (Block/Paragraph)](../examples/widget_box_demo/app.rb)
|
|
283
|
+
* [Calendar](../examples/widget_calendar_demo/app.rb)
|
|
284
|
+
* [Chart](../examples/widget_chart_demo/app.rb)
|
|
285
|
+
* [Gauge](../examples/widget_gauge_demo/app.rb)
|
|
286
|
+
* [Line Gauge](../examples/widget_line_gauge_demo/app.rb)
|
|
287
|
+
* [List](../examples/widget_list_demo/app.rb)
|
|
288
|
+
* [Popup (Clear)](../examples/widget_popup_demo/app.rb)
|
|
289
|
+
* [Rect](../examples/widget_rect/app.rb)
|
|
290
|
+
* [Ratatui Logo](../examples/widget_ratatui_logo_demo/app.rb)
|
|
291
|
+
* [Ratatui Mascot](../examples/widget_ratatui_mascot_demo/app.rb)
|
|
292
|
+
* [Rich Text](../examples/widget_rich_text/app.rb)
|
|
293
|
+
* [Scrollbar](../examples/widget_scrollbar_demo/app.rb)
|
|
294
|
+
* [Scroll Text](../examples/widget_scroll_text/app.rb)
|
|
295
|
+
* [Sparkline](../examples/widget_sparkline_demo/app.rb)
|
|
296
|
+
* [Table Flex](../examples/widget_table_flex/app.rb)
|
|
297
|
+
* [Tabs](../examples/widget_tabs_demo/app.rb)
|
|
298
|
+
* [Widget Style Colors](../examples/widget_style_colors/app.rb)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# App All Events Example
|
|
7
|
+
|
|
8
|
+
This example application captures and visualizes every event supported by `ratatui_ruby`. It serves as a comprehensive reference for event handling and a demonstration of a clean, scalable architectural pattern.
|
|
9
|
+
|
|
10
|
+
## Architecture: MVVM (Model-View-ViewModel)
|
|
11
|
+
|
|
12
|
+
This application demonstrates the **Model-View-ViewModel (MVVM)** pattern, modified for the immediate-mode nature of terminal UIs. This separation of concerns ensures that the UI logic is completely decoupled from the business logic, making the application easier to test and maintain.
|
|
13
|
+
|
|
14
|
+
### 1. Model (`model/`)
|
|
15
|
+
The **Model** manages the application's domain data and logic. It knows nothing about the UI.
|
|
16
|
+
|
|
17
|
+
* **`Events` (`model/events.rb`)**: The core store. It records incoming events, maintains statistics (counts), and handles business logic like "highlight this event type for 300ms."
|
|
18
|
+
* **`EventEntry` (`model/event_entry.rb`)**: A value object representing a single recorded event.
|
|
19
|
+
|
|
20
|
+
### 2. View State (ViewModel) (`view_state.rb`)
|
|
21
|
+
The **View State** (comparable to a ViewModel or Presenter) is an immutable data structure built specifically for the View.
|
|
22
|
+
|
|
23
|
+
* **`ViewState`**: It acts as a bridge. In every render loop, the application builds a fresh `ViewState` object, calculating derived data (like styles, active flags, and formatted strings) from the raw Model data.
|
|
24
|
+
* **Why?**: This prevents the View from having to contain logic. The View doesn't ask "is the app focused so I should use green?"; it just asks `state.border_color`.
|
|
25
|
+
|
|
26
|
+
### 3. View (`view/`)
|
|
27
|
+
The **View** is responsible **only** for rendering. It receives the `ViewState` and draws to the screen.
|
|
28
|
+
|
|
29
|
+
* **`View::App` (`view/app_view.rb`)**: The root view. It handles the high-level layout (splitting the screen into areas).
|
|
30
|
+
* **Sub-views**: `Counts`, `Live`, `Log`, `Controls`. Each is a small, focused component that renders a specific part of the screen based on the data in `ViewState`.
|
|
31
|
+
|
|
32
|
+
### 4. Controller/App (`app.rb`)
|
|
33
|
+
The **`AppAllEvents`** class ties it all together. It owns the main loop:
|
|
34
|
+
|
|
35
|
+
1. **Poll**: Waits for an event from the terminal.
|
|
36
|
+
2. **Update**: Passes the event to the **Model** (`@events.record`).
|
|
37
|
+
3. **Build State**: Creates a new **ViewState** from the current Model and global state.
|
|
38
|
+
4. **Render**: Passes the **ViewState** to the **View** to draw the frame.
|
|
39
|
+
|
|
40
|
+
## Library Features Showcased
|
|
41
|
+
|
|
42
|
+
Reading this code will teach you how to:
|
|
43
|
+
|
|
44
|
+
* **Handle All Events**:
|
|
45
|
+
* **Keyboard**: Capture normal keys and modifiers (`Ctrl+c`, `q`).
|
|
46
|
+
* **Mouse**: track clicks, drags, and scroll events.
|
|
47
|
+
* **Focus**: React to the terminal window gaining or losing focus (`FocusGained`/`FocusLost`).
|
|
48
|
+
* **Resize**: Dynamically adapt layouts when the terminal size changes.
|
|
49
|
+
* **Paste**: Handle bracketed paste events (if supported by the terminal).
|
|
50
|
+
* **Layouts**: Use `tui.layout_split` with constraints (`Length`, `Fill`) to create complex, responsive dashboards.
|
|
51
|
+
* **Styling**: Apply dynamic styles (bold, colors) based on application state.
|
|
52
|
+
* **Structure**: Organize a non-trivial CLI tool into small, single-purpose classes.
|
|
53
|
+
|
|
54
|
+
## What Problems Does This Solve?
|
|
55
|
+
|
|
56
|
+
### "What key code is my terminal sending?"
|
|
57
|
+
If you are building an app and your logic isn't catching `Ctrl+Left`, run this app and press the keys. You will see exactly how `ratatui_ruby` parses that input (e.g., is it a `Key` event? What are the modifiers?).
|
|
58
|
+
|
|
59
|
+
### "How do I structure a real app?"
|
|
60
|
+
Hello World examples are great, but they don't scale. This example shows how to structure an application that can grow. By simulating a "dashboard" with multiple independent widgets updating in real-time, it solves the problem of "how do I pass data around without global variables?"
|
|
61
|
+
|
|
62
|
+
### "How do I implement an event loop?"
|
|
63
|
+
It provides a robust reference implementation of the standard `loop { draw; handle_input }` cycle, including the correct way to handle quit signals.
|
|
64
|
+
|
|
65
|
+
## Comparison: Choosing an Architecture
|
|
66
|
+
|
|
67
|
+
Complex applications require structured state habits. `AppAllEvents` and the [Color Picker](../app_color_picker/README.md) demonstrate two different approaches.
|
|
68
|
+
|
|
69
|
+
### The Dashboard Approach (AppAllEvents)
|
|
70
|
+
|
|
71
|
+
Dashboards display data. They rarely require complex mouse interaction. Strict MVVM works best here. The View is a pure function. It accepts a `ViewState` and draws it. It ignores input. This simplifies testing.
|
|
72
|
+
|
|
73
|
+
Use this pattern for logs, monitors, and data viewers.
|
|
74
|
+
|
|
75
|
+
### The Tool Approach (Color Picker)
|
|
76
|
+
|
|
77
|
+
Tools require interaction. Users click buttons and drag sliders. The Controller needs to know where components exist on screen. MVVM hides this layout data.
|
|
78
|
+
|
|
79
|
+
The Color Picker uses a "Scene" pattern. The View exposes layout rectangles. The Controller uses these rectangles to handle mouse clicks.
|
|
80
|
+
|
|
81
|
+
Use this pattern for forms, editors, and mouse-driven tools.
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
$LOAD_PATH.unshift File.expand_path(__dir__)
|
|
8
|
+
|
|
9
|
+
require "ratatui_ruby"
|
|
10
|
+
require_relative "model/events"
|
|
11
|
+
require_relative "view_state"
|
|
12
|
+
require_relative "view/app_view"
|
|
13
|
+
|
|
14
|
+
# Demonstrates the full range of terminal events supported by RatatuiRuby.
|
|
15
|
+
#
|
|
16
|
+
# Developers need a comprehensive example to understand how keys, mouse, resize, and focus events behave.
|
|
17
|
+
# Testing event handling across different terminal emulators and platforms can be unpredictable.
|
|
18
|
+
#
|
|
19
|
+
# This application captures and logs every event received from the backend, providing real-time feedback and history.
|
|
20
|
+
#
|
|
21
|
+
# Use it to verify your terminal's capabilities or as a reference for complex event handling.
|
|
22
|
+
#
|
|
23
|
+
# === Examples
|
|
24
|
+
#
|
|
25
|
+
# # Run from the command line:
|
|
26
|
+
# # ruby examples/app_all_events/app.rb
|
|
27
|
+
#
|
|
28
|
+
# app = AppAllEvents.new
|
|
29
|
+
# app.run
|
|
30
|
+
class AppAllEvents
|
|
31
|
+
# List of all event types tracked by this application.
|
|
32
|
+
EVENT_TYPES = %i[key mouse resize paste focus none].freeze
|
|
33
|
+
|
|
34
|
+
# Creates a new AppAllEvents instance and initializes its state.
|
|
35
|
+
def initialize
|
|
36
|
+
@view = View::App.new
|
|
37
|
+
@events = Events.new
|
|
38
|
+
@focused = true
|
|
39
|
+
@last_dimensions = [80, 24]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Starts the application event loop.
|
|
43
|
+
#
|
|
44
|
+
# === Example
|
|
45
|
+
#
|
|
46
|
+
# app.run
|
|
47
|
+
def run
|
|
48
|
+
RatatuiRuby.run do |tui|
|
|
49
|
+
@tui = tui
|
|
50
|
+
loop do
|
|
51
|
+
render
|
|
52
|
+
break if handle_input == :quit
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private def render
|
|
58
|
+
view_state = ViewState.build(
|
|
59
|
+
@events,
|
|
60
|
+
@focused,
|
|
61
|
+
@tui,
|
|
62
|
+
nil
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@tui.draw { |frame| @view.call(view_state, @tui, frame, frame.area) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private def handle_input
|
|
69
|
+
event = @tui.poll_event
|
|
70
|
+
|
|
71
|
+
case event
|
|
72
|
+
when RatatuiRuby::Event::Key
|
|
73
|
+
return :quit if event.code == "q"
|
|
74
|
+
return :quit if event.code == "c" && event.modifiers.include?("ctrl")
|
|
75
|
+
@events.record(event)
|
|
76
|
+
when RatatuiRuby::Event::Resize
|
|
77
|
+
@events.record(event, context: { last_dimensions: @last_dimensions })
|
|
78
|
+
@last_dimensions = [event.width, event.height]
|
|
79
|
+
when RatatuiRuby::Event::FocusGained
|
|
80
|
+
@focused = true
|
|
81
|
+
@events.record(event)
|
|
82
|
+
when RatatuiRuby::Event::FocusLost
|
|
83
|
+
@focused = false
|
|
84
|
+
@events.record(event)
|
|
85
|
+
else
|
|
86
|
+
@events.record(event)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
AppAllEvents.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
# Cycles through a set of colors for event logging.
|
|
7
|
+
#
|
|
8
|
+
# Sequential events in a log are hard to distinguish if they all look the same.
|
|
9
|
+
# Manually assigning colors to every event type or entry is repetitive.
|
|
10
|
+
#
|
|
11
|
+
# This class automatically cycles through a predefined list of vibrant colors.
|
|
12
|
+
#
|
|
13
|
+
# Use it to give each event in a log a distinct visual identity.
|
|
14
|
+
#
|
|
15
|
+
# === Examples
|
|
16
|
+
#
|
|
17
|
+
# cycler = EventColorCycle.new
|
|
18
|
+
# cycler.next_color #=> :cyan
|
|
19
|
+
# cycler.next_color #=> :magenta
|
|
20
|
+
# cycler.next_color #=> :yellow
|
|
21
|
+
# cycler.next_color #=> :cyan
|
|
22
|
+
class EventColorCycle
|
|
23
|
+
# List of colors to cycle through.
|
|
24
|
+
COLORS = %i[cyan magenta yellow].freeze
|
|
25
|
+
|
|
26
|
+
# Creates a new EventColorCycle.
|
|
27
|
+
def initialize
|
|
28
|
+
@index = 0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns the next color in the cycle.
|
|
32
|
+
#
|
|
33
|
+
# === Example
|
|
34
|
+
#
|
|
35
|
+
# cycler.next_color #=> :cyan
|
|
36
|
+
def next_color
|
|
37
|
+
color = COLORS[@index]
|
|
38
|
+
@index = (@index + 1) % COLORS.length
|
|
39
|
+
color
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
require_relative "timestamp"
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
# Stores details about a single event in the history log.
|
|
10
|
+
#
|
|
11
|
+
# Event logs need to store diverse data including types, keys, colors, and timestamps.
|
|
12
|
+
# Managing loose hashes or arrays for event history is error-prone and hard to query.
|
|
13
|
+
#
|
|
14
|
+
# This class provides a structured data object for every recorded event.
|
|
15
|
+
#
|
|
16
|
+
# Use it to represent mouse clicks, key presses, or resize events in a log.
|
|
17
|
+
#
|
|
18
|
+
# === Examples
|
|
19
|
+
#
|
|
20
|
+
# # Typically created via Events.record
|
|
21
|
+
# entry = EventEntry.create(key_event, :cyan, Timestamp.now)
|
|
22
|
+
# puts entry.type #=> :key
|
|
23
|
+
# puts entry.description #=> '#<RatatuiRuby::Event::Key ...>'
|
|
24
|
+
class EventEntry < Data.define(:event, :color, :timestamp)
|
|
25
|
+
# Creates a new EventEntry.
|
|
26
|
+
#
|
|
27
|
+
# [event] RatatuiRuby::Event object.
|
|
28
|
+
# [color] Symbol color for the log display.
|
|
29
|
+
# [timestamp] Timestamp of when the event occurred.
|
|
30
|
+
def self.create(event, color, timestamp)
|
|
31
|
+
new(
|
|
32
|
+
event:,
|
|
33
|
+
color:,
|
|
34
|
+
timestamp:
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the event type.
|
|
39
|
+
#
|
|
40
|
+
# === Example
|
|
41
|
+
#
|
|
42
|
+
# entry.type #=> :key
|
|
43
|
+
def type
|
|
44
|
+
case event
|
|
45
|
+
when RatatuiRuby::Event::Key then :key
|
|
46
|
+
when RatatuiRuby::Event::Mouse then :mouse
|
|
47
|
+
when RatatuiRuby::Event::Resize then :resize
|
|
48
|
+
when RatatuiRuby::Event::Paste then :paste
|
|
49
|
+
when RatatuiRuby::Event::FocusGained then :focus_gained
|
|
50
|
+
when RatatuiRuby::Event::FocusLost then :focus_lost
|
|
51
|
+
else :unknown
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns the event description using inspect.
|
|
56
|
+
#
|
|
57
|
+
# === Example
|
|
58
|
+
#
|
|
59
|
+
# entry.description #=> '#<RatatuiRuby::Event::Key code="a" modifiers=[]>'
|
|
60
|
+
def description
|
|
61
|
+
event.inspect
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Checks if the entry matches the given type.
|
|
65
|
+
#
|
|
66
|
+
# [check_type] Symbol type to check against.
|
|
67
|
+
#
|
|
68
|
+
# === Example
|
|
69
|
+
#
|
|
70
|
+
# entry.matches_type?(:key) #=> true
|
|
71
|
+
def matches_type?(check_type)
|
|
72
|
+
return true if check_type == :focus && (type == :focus_gained || type == :focus_lost)
|
|
73
|
+
type == check_type
|
|
74
|
+
end
|
|
75
|
+
end
|