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
|
@@ -28,10 +28,13 @@ pub fn inject_test_event(event_type: String, data: magnus::RHash) -> Result<(),
|
|
|
28
28
|
Ok(())
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
fn parse_key_event(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
fn parse_key_event(
|
|
32
|
+
data: magnus::RHash,
|
|
33
|
+
ruby: &magnus::Ruby,
|
|
34
|
+
) -> Result<ratatui::crossterm::event::Event, Error> {
|
|
35
|
+
let code_val: Value = data
|
|
36
|
+
.get(ruby.to_symbol("code"))
|
|
37
|
+
.ok_or_else(|| Error::new(ruby.exception_arg_error(), "Missing 'code' in key event"))?;
|
|
35
38
|
let code_str: String = String::try_convert(code_val)?;
|
|
36
39
|
let code = match code_str.as_str() {
|
|
37
40
|
"up" => ratatui::crossterm::event::KeyCode::Up,
|
|
@@ -59,13 +62,18 @@ fn parse_key_event(data: magnus::RHash, ruby: &magnus::Ruby) -> Result<ratatui::
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
Ok(ratatui::crossterm::event::Event::Key(
|
|
65
|
+
Ok(ratatui::crossterm::event::Event::Key(
|
|
66
|
+
ratatui::crossterm::event::KeyEvent::new(code, modifiers),
|
|
67
|
+
))
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
fn parse_mouse_event(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
fn parse_mouse_event(
|
|
71
|
+
data: magnus::RHash,
|
|
72
|
+
ruby: &magnus::Ruby,
|
|
73
|
+
) -> Result<ratatui::crossterm::event::Event, Error> {
|
|
74
|
+
let kind_val: Value = data
|
|
75
|
+
.get(ruby.to_symbol("kind"))
|
|
76
|
+
.ok_or_else(|| Error::new(ruby.exception_arg_error(), "Missing 'kind' in mouse event"))?;
|
|
69
77
|
let kind_str: String = String::try_convert(kind_val)?;
|
|
70
78
|
|
|
71
79
|
let button = if let Some(btn_val) = data.get(ruby.to_symbol("button")) {
|
|
@@ -79,14 +87,14 @@ fn parse_mouse_event(data: magnus::RHash, ruby: &magnus::Ruby) -> Result<ratatui
|
|
|
79
87
|
ratatui::crossterm::event::MouseButton::Left
|
|
80
88
|
};
|
|
81
89
|
|
|
82
|
-
let x_val: Value = data
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
let x_val: Value = data
|
|
91
|
+
.get(ruby.to_symbol("x"))
|
|
92
|
+
.ok_or_else(|| Error::new(ruby.exception_arg_error(), "Missing 'x' in mouse event"))?;
|
|
85
93
|
let x: u16 = u16::try_convert(x_val)?;
|
|
86
94
|
|
|
87
|
-
let y_val: Value = data
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
let y_val: Value = data
|
|
96
|
+
.get(ruby.to_symbol("y"))
|
|
97
|
+
.ok_or_else(|| Error::new(ruby.exception_arg_error(), "Missing 'y' in mouse event"))?;
|
|
90
98
|
let y: u16 = u16::try_convert(y_val)?;
|
|
91
99
|
|
|
92
100
|
let kind = match kind_str.as_str() {
|
|
@@ -98,7 +106,12 @@ fn parse_mouse_event(data: magnus::RHash, ruby: &magnus::Ruby) -> Result<ratatui
|
|
|
98
106
|
"scroll_up" => ratatui::crossterm::event::MouseEventKind::ScrollUp,
|
|
99
107
|
"scroll_left" => ratatui::crossterm::event::MouseEventKind::ScrollLeft,
|
|
100
108
|
"scroll_right" => ratatui::crossterm::event::MouseEventKind::ScrollRight,
|
|
101
|
-
_ =>
|
|
109
|
+
_ => {
|
|
110
|
+
return Err(Error::new(
|
|
111
|
+
ruby.exception_arg_error(),
|
|
112
|
+
format!("Unknown mouse kind: {kind_str}"),
|
|
113
|
+
))
|
|
114
|
+
}
|
|
102
115
|
};
|
|
103
116
|
|
|
104
117
|
let mut modifiers = ratatui::crossterm::event::KeyModifiers::empty();
|
|
@@ -114,26 +127,48 @@ fn parse_mouse_event(data: magnus::RHash, ruby: &magnus::Ruby) -> Result<ratatui
|
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
Ok(ratatui::crossterm::event::Event::Mouse(
|
|
130
|
+
Ok(ratatui::crossterm::event::Event::Mouse(
|
|
131
|
+
ratatui::crossterm::event::MouseEvent {
|
|
132
|
+
kind,
|
|
133
|
+
column: x,
|
|
134
|
+
row: y,
|
|
135
|
+
modifiers,
|
|
136
|
+
},
|
|
137
|
+
))
|
|
118
138
|
}
|
|
119
139
|
|
|
120
|
-
fn parse_resize_event(
|
|
140
|
+
fn parse_resize_event(
|
|
141
|
+
data: magnus::RHash,
|
|
142
|
+
ruby: &magnus::Ruby,
|
|
143
|
+
) -> Result<ratatui::crossterm::event::Event, Error> {
|
|
121
144
|
let width_val: Value = data.get(ruby.to_symbol("width")).ok_or_else(|| {
|
|
122
|
-
Error::new(
|
|
145
|
+
Error::new(
|
|
146
|
+
ruby.exception_arg_error(),
|
|
147
|
+
"Missing 'width' in resize event",
|
|
148
|
+
)
|
|
123
149
|
})?;
|
|
124
150
|
let width: u16 = u16::try_convert(width_val)?;
|
|
125
151
|
|
|
126
152
|
let height_val: Value = data.get(ruby.to_symbol("height")).ok_or_else(|| {
|
|
127
|
-
Error::new(
|
|
153
|
+
Error::new(
|
|
154
|
+
ruby.exception_arg_error(),
|
|
155
|
+
"Missing 'height' in resize event",
|
|
156
|
+
)
|
|
128
157
|
})?;
|
|
129
158
|
let height: u16 = u16::try_convert(height_val)?;
|
|
130
159
|
|
|
131
160
|
Ok(ratatui::crossterm::event::Event::Resize(width, height))
|
|
132
161
|
}
|
|
133
162
|
|
|
134
|
-
fn parse_paste_event(
|
|
163
|
+
fn parse_paste_event(
|
|
164
|
+
data: magnus::RHash,
|
|
165
|
+
ruby: &magnus::Ruby,
|
|
166
|
+
) -> Result<ratatui::crossterm::event::Event, Error> {
|
|
135
167
|
let content_val: Value = data.get(ruby.to_symbol("content")).ok_or_else(|| {
|
|
136
|
-
Error::new(
|
|
168
|
+
Error::new(
|
|
169
|
+
ruby.exception_arg_error(),
|
|
170
|
+
"Missing 'content' in paste event",
|
|
171
|
+
)
|
|
137
172
|
})?;
|
|
138
173
|
let content: String = String::try_convert(content_val)?;
|
|
139
174
|
Ok(ratatui::crossterm::event::Event::Paste(content))
|
|
@@ -159,7 +194,10 @@ pub fn poll_event(ruby: &magnus::Ruby) -> Result<Value, Error> {
|
|
|
159
194
|
|
|
160
195
|
let is_test_mode = {
|
|
161
196
|
let term_lock = crate::terminal::TERMINAL.lock().unwrap();
|
|
162
|
-
matches!(
|
|
197
|
+
matches!(
|
|
198
|
+
term_lock.as_ref(),
|
|
199
|
+
Some(crate::terminal::TerminalWrapper::Test(_))
|
|
200
|
+
)
|
|
163
201
|
};
|
|
164
202
|
|
|
165
203
|
if is_test_mode {
|
|
@@ -209,10 +247,27 @@ fn handle_key_event(key: ratatui::crossterm::event::KeyEvent) -> Result<Value, E
|
|
|
209
247
|
};
|
|
210
248
|
hash.aset(ruby.to_symbol("code"), code)?;
|
|
211
249
|
let mut modifiers = Vec::new();
|
|
212
|
-
if key
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
250
|
+
if key
|
|
251
|
+
.modifiers
|
|
252
|
+
.contains(ratatui::crossterm::event::KeyModifiers::CONTROL)
|
|
253
|
+
{
|
|
254
|
+
modifiers.push("ctrl");
|
|
255
|
+
}
|
|
256
|
+
if key
|
|
257
|
+
.modifiers
|
|
258
|
+
.contains(ratatui::crossterm::event::KeyModifiers::ALT)
|
|
259
|
+
{
|
|
260
|
+
modifiers.push("alt");
|
|
261
|
+
}
|
|
262
|
+
if key
|
|
263
|
+
.modifiers
|
|
264
|
+
.contains(ratatui::crossterm::event::KeyModifiers::SHIFT)
|
|
265
|
+
{
|
|
266
|
+
modifiers.push("shift");
|
|
267
|
+
}
|
|
268
|
+
if !modifiers.is_empty() {
|
|
269
|
+
hash.aset(ruby.to_symbol("modifiers"), modifiers)?;
|
|
270
|
+
}
|
|
216
271
|
Ok(hash.into_value_with(&ruby))
|
|
217
272
|
}
|
|
218
273
|
|
|
@@ -224,14 +279,29 @@ fn handle_mouse_event(event: ratatui::crossterm::event::MouseEvent) -> Result<Va
|
|
|
224
279
|
ratatui::crossterm::event::MouseEventKind::Down(btn) => ("down", btn),
|
|
225
280
|
ratatui::crossterm::event::MouseEventKind::Up(btn) => ("up", btn),
|
|
226
281
|
ratatui::crossterm::event::MouseEventKind::Drag(btn) => ("drag", btn),
|
|
227
|
-
ratatui::crossterm::event::MouseEventKind::Moved =>
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
ratatui::crossterm::event::MouseEventKind::
|
|
231
|
-
|
|
282
|
+
ratatui::crossterm::event::MouseEventKind::Moved => {
|
|
283
|
+
("moved", ratatui::crossterm::event::MouseButton::Left)
|
|
284
|
+
}
|
|
285
|
+
ratatui::crossterm::event::MouseEventKind::ScrollDown => {
|
|
286
|
+
("scroll_down", ratatui::crossterm::event::MouseButton::Left)
|
|
287
|
+
}
|
|
288
|
+
ratatui::crossterm::event::MouseEventKind::ScrollUp => {
|
|
289
|
+
("scroll_up", ratatui::crossterm::event::MouseButton::Left)
|
|
290
|
+
}
|
|
291
|
+
ratatui::crossterm::event::MouseEventKind::ScrollLeft => {
|
|
292
|
+
("scroll_left", ratatui::crossterm::event::MouseButton::Left)
|
|
293
|
+
}
|
|
294
|
+
ratatui::crossterm::event::MouseEventKind::ScrollRight => {
|
|
295
|
+
("scroll_right", ratatui::crossterm::event::MouseButton::Left)
|
|
296
|
+
}
|
|
232
297
|
};
|
|
233
298
|
hash.aset(ruby.to_symbol("kind"), ruby.to_symbol(kind))?;
|
|
234
|
-
if matches!(
|
|
299
|
+
if matches!(
|
|
300
|
+
event.kind,
|
|
301
|
+
ratatui::crossterm::event::MouseEventKind::Down(_)
|
|
302
|
+
| ratatui::crossterm::event::MouseEventKind::Up(_)
|
|
303
|
+
| ratatui::crossterm::event::MouseEventKind::Drag(_)
|
|
304
|
+
) {
|
|
235
305
|
let btn_sym = match button {
|
|
236
306
|
ratatui::crossterm::event::MouseButton::Left => "left",
|
|
237
307
|
ratatui::crossterm::event::MouseButton::Right => "right",
|
|
@@ -244,9 +314,24 @@ fn handle_mouse_event(event: ratatui::crossterm::event::MouseEvent) -> Result<Va
|
|
|
244
314
|
hash.aset(ruby.to_symbol("x"), event.column)?;
|
|
245
315
|
hash.aset(ruby.to_symbol("y"), event.row)?;
|
|
246
316
|
let mut modifiers = Vec::new();
|
|
247
|
-
if event
|
|
248
|
-
|
|
249
|
-
|
|
317
|
+
if event
|
|
318
|
+
.modifiers
|
|
319
|
+
.contains(ratatui::crossterm::event::KeyModifiers::CONTROL)
|
|
320
|
+
{
|
|
321
|
+
modifiers.push("ctrl");
|
|
322
|
+
}
|
|
323
|
+
if event
|
|
324
|
+
.modifiers
|
|
325
|
+
.contains(ratatui::crossterm::event::KeyModifiers::ALT)
|
|
326
|
+
{
|
|
327
|
+
modifiers.push("alt");
|
|
328
|
+
}
|
|
329
|
+
if event
|
|
330
|
+
.modifiers
|
|
331
|
+
.contains(ratatui::crossterm::event::KeyModifiers::SHIFT)
|
|
332
|
+
{
|
|
333
|
+
modifiers.push("shift");
|
|
334
|
+
}
|
|
250
335
|
hash.aset(ruby.to_symbol("modifiers"), modifiers)?;
|
|
251
336
|
Ok(hash.into_value_with(&ruby))
|
|
252
337
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
//! Frame wrapper for exposing Ratatui's Frame to Ruby.
|
|
5
|
+
//!
|
|
6
|
+
//! This module provides `RubyFrame`, a struct that wraps `ratatui::Frame` and exposes
|
|
7
|
+
//! it to Ruby via Magnus. It enables explicit widget placement through `render_widget`,
|
|
8
|
+
//! aligning `RatatuiRuby` with native Rust Ratatui patterns.
|
|
9
|
+
//!
|
|
10
|
+
//! # Safety
|
|
11
|
+
//!
|
|
12
|
+
//! `RubyFrame` uses raw pointer casting to store a `Frame` reference with an erased
|
|
13
|
+
//! lifetime. This is safe because:
|
|
14
|
+
//! 1. `RubyFrame` is only created within `Terminal::draw()` callbacks
|
|
15
|
+
//! 2. `RubyFrame` is never returned from or stored beyond the callback scope
|
|
16
|
+
//! 3. The Ruby block receiving `RubyFrame` completes before the callback returns
|
|
17
|
+
//!
|
|
18
|
+
//! The `'static` lifetime is a lie, but a safe one within these constraints.
|
|
19
|
+
|
|
20
|
+
use crate::rendering;
|
|
21
|
+
use magnus::{prelude::*, Error, Value};
|
|
22
|
+
use ratatui::layout::Rect;
|
|
23
|
+
use ratatui::Frame;
|
|
24
|
+
use std::cell::UnsafeCell;
|
|
25
|
+
use std::ptr::NonNull;
|
|
26
|
+
|
|
27
|
+
/// A wrapper around Ratatui's `Frame` that can be exposed to Ruby.
|
|
28
|
+
///
|
|
29
|
+
/// This struct uses raw pointers to hold a mutable reference to the frame,
|
|
30
|
+
/// which is valid only for the duration of the draw callback.
|
|
31
|
+
///
|
|
32
|
+
/// # Safety
|
|
33
|
+
///
|
|
34
|
+
/// We implement `Send` manually because:
|
|
35
|
+
/// 1. `RubyFrame` is only created and used within a single `Terminal::draw()` callback
|
|
36
|
+
/// 2. The Ruby VM is single-threaded (GVL), so the frame pointer is never accessed
|
|
37
|
+
/// from multiple threads simultaneously
|
|
38
|
+
/// 3. `RubyFrame` never escapes the draw callback scope
|
|
39
|
+
#[magnus::wrap(class = "RatatuiRuby::Frame")]
|
|
40
|
+
pub struct RubyFrame {
|
|
41
|
+
/// Pointer to the underlying frame. Valid only during the draw callback.
|
|
42
|
+
inner: UnsafeCell<NonNull<Frame<'static>>>,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// SAFETY: RubyFrame is only used within Terminal::draw() callbacks, which are
|
|
46
|
+
// single-threaded. The Ruby VM's GVL ensures no concurrent access.
|
|
47
|
+
unsafe impl Send for RubyFrame {}
|
|
48
|
+
|
|
49
|
+
impl RubyFrame {
|
|
50
|
+
/// Creates a new `RubyFrame` wrapping the given frame reference.
|
|
51
|
+
///
|
|
52
|
+
/// # Safety
|
|
53
|
+
///
|
|
54
|
+
/// The caller must ensure that:
|
|
55
|
+
/// 1. The `RubyFrame` does not outlive the frame reference
|
|
56
|
+
/// 2. No other mutable references to the frame exist while `RubyFrame` is in use
|
|
57
|
+
pub fn new(frame: &mut Frame<'_>) -> Self {
|
|
58
|
+
// SAFETY: We cast the frame pointer to 'static lifetime. This is safe because:
|
|
59
|
+
// - RubyFrame is only used within Terminal::draw() callbacks
|
|
60
|
+
// - The Ruby block completes before the callback returns
|
|
61
|
+
// - No reference to RubyFrame escapes the callback scope
|
|
62
|
+
let ptr = NonNull::from(frame);
|
|
63
|
+
let static_ptr: NonNull<Frame<'static>> =
|
|
64
|
+
// SAFETY: Lifetime erasure is safe within the draw callback scope.
|
|
65
|
+
// The frame pointer remains valid for the entire callback duration.
|
|
66
|
+
unsafe { std::mem::transmute(ptr) };
|
|
67
|
+
|
|
68
|
+
Self {
|
|
69
|
+
inner: UnsafeCell::new(static_ptr),
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Returns the terminal area as a Ruby `RatatuiRuby::Rect`.
|
|
74
|
+
///
|
|
75
|
+
/// This mirrors `frame.area()` in Rust Ratatui.
|
|
76
|
+
pub fn area(&self) -> Result<Value, Error> {
|
|
77
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
78
|
+
|
|
79
|
+
// SAFETY: The frame pointer is valid for the duration of the draw callback.
|
|
80
|
+
// We only read from the frame, which is safe with an immutable reference.
|
|
81
|
+
let area = unsafe { (*self.inner.get()).as_ref().area() };
|
|
82
|
+
|
|
83
|
+
// Create a Ruby Rect object
|
|
84
|
+
let module = ruby.define_module("RatatuiRuby")?;
|
|
85
|
+
let class = module.const_get::<_, magnus::RClass>("Rect")?;
|
|
86
|
+
class.funcall("new", (area.x, area.y, area.width, area.height))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// Renders a widget at the specified area.
|
|
90
|
+
///
|
|
91
|
+
/// This mirrors `frame.render_widget(widget, area)` in Rust Ratatui.
|
|
92
|
+
///
|
|
93
|
+
/// # Arguments
|
|
94
|
+
///
|
|
95
|
+
/// * `widget` - A Ruby widget object (e.g., `RatatuiRuby::Paragraph`)
|
|
96
|
+
/// * `area` - A Ruby `Rect` or hash-like object with `x`, `y`, `width`, `height`
|
|
97
|
+
pub fn render_widget(&self, widget: Value, area: Value) -> Result<(), Error> {
|
|
98
|
+
// Parse the Ruby area into a Rust Rect
|
|
99
|
+
let x: u16 = area.funcall("x", ())?;
|
|
100
|
+
let y: u16 = area.funcall("y", ())?;
|
|
101
|
+
let width: u16 = area.funcall("width", ())?;
|
|
102
|
+
let height: u16 = area.funcall("height", ())?;
|
|
103
|
+
let rect = Rect::new(x, y, width, height);
|
|
104
|
+
|
|
105
|
+
// SAFETY: The frame pointer is valid for the duration of the draw callback.
|
|
106
|
+
// We take a mutable reference which is safe because:
|
|
107
|
+
// 1. RubyFrame is only used within Terminal::draw() callbacks
|
|
108
|
+
// 2. Ruby's GVL ensures single-threaded access
|
|
109
|
+
// 3. No other code holds a reference to the frame during this call
|
|
110
|
+
let frame = unsafe { (*self.inner.get()).as_mut() };
|
|
111
|
+
|
|
112
|
+
// Delegate to the existing render_node function
|
|
113
|
+
rendering::render_node(frame, rect, widget)
|
|
114
|
+
}
|
|
115
|
+
}
|
data/ext/ratatui_ruby/src/lib.rs
CHANGED
|
@@ -11,36 +11,83 @@
|
|
|
11
11
|
#![allow(clippy::module_name_repetitions)]
|
|
12
12
|
|
|
13
13
|
mod events;
|
|
14
|
+
mod frame;
|
|
14
15
|
mod rendering;
|
|
15
16
|
mod style;
|
|
16
17
|
mod terminal;
|
|
17
18
|
mod text;
|
|
18
19
|
mod widgets;
|
|
19
20
|
|
|
20
|
-
use
|
|
21
|
+
use frame::RubyFrame;
|
|
22
|
+
use magnus::{function, method, Error, Module, Object, Ruby, Value};
|
|
21
23
|
use terminal::{init_terminal, restore_terminal, TERMINAL};
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
/// Draw to the terminal.
|
|
26
|
+
///
|
|
27
|
+
/// Supports two calling conventions:
|
|
28
|
+
/// - Legacy: `RatatuiRuby.draw(tree)` - Renders a widget tree to the full terminal area
|
|
29
|
+
/// - New: `RatatuiRuby.draw { |frame| ... }` - Yields a Frame for explicit widget placement
|
|
30
|
+
fn draw(args: &[Value]) -> Result<(), Error> {
|
|
31
|
+
let ruby = Ruby::get().unwrap();
|
|
32
|
+
|
|
33
|
+
// Parse arguments: check for optional tree argument
|
|
34
|
+
let tree: Option<Value> = if args.is_empty() {
|
|
35
|
+
None
|
|
36
|
+
} else if args.len() == 1 {
|
|
37
|
+
Some(args[0])
|
|
38
|
+
} else {
|
|
39
|
+
return Err(Error::new(
|
|
40
|
+
ruby.exception_arg_error(),
|
|
41
|
+
format!(
|
|
42
|
+
"wrong number of arguments (given {}, expected 0..1)",
|
|
43
|
+
args.len()
|
|
44
|
+
),
|
|
45
|
+
));
|
|
46
|
+
};
|
|
47
|
+
let block_given = ruby.block_given();
|
|
48
|
+
|
|
49
|
+
// Validate: must have either tree or block, but not both
|
|
50
|
+
if tree.is_some() && block_given {
|
|
51
|
+
return Err(Error::new(
|
|
52
|
+
ruby.exception_arg_error(),
|
|
53
|
+
"Cannot provide both a tree and a block to draw",
|
|
54
|
+
));
|
|
55
|
+
}
|
|
56
|
+
if tree.is_none() && !block_given {
|
|
57
|
+
return Err(Error::new(
|
|
58
|
+
ruby.exception_arg_error(),
|
|
59
|
+
"Must provide either a tree or a block to draw",
|
|
60
|
+
));
|
|
61
|
+
}
|
|
62
|
+
|
|
25
63
|
let mut term_lock = TERMINAL.lock().unwrap();
|
|
26
|
-
let mut render_error = None;
|
|
64
|
+
let mut render_error: Option<Error> = None;
|
|
65
|
+
|
|
66
|
+
// Helper closure to execute the draw callback logic for either terminal type
|
|
67
|
+
let mut draw_callback = |f: &mut ratatui::Frame<'_>| {
|
|
68
|
+
if block_given {
|
|
69
|
+
// New API: yield RubyFrame to block
|
|
70
|
+
let ruby_frame = RubyFrame::new(f);
|
|
71
|
+
if let Err(e) = ruby.yield_value::<_, Value>(ruby_frame) {
|
|
72
|
+
render_error = Some(e);
|
|
73
|
+
}
|
|
74
|
+
} else if let Some(tree_value) = tree {
|
|
75
|
+
// Legacy API: render tree to full area
|
|
76
|
+
if let Err(e) = rendering::render_node(f, f.area(), tree_value) {
|
|
77
|
+
render_error = Some(e);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
27
82
|
if let Some(wrapper) = term_lock.as_mut() {
|
|
28
83
|
match wrapper {
|
|
29
84
|
terminal::TerminalWrapper::Crossterm(term) => {
|
|
30
|
-
term.draw(
|
|
31
|
-
|
|
32
|
-
render_error = Some(e);
|
|
33
|
-
}
|
|
34
|
-
})
|
|
35
|
-
.map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
|
|
85
|
+
term.draw(&mut draw_callback)
|
|
86
|
+
.map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
|
|
36
87
|
}
|
|
37
88
|
terminal::TerminalWrapper::Test(term) => {
|
|
38
|
-
term.draw(
|
|
39
|
-
|
|
40
|
-
render_error = Some(e);
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
.map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
|
|
89
|
+
term.draw(&mut draw_callback)
|
|
90
|
+
.map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?;
|
|
44
91
|
}
|
|
45
92
|
}
|
|
46
93
|
} else {
|
|
@@ -59,11 +106,14 @@ fn init() -> Result<(), Error> {
|
|
|
59
106
|
let ruby = magnus::Ruby::get().unwrap();
|
|
60
107
|
let m = ruby.define_module("RatatuiRuby")?;
|
|
61
108
|
|
|
62
|
-
|
|
63
|
-
|
|
64
109
|
m.define_module_function("_init_terminal", function!(init_terminal, 2))?;
|
|
65
110
|
m.define_module_function("restore_terminal", function!(restore_terminal, 0))?;
|
|
66
|
-
m.define_module_function("
|
|
111
|
+
m.define_module_function("_draw", function!(draw, -1))?;
|
|
112
|
+
|
|
113
|
+
// Register Frame class
|
|
114
|
+
let frame_class = m.define_class("Frame", ruby.class_object())?;
|
|
115
|
+
frame_class.define_method("area", method!(RubyFrame::area, 0))?;
|
|
116
|
+
frame_class.define_method("render_widget", method!(RubyFrame::render_widget, 2))?;
|
|
67
117
|
m.define_module_function("_poll_event", function!(events::poll_event, 0))?;
|
|
68
118
|
m.define_module_function("inject_test_event", function!(events::inject_test_event, 2))?;
|
|
69
119
|
m.define_module_function("clear_events", function!(events::clear_events, 0))?;
|
|
@@ -81,19 +131,22 @@ fn init() -> Result<(), Error> {
|
|
|
81
131
|
"get_cursor_position",
|
|
82
132
|
function!(terminal::get_cursor_position, 0),
|
|
83
133
|
)?;
|
|
84
|
-
m.define_module_function(
|
|
85
|
-
"_get_cell_at",
|
|
86
|
-
function!(terminal::get_cell_at, 2),
|
|
87
|
-
)?;
|
|
134
|
+
m.define_module_function("_get_cell_at", function!(terminal::get_cell_at, 2))?;
|
|
88
135
|
m.define_module_function("resize_terminal", function!(terminal::resize_terminal, 2))?;
|
|
89
136
|
|
|
90
137
|
// Register Layout.split on the Layout class
|
|
91
138
|
let layout_class = m.const_get::<_, magnus::RClass>("Layout")?;
|
|
92
139
|
layout_class.define_singleton_method("_split", function!(widgets::layout::split_layout, 4))?;
|
|
93
|
-
|
|
140
|
+
|
|
94
141
|
// Paragraph metrics
|
|
95
|
-
m.define_module_function(
|
|
96
|
-
|
|
142
|
+
m.define_module_function(
|
|
143
|
+
"_paragraph_line_count",
|
|
144
|
+
function!(widgets::paragraph::line_count, 2),
|
|
145
|
+
)?;
|
|
146
|
+
m.define_module_function(
|
|
147
|
+
"_paragraph_line_width",
|
|
148
|
+
function!(widgets::paragraph::line_width, 1),
|
|
149
|
+
)?;
|
|
97
150
|
|
|
98
151
|
// Tabs metrics
|
|
99
152
|
m.define_module_function("_tabs_width", function!(widgets::tabs::width, 1))?;
|
|
@@ -22,7 +22,8 @@ pub fn render_node(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Err
|
|
|
22
22
|
if let Some(arr) = RArray::from_value(commands) {
|
|
23
23
|
for i in 0..arr.len() {
|
|
24
24
|
let ruby = magnus::Ruby::get().unwrap();
|
|
25
|
-
let index = isize::try_from(i)
|
|
25
|
+
let index = isize::try_from(i)
|
|
26
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
26
27
|
let cmd: Value = arr.entry(index)?;
|
|
27
28
|
process_draw_command(frame.buffer_mut(), cmd)?;
|
|
28
29
|
}
|
|
@@ -54,8 +55,10 @@ pub fn render_node(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Err
|
|
|
54
55
|
"RatatuiRuby::Chart" | "RatatuiRuby::LineChart" => {
|
|
55
56
|
widgets::chart::render(frame, area, node)?;
|
|
56
57
|
}
|
|
57
|
-
"RatatuiRuby::RatatuiLogo" => widgets::ratatui_logo::render(frame, area, node)
|
|
58
|
-
"RatatuiRuby::RatatuiMascot" =>
|
|
58
|
+
"RatatuiRuby::RatatuiLogo" => widgets::ratatui_logo::render(frame, area, node),
|
|
59
|
+
"RatatuiRuby::RatatuiMascot" => {
|
|
60
|
+
widgets::ratatui_mascot::render_ratatui_mascot(frame, area, node)?;
|
|
61
|
+
}
|
|
59
62
|
_ => {}
|
|
60
63
|
}
|
|
61
64
|
Ok(())
|
|
@@ -105,7 +108,8 @@ fn process_draw_command(buffer: &mut Buffer, cmd: Value) -> Result<(), Error> {
|
|
|
105
108
|
|
|
106
109
|
if let Some(mods_array) = RArray::from_value(modifiers_val) {
|
|
107
110
|
for i in 0..mods_array.len() {
|
|
108
|
-
let index = isize::try_from(i)
|
|
111
|
+
let index = isize::try_from(i)
|
|
112
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
109
113
|
let mod_str: String = mods_array.entry::<String>(index)?;
|
|
110
114
|
if let Some(modifier) = parse_modifier_str(&mod_str) {
|
|
111
115
|
style = style.add_modifier(modifier);
|