ratatui_ruby 0.8.0 → 0.9.1
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 +2 -2
- data/.builds/ruby-3.3.yml +2 -2
- data/.builds/ruby-3.4.yml +2 -2
- data/.builds/ruby-4.0.0.yml +2 -2
- data/.pre-commit-config.yaml +1 -1
- data/AGENTS.md +3 -3
- data/CHANGELOG.md +77 -1
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/README.md +33 -5
- data/Rakefile +1 -1
- data/doc/concepts/application_architecture.md +44 -3
- data/doc/concepts/application_testing.md +43 -1
- data/doc/concepts/async.md +32 -2
- data/doc/concepts/custom_widgets.md +247 -0
- data/doc/concepts/event_handling.md +32 -3
- data/doc/concepts/interactive_design.md +32 -2
- data/doc/contributors/auditing/parity.md +7 -1
- data/doc/contributors/design/ruby_frontend.md +85 -1
- data/doc/contributors/design/rust_backend.md +67 -1
- data/doc/contributors/developing_examples.md +56 -2
- data/doc/contributors/documentation_style.md +20 -3
- data/doc/contributors/future_work.md +169 -0
- data/doc/contributors/index.md +1 -1
- data/doc/contributors/v1.0.0_blockers.md +19 -185
- data/doc/getting_started/quickstart.md +22 -4
- data/doc/getting_started/why.md +1 -1
- data/doc/index.md +2 -1
- data/doc/troubleshooting/debugging.md +32 -2
- data/doc/troubleshooting/terminal_limitations.md +8 -2
- data/doc/troubleshooting/tui_output.md +42 -0
- data/examples/app_all_events/README.md +14 -2
- data/examples/app_all_events/app.rb +1 -1
- data/examples/app_all_events/model/app_model.rb +1 -1
- data/examples/app_all_events/model/event_color_cycle.rb +1 -1
- data/examples/app_all_events/model/event_entry.rb +1 -1
- data/examples/app_all_events/model/msg.rb +1 -1
- data/examples/app_all_events/model/timestamp.rb +1 -1
- data/examples/app_all_events/update.rb +1 -1
- data/examples/app_all_events/view/app_view.rb +1 -1
- data/examples/app_all_events/view/controls_view.rb +1 -1
- data/examples/app_all_events/view/counts_view.rb +1 -1
- data/examples/app_all_events/view/live_view.rb +1 -1
- data/examples/app_all_events/view/log_view.rb +1 -1
- data/examples/app_all_events/view.rb +1 -1
- data/examples/app_color_picker/README.md +20 -2
- data/examples/app_color_picker/app.rb +1 -1
- data/examples/app_color_picker/clipboard.rb +1 -1
- data/examples/app_color_picker/color.rb +1 -1
- data/examples/app_color_picker/controls.rb +1 -1
- data/examples/app_color_picker/copy_dialog.rb +1 -1
- data/examples/app_color_picker/export_pane.rb +1 -1
- data/examples/app_color_picker/harmony.rb +1 -1
- data/examples/app_color_picker/input.rb +1 -1
- data/examples/app_color_picker/main_container.rb +1 -1
- data/examples/app_color_picker/palette.rb +1 -1
- data/examples/app_login_form/README.md +8 -2
- data/examples/app_login_form/app.rb +1 -1
- data/examples/app_stateful_interaction/README.md +2 -2
- data/examples/app_stateful_interaction/app.rb +71 -17
- data/examples/timeout_demo.rb +1 -1
- data/examples/verify_quickstart_dsl/README.md +6 -0
- data/examples/verify_quickstart_dsl/app.rb +3 -3
- data/examples/verify_quickstart_layout/README.md +6 -0
- data/examples/verify_quickstart_layout/app.rb +3 -3
- data/examples/verify_quickstart_lifecycle/README.md +6 -0
- data/examples/verify_quickstart_lifecycle/app.rb +3 -3
- data/examples/verify_readme_usage/README.md +6 -0
- data/examples/verify_readme_usage/app.rb +3 -3
- data/examples/widget_barchart/README.md +6 -0
- data/examples/widget_barchart/app.rb +2 -2
- data/examples/widget_block/README.md +7 -1
- data/examples/widget_block/app.rb +2 -2
- data/examples/widget_box/README.md +6 -0
- data/examples/widget_box/app.rb +9 -6
- data/examples/widget_calendar/README.md +6 -0
- data/examples/widget_calendar/app.rb +2 -2
- data/examples/widget_canvas/README.md +4 -0
- data/examples/widget_canvas/app.rb +2 -2
- data/examples/widget_cell/README.md +6 -0
- data/examples/widget_cell/app.rb +2 -3
- data/examples/widget_center/README.md +4 -0
- data/examples/widget_center/app.rb +2 -2
- data/examples/widget_chart/README.md +6 -0
- data/examples/widget_chart/app.rb +2 -2
- data/examples/widget_gauge/README.md +6 -0
- data/examples/widget_gauge/app.rb +2 -2
- data/examples/widget_layout_split/README.md +6 -0
- data/examples/widget_layout_split/app.rb +9 -3
- data/examples/widget_line_gauge/README.md +6 -0
- data/examples/widget_line_gauge/app.rb +2 -2
- data/examples/widget_list/README.md +6 -0
- data/examples/widget_list/app.rb +2 -2
- data/examples/widget_map/README.md +8 -2
- data/examples/widget_map/app.rb +2 -2
- data/examples/widget_overlay/README.md +7 -1
- data/examples/widget_overlay/app.rb +2 -2
- data/examples/widget_popup/README.md +6 -0
- data/examples/widget_popup/app.rb +2 -2
- data/examples/widget_ratatui_logo/README.md +6 -0
- data/examples/widget_ratatui_logo/app.rb +2 -3
- data/examples/widget_ratatui_mascot/README.md +6 -0
- data/examples/widget_ratatui_mascot/app.rb +2 -2
- data/examples/widget_rect/README.md +12 -0
- data/examples/widget_rect/app.rb +40 -26
- data/examples/widget_render/README.md +6 -0
- data/examples/widget_render/app.rb +2 -2
- data/examples/widget_render/app.rbs +41 -0
- data/examples/widget_rich_text/README.md +6 -0
- data/examples/widget_rich_text/app.rb +2 -2
- data/examples/widget_scroll_text/README.md +6 -0
- data/examples/widget_scroll_text/app.rb +2 -2
- data/examples/widget_scrollbar/README.md +6 -0
- data/examples/widget_scrollbar/app.rb +2 -2
- data/examples/widget_sparkline/README.md +6 -0
- data/examples/widget_sparkline/app.rb +2 -2
- data/examples/widget_style_colors/README.md +6 -0
- data/examples/widget_style_colors/app.rb +2 -2
- data/examples/widget_table/README.md +8 -2
- data/examples/widget_table/app.rb +2 -2
- data/examples/widget_tabs/README.md +6 -0
- data/examples/widget_tabs/app.rb +2 -2
- data/examples/widget_text_width/README.md +6 -0
- data/examples/widget_text_width/app.rb +4 -4
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/extconf.rb +2 -2
- data/ext/ratatui_ruby/src/events.rs +1 -0
- data/ext/ratatui_ruby/src/rendering.rs +1 -1
- data/ext/ratatui_ruby/src/style.rs +0 -8
- data/ext/ratatui_ruby/src/widgets/chart.rs +0 -118
- data/ext/ratatui_ruby/src/widgets/list_state.rs +36 -0
- data/lib/ratatui_ruby/buffer/cell.rb +34 -2
- data/lib/ratatui_ruby/buffer.rb +2 -2
- data/lib/ratatui_ruby/cell.rb +34 -2
- data/lib/ratatui_ruby/event/focus_gained.rb +26 -2
- data/lib/ratatui_ruby/event/focus_lost.rb +26 -2
- data/lib/ratatui_ruby/event/key/character.rb +18 -2
- data/lib/ratatui_ruby/event/key/media.rb +2 -2
- data/lib/ratatui_ruby/event/key/modifier.rb +10 -2
- data/lib/ratatui_ruby/event/key/navigation.rb +2 -2
- data/lib/ratatui_ruby/event/key/system.rb +2 -2
- data/lib/ratatui_ruby/event/key.rb +114 -2
- data/lib/ratatui_ruby/event/mouse.rb +42 -2
- data/lib/ratatui_ruby/event/none.rb +10 -2
- data/lib/ratatui_ruby/event/paste.rb +34 -2
- data/lib/ratatui_ruby/event/resize.rb +34 -2
- data/lib/ratatui_ruby/event/sync.rb +52 -0
- data/lib/ratatui_ruby/event.rb +32 -2
- data/lib/ratatui_ruby/frame.rb +74 -2
- data/lib/ratatui_ruby/layout/constraint.rb +193 -2
- data/lib/ratatui_ruby/layout/layout.rb +47 -2
- data/lib/ratatui_ruby/layout/rect.rb +403 -2
- data/lib/ratatui_ruby/layout.rb +2 -2
- data/lib/ratatui_ruby/list_state.rb +113 -2
- data/lib/ratatui_ruby/output_guard.rb +26 -3
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -2
- data/lib/ratatui_ruby/schema/bar_chart.rb +50 -2
- data/lib/ratatui_ruby/schema/block.rb +21 -15
- data/lib/ratatui_ruby/schema/calendar.rb +2 -2
- data/lib/ratatui_ruby/schema/canvas.rb +10 -2
- data/lib/ratatui_ruby/schema/center.rb +10 -2
- data/lib/ratatui_ruby/schema/chart.rb +2 -28
- data/lib/ratatui_ruby/schema/clear.rb +10 -2
- data/lib/ratatui_ruby/schema/constraint.rb +58 -2
- data/lib/ratatui_ruby/schema/cursor.rb +10 -2
- data/lib/ratatui_ruby/schema/draw.rb +10 -2
- data/lib/ratatui_ruby/schema/gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/layout.rb +18 -2
- data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/list.rb +10 -2
- data/lib/ratatui_ruby/schema/list_item.rb +10 -2
- data/lib/ratatui_ruby/schema/overlay.rb +10 -2
- data/lib/ratatui_ruby/schema/paragraph.rb +10 -2
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +2 -2
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +2 -2
- data/lib/ratatui_ruby/schema/rect.rb +58 -2
- data/lib/ratatui_ruby/schema/row.rb +10 -2
- data/lib/ratatui_ruby/schema/scrollbar.rb +2 -2
- data/lib/ratatui_ruby/schema/shape/label.rb +10 -2
- data/lib/ratatui_ruby/schema/sparkline.rb +10 -2
- data/lib/ratatui_ruby/schema/style.rb +18 -2
- data/lib/ratatui_ruby/schema/table.rb +2 -2
- data/lib/ratatui_ruby/schema/tabs.rb +2 -2
- data/lib/ratatui_ruby/schema/text.rb +34 -2
- data/lib/ratatui_ruby/scrollbar_state.rb +10 -2
- data/lib/ratatui_ruby/style/style.rb +18 -2
- data/lib/ratatui_ruby/style.rb +2 -2
- data/lib/ratatui_ruby/synthetic_events.rb +86 -0
- data/lib/ratatui_ruby/table_state.rb +10 -2
- data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
- data/lib/ratatui_ruby/test_helper/event_injection.rb +62 -2
- data/lib/ratatui_ruby/test_helper/snapshot.rb +74 -9
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +98 -2
- data/lib/ratatui_ruby/test_helper/terminal.rb +50 -2
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +18 -2
- data/lib/ratatui_ruby/test_helper.rb +10 -2
- data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/core.rb +2 -2
- data/lib/ratatui_ruby/tui/layout_factories.rb +32 -2
- data/lib/ratatui_ruby/tui/state_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/style_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/text_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/widget_factories.rb +2 -2
- data/lib/ratatui_ruby/tui.rb +11 -3
- data/lib/ratatui_ruby/version.rb +3 -3
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -2
- data/lib/ratatui_ruby/widgets/bar_chart.rb +58 -2
- data/lib/ratatui_ruby/widgets/block.rb +37 -15
- data/lib/ratatui_ruby/widgets/calendar.rb +2 -2
- data/lib/ratatui_ruby/widgets/canvas.rb +10 -2
- data/lib/ratatui_ruby/widgets/cell.rb +10 -2
- data/lib/ratatui_ruby/widgets/center.rb +10 -2
- data/lib/ratatui_ruby/widgets/chart.rb +2 -28
- data/lib/ratatui_ruby/widgets/clear.rb +10 -2
- data/lib/ratatui_ruby/widgets/cursor.rb +10 -2
- data/lib/ratatui_ruby/widgets/gauge.rb +16 -2
- data/lib/ratatui_ruby/widgets/line_gauge.rb +16 -2
- data/lib/ratatui_ruby/widgets/list.rb +41 -2
- data/lib/ratatui_ruby/widgets/list_item.rb +10 -2
- data/lib/ratatui_ruby/widgets/overlay.rb +10 -2
- data/lib/ratatui_ruby/widgets/paragraph.rb +10 -2
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -2
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -2
- data/lib/ratatui_ruby/widgets/row.rb +10 -2
- data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -2
- data/lib/ratatui_ruby/widgets/shape/label.rb +10 -2
- data/lib/ratatui_ruby/widgets/sparkline.rb +10 -2
- data/lib/ratatui_ruby/widgets/table.rb +62 -2
- data/lib/ratatui_ruby/widgets/tabs.rb +2 -2
- data/lib/ratatui_ruby/widgets.rb +2 -2
- data/lib/ratatui_ruby.rb +101 -9
- data/sig/examples/app_all_events/view.rbs +7 -1
- data/sig/examples/app_all_events/view_state.rbs +7 -1
- data/sig/examples/app_color_picker/app.rbs +5 -0
- data/sig/examples/app_stateful_interaction/app.rbs +7 -1
- data/sig/examples/verify_quickstart_dsl/app.rbs +7 -1
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +7 -1
- data/sig/examples/verify_readme_usage/app.rbs +7 -1
- data/sig/examples/widget_block_demo/app.rbs +6 -0
- data/sig/examples/widget_box_demo/app.rbs +7 -1
- data/sig/examples/widget_calendar_demo/app.rbs +7 -1
- data/sig/examples/widget_cell_demo/app.rbs +7 -1
- data/sig/examples/widget_chart_demo/app.rbs +7 -1
- data/sig/examples/widget_gauge_demo/app.rbs +7 -1
- data/sig/examples/widget_layout_split/app.rbs +7 -1
- data/sig/examples/widget_line_gauge_demo/app.rbs +7 -1
- data/sig/examples/widget_list_demo/app.rbs +5 -0
- data/sig/examples/widget_map_demo/app.rbs +7 -1
- data/sig/examples/widget_popup_demo/app.rbs +7 -1
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +7 -1
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +7 -1
- data/sig/examples/widget_rect/app.rbs +7 -1
- data/sig/examples/widget_render/app.rbs +7 -1
- data/sig/examples/widget_rich_text/app.rbs +7 -1
- data/sig/examples/widget_scroll_text/app.rbs +7 -1
- data/sig/examples/widget_scrollbar_demo/app.rbs +7 -1
- data/sig/examples/widget_sparkline_demo/app.rbs +7 -1
- data/sig/examples/widget_style_colors/app.rbs +7 -1
- data/sig/examples/widget_table_demo/app.rbs +7 -1
- data/sig/examples/widget_text_width/app.rbs +7 -1
- data/sig/ratatui_ruby/event.rbs +7 -1
- data/sig/ratatui_ruby/frame.rbs +15 -3
- data/sig/ratatui_ruby/list_state.rbs +11 -1
- data/sig/ratatui_ruby/ratatui_ruby.rbs +8 -2
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +7 -1
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +6 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +6 -0
- data/sig/ratatui_ruby/schema/block.rbs +7 -1
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -0
- data/sig/ratatui_ruby/schema/canvas.rbs +6 -0
- data/sig/ratatui_ruby/schema/center.rbs +6 -0
- data/sig/ratatui_ruby/schema/chart.rbs +6 -9
- data/sig/ratatui_ruby/schema/constraint.rbs +14 -0
- data/sig/ratatui_ruby/schema/cursor.rbs +6 -0
- data/sig/ratatui_ruby/schema/draw.rbs +6 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +9 -1
- data/sig/ratatui_ruby/schema/layout.rbs +6 -0
- data/sig/ratatui_ruby/schema/line_gauge.rbs +9 -1
- data/sig/ratatui_ruby/schema/list.rbs +9 -1
- data/sig/ratatui_ruby/schema/list_item.rbs +7 -1
- data/sig/ratatui_ruby/schema/overlay.rbs +6 -0
- data/sig/ratatui_ruby/schema/paragraph.rbs +6 -0
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +6 -0
- data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +5 -0
- data/sig/ratatui_ruby/schema/rect.rbs +30 -0
- data/sig/ratatui_ruby/schema/row.rbs +7 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +6 -0
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -0
- data/sig/ratatui_ruby/schema/style.rbs +7 -1
- data/sig/ratatui_ruby/schema/table.rbs +11 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +6 -0
- data/sig/ratatui_ruby/schema/text.rbs +7 -1
- data/sig/ratatui_ruby/scrollbar_state.rbs +7 -1
- data/sig/ratatui_ruby/session.rbs +7 -1
- data/sig/ratatui_ruby/table_state.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/terminal.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -1
- data/sig/ratatui_ruby/test_helper.rbs +7 -1
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/core.rbs +7 -1
- data/sig/ratatui_ruby/tui/layout_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/state_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/style_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/text_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/widget_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui.rbs +7 -1
- data/sig/ratatui_ruby/version.rbs +6 -0
- data/tasks/autodoc/examples.rb +1 -1
- data/tasks/autodoc/member.rb +1 -1
- data/tasks/autodoc/name.rb +1 -1
- data/tasks/bump/cargo_lockfile.rb +1 -1
- data/tasks/bump/changelog.rb +1 -1
- data/tasks/bump/header.rb +1 -1
- data/tasks/bump/history.rb +1 -1
- data/tasks/bump/links.rb +1 -1
- data/tasks/bump/manifest.rb +1 -1
- data/tasks/bump/ruby_gem.rb +1 -1
- data/tasks/bump/sem_ver.rb +1 -1
- data/tasks/bump/unreleased_section.rb +1 -1
- data/tasks/license/headers_md.rb +223 -0
- data/tasks/license/headers_rb.rb +210 -0
- data/tasks/license/license_utils.rb +130 -0
- data/tasks/license/snippets_md.rb +315 -0
- data/tasks/license/snippets_rdoc.rb +150 -0
- data/tasks/license.rake +91 -0
- data/tasks/rdoc_config.rb +1 -1
- data/tasks/resources/build.yml.erb +13 -7
- data/tasks/sourcehut.rake +3 -1
- data/tasks/terminal_preview/app_screenshot.rb +1 -1
- data/tasks/terminal_preview/crash_report.rb +1 -1
- data/tasks/terminal_preview/example_app.rb +1 -1
- data/tasks/terminal_preview/launcher_script.rb +1 -1
- data/tasks/terminal_preview/preview_collection.rb +1 -1
- data/tasks/terminal_preview/preview_timing.rb +1 -1
- data/tasks/terminal_preview/safety_confirmation.rb +1 -1
- data/tasks/terminal_preview/saved_screenshot.rb +1 -1
- data/tasks/terminal_preview/system_appearance.rb +1 -1
- data/tasks/terminal_preview/terminal_window.rb +1 -1
- data/tasks/terminal_preview/window_id.rb +1 -1
- data/tasks/website/index_page.rb +1 -1
- data/tasks/website/version.rb +1 -1
- data/tasks/website/version_menu.rb +1 -1
- data/tasks/website/versioned_documentation.rb +1 -1
- data/tasks/website/website.rb +1 -1
- metadata +15 -3
- data/doc/migration/v0_7_0.md +0 -236
data/examples/widget_rect/app.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -14,9 +14,11 @@ require "ratatui_ruby"
|
|
|
14
14
|
#
|
|
15
15
|
# Rect is the fundamental geometry primitive for TUI layout. This example shows:
|
|
16
16
|
# - Rect attributes: x, y, width, height
|
|
17
|
+
# - Edge accessors: left, right, top, bottom
|
|
18
|
+
# - Geometry methods: area, empty?, union, inner, offset, clamp
|
|
19
|
+
# - Iterators: rows, columns, positions
|
|
17
20
|
# - Rect#contains? for hit testing mouse clicks
|
|
18
21
|
# - Layout.split returning cached rects for reuse
|
|
19
|
-
# - The layout caching pattern: compute in draw, reuse in handle_input
|
|
20
22
|
#
|
|
21
23
|
# Controls:
|
|
22
24
|
# ←/→: Adjust sidebar width
|
|
@@ -57,7 +59,7 @@ class WidgetRect
|
|
|
57
59
|
direction: :vertical,
|
|
58
60
|
constraints: [
|
|
59
61
|
@tui.constraint_fill(1),
|
|
60
|
-
@tui.constraint_length(
|
|
62
|
+
@tui.constraint_length(5),
|
|
61
63
|
]
|
|
62
64
|
)
|
|
63
65
|
|
|
@@ -88,21 +90,41 @@ class WidgetRect
|
|
|
88
90
|
end
|
|
89
91
|
|
|
90
92
|
private def render_content(frame)
|
|
93
|
+
r = @content_rect
|
|
94
|
+
inner_r = r.inner(2)
|
|
95
|
+
offset_r = r.offset(3, 2)
|
|
96
|
+
bounds = RatatuiRuby::Layout::Rect.new(x: 0, y: 0, width: 50, height: 20)
|
|
97
|
+
clamped = r.clamp(bounds)
|
|
98
|
+
union_r = r.union(@sidebar_rect)
|
|
99
|
+
|
|
91
100
|
text_content = [
|
|
92
101
|
@tui.text_line(spans: [
|
|
93
102
|
@tui.text_span(content: "Active View: ", style: @label_style),
|
|
94
103
|
@tui.text_span(content: MENU_ITEMS[@selected_index], style: @tui.style(fg: :green)),
|
|
95
104
|
]),
|
|
96
|
-
"",
|
|
97
105
|
@tui.text_line(spans: [
|
|
98
106
|
@tui.text_span(content: "Rect Attributes ", style: @label_style),
|
|
99
107
|
@tui.text_span(content: "(from Layout.split):", style: @dim_style),
|
|
100
108
|
]),
|
|
101
|
-
"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"",
|
|
109
|
+
" x:#{r.x} y:#{r.y} width:#{r.width} height:#{r.height}",
|
|
110
|
+
@tui.text_line(spans: [
|
|
111
|
+
@tui.text_span(content: "Edge Accessors:", style: @label_style),
|
|
112
|
+
]),
|
|
113
|
+
" left:#{r.left} right:#{r.right} top:#{r.top} bottom:#{r.bottom}",
|
|
114
|
+
@tui.text_line(spans: [
|
|
115
|
+
@tui.text_span(content: "Size Methods:", style: @label_style),
|
|
116
|
+
]),
|
|
117
|
+
" area:#{r.area} empty?:#{r.empty?}",
|
|
118
|
+
@tui.text_line(spans: [
|
|
119
|
+
@tui.text_span(content: "Geometry Transformations:", style: @label_style),
|
|
120
|
+
]),
|
|
121
|
+
" inner(2): x:#{inner_r.x} y:#{inner_r.y} w:#{inner_r.width} h:#{inner_r.height}",
|
|
122
|
+
" offset(3,2): x:#{offset_r.x} y:#{offset_r.y} clamp: x:#{clamped.x} y:#{clamped.y}",
|
|
123
|
+
" union(sidebar): w:#{union_r.width} h:#{union_r.height}",
|
|
124
|
+
@tui.text_line(spans: [
|
|
125
|
+
@tui.text_span(content: "Iterators:", style: @label_style),
|
|
126
|
+
]),
|
|
127
|
+
" rows:#{r.height} columns:#{r.width} positions:#{r.area}",
|
|
106
128
|
@tui.text_line(spans: [
|
|
107
129
|
@tui.text_span(content: "Hit Testing ", style: @label_style),
|
|
108
130
|
@tui.text_span(content: "(Rect#contains?):", style: @dim_style),
|
|
@@ -125,27 +147,19 @@ class WidgetRect
|
|
|
125
147
|
@tui.paragraph(
|
|
126
148
|
text: [
|
|
127
149
|
@tui.text_line(spans: [
|
|
128
|
-
@tui.text_span(content: "LAYOUT", style: @label_style),
|
|
129
|
-
@tui.text_span(content: " "),
|
|
130
150
|
@tui.text_span(content: "←", style: @hotkey_style),
|
|
131
|
-
@tui.text_span(content: "
|
|
151
|
+
@tui.text_span(content: "/"),
|
|
132
152
|
@tui.text_span(content: "→", style: @hotkey_style),
|
|
133
|
-
@tui.text_span(content: ":
|
|
134
|
-
@tui.text_span(content: "
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
@tui.text_span(content: "
|
|
138
|
-
@tui.text_span(content: "
|
|
139
|
-
@tui.text_span(content: "
|
|
140
|
-
@tui.text_span(content: ": Select menu item "),
|
|
153
|
+
@tui.text_span(content: ": Sidebar width "),
|
|
154
|
+
@tui.text_span(content: "↑", style: @hotkey_style),
|
|
155
|
+
@tui.text_span(content: "/"),
|
|
156
|
+
@tui.text_span(content: "↓", style: @hotkey_style),
|
|
157
|
+
@tui.text_span(content: ": Menu selection "),
|
|
158
|
+
@tui.text_span(content: "Click", style: @hotkey_style),
|
|
159
|
+
@tui.text_span(content: ": Hit test "),
|
|
141
160
|
@tui.text_span(content: "q", style: @hotkey_style),
|
|
142
161
|
@tui.text_span(content: ": Quit"),
|
|
143
162
|
]),
|
|
144
|
-
"",
|
|
145
|
-
@tui.text_line(spans: [
|
|
146
|
-
@tui.text_span(content: "HIT TESTING", style: @label_style),
|
|
147
|
-
@tui.text_span(content: " Click any panel → Rect#contains?(x, y) determines which rect was hit."),
|
|
148
|
-
]),
|
|
149
163
|
]
|
|
150
164
|
),
|
|
151
165
|
]
|
|
@@ -25,9 +25,15 @@ Sometimes standard widgets aren't enough. You need to draw custom shapes, games,
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
+
<!-- SPDX-SnippetBegin -->
|
|
29
|
+
<!--
|
|
30
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
31
|
+
SPDX-License-Identifier: MIT-0
|
|
32
|
+
-->
|
|
28
33
|
```bash
|
|
29
34
|
ruby examples/widget_render/app.rb
|
|
30
35
|
```
|
|
36
|
+
<!-- SPDX-SnippetEnd -->
|
|
31
37
|
|
|
32
38
|
## Learning Outcomes
|
|
33
39
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT-0
|
|
4
|
+
|
|
5
|
+
class DiagonalWidget
|
|
6
|
+
@widget_index: Integer
|
|
7
|
+
@widgets: Array[{ name: String, widget: (DiagonalWidget | CheckerboardWidget | BorderWidget) }]
|
|
8
|
+
@tui: RatatuiRuby::TUI
|
|
9
|
+
|
|
10
|
+
def run: () -> void
|
|
11
|
+
|
|
12
|
+
private def render: () -> void
|
|
13
|
+
private def handle_input: () -> Symbol?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class DiagonalWidget
|
|
17
|
+
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class CheckerboardWidget
|
|
21
|
+
@char: String
|
|
22
|
+
|
|
23
|
+
def initialize: (?String char) -> void
|
|
24
|
+
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class BorderWidget
|
|
28
|
+
def render: (RatatuiRuby::Rect area) -> Array[RatatuiRuby::Draw::StringCmd | RatatuiRuby::Draw::CellCmd]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class WidgetRender
|
|
32
|
+
@widget_index: Integer
|
|
33
|
+
@widgets: Array[{ name: String, widget: (DiagonalWidget | CheckerboardWidget | BorderWidget) }]
|
|
34
|
+
@tui: RatatuiRuby::TUI
|
|
35
|
+
|
|
36
|
+
def initialize: () -> void
|
|
37
|
+
def run: () -> void
|
|
38
|
+
|
|
39
|
+
private def render: () -> void
|
|
40
|
+
private def handle_input: () -> Symbol?
|
|
41
|
+
end
|
|
@@ -23,9 +23,15 @@ Standard strings are monochromatic. "Rich Text" is composed of `Lines` containin
|
|
|
23
23
|
|
|
24
24
|
## Usage
|
|
25
25
|
|
|
26
|
+
<!-- SPDX-SnippetBegin -->
|
|
27
|
+
<!--
|
|
28
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
29
|
+
SPDX-License-Identifier: MIT-0
|
|
30
|
+
-->
|
|
26
31
|
```bash
|
|
27
32
|
ruby examples/widget_rich_text/app.rb
|
|
28
33
|
```
|
|
34
|
+
<!-- SPDX-SnippetEnd -->
|
|
29
35
|
|
|
30
36
|
## Learning Outcomes
|
|
31
37
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -25,9 +25,15 @@ Sometimes text exceeds the available space. The `Paragraph` widget supports a `s
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
+
<!-- SPDX-SnippetBegin -->
|
|
29
|
+
<!--
|
|
30
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
31
|
+
SPDX-License-Identifier: MIT-0
|
|
32
|
+
-->
|
|
28
33
|
```bash
|
|
29
34
|
ruby examples/widget_scroll_text/app.rb
|
|
30
35
|
```
|
|
36
|
+
<!-- SPDX-SnippetEnd -->
|
|
31
37
|
|
|
32
38
|
## Learning Outcomes
|
|
33
39
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -26,9 +26,15 @@ Content overflows. Users get lost in long lists. Scrollbars provide essential sp
|
|
|
26
26
|
|
|
27
27
|
## Usage
|
|
28
28
|
|
|
29
|
+
<!-- SPDX-SnippetBegin -->
|
|
30
|
+
<!--
|
|
31
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
32
|
+
SPDX-License-Identifier: MIT-0
|
|
33
|
+
-->
|
|
29
34
|
```bash
|
|
30
35
|
ruby examples/widget_scrollbar/app.rb
|
|
31
36
|
```
|
|
37
|
+
<!-- SPDX-SnippetEnd -->
|
|
32
38
|
|
|
33
39
|
## Learning Outcomes
|
|
34
40
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -30,9 +30,15 @@ Users need context. A single number ("90% CPU") tells you status, but not the tr
|
|
|
30
30
|
|
|
31
31
|
## Usage
|
|
32
32
|
|
|
33
|
+
<!-- SPDX-SnippetBegin -->
|
|
34
|
+
<!--
|
|
35
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
36
|
+
SPDX-License-Identifier: MIT-0
|
|
37
|
+
-->
|
|
33
38
|
```bash
|
|
34
39
|
ruby examples/widget_sparkline/app.rb
|
|
35
40
|
```
|
|
41
|
+
<!-- SPDX-SnippetEnd -->
|
|
36
42
|
|
|
37
43
|
## Learning Outcomes
|
|
38
44
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -22,9 +22,15 @@ Terminals support millions of colors. This example generates a mathematically pr
|
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
|
+
<!-- SPDX-SnippetBegin -->
|
|
26
|
+
<!--
|
|
27
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
28
|
+
SPDX-License-Identifier: MIT-0
|
|
29
|
+
-->
|
|
25
30
|
```bash
|
|
26
31
|
ruby examples/widget_style_colors/app.rb
|
|
27
32
|
```
|
|
33
|
+
<!-- SPDX-SnippetEnd -->
|
|
28
34
|
|
|
29
35
|
## Learning Outcomes
|
|
30
36
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText:
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
# Table (Row, Cell) Example
|
|
@@ -36,9 +36,15 @@ Data grids are complex. Users expect to navigate them with keys, select rows, an
|
|
|
36
36
|
|
|
37
37
|
## Usage
|
|
38
38
|
|
|
39
|
+
<!-- SPDX-SnippetBegin -->
|
|
40
|
+
<!--
|
|
41
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
42
|
+
SPDX-License-Identifier: MIT-0
|
|
43
|
+
-->
|
|
39
44
|
```bash
|
|
40
45
|
ruby examples/widget_table/app.rb
|
|
41
46
|
```
|
|
47
|
+
<!-- SPDX-SnippetEnd -->
|
|
42
48
|
|
|
43
49
|
## Learning Outcomes
|
|
44
50
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -29,9 +29,15 @@ Screen real estate is limited. You cannot show everything at once. Tabs segregat
|
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
32
|
+
<!-- SPDX-SnippetBegin -->
|
|
33
|
+
<!--
|
|
34
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
35
|
+
SPDX-License-Identifier: MIT-0
|
|
36
|
+
-->
|
|
32
37
|
```bash
|
|
33
38
|
ruby examples/widget_tabs/app.rb
|
|
34
39
|
```
|
|
40
|
+
<!-- SPDX-SnippetEnd -->
|
|
35
41
|
|
|
36
42
|
## Learning Outcomes
|
|
37
43
|
|
data/examples/widget_tabs/app.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -23,9 +23,15 @@ Not all characters are created equal. In a TUI, "Width" means cell count, not st
|
|
|
23
23
|
|
|
24
24
|
## Usage
|
|
25
25
|
|
|
26
|
+
<!-- SPDX-SnippetBegin -->
|
|
27
|
+
<!--
|
|
28
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long
|
|
29
|
+
SPDX-License-Identifier: MIT-0
|
|
30
|
+
-->
|
|
26
31
|
```bash
|
|
27
32
|
ruby examples/widget_text_width/app.rb
|
|
28
33
|
```
|
|
34
|
+
<!-- SPDX-SnippetEnd -->
|
|
29
35
|
|
|
30
36
|
## Learning Outcomes
|
|
31
37
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: MIT-0
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
@@ -73,7 +73,7 @@ class WidgetTextWidth
|
|
|
73
73
|
block: @tui.block(
|
|
74
74
|
title: "Text Width Calculator",
|
|
75
75
|
borders: [:all],
|
|
76
|
-
|
|
76
|
+
border_style: { fg: "cyan" }
|
|
77
77
|
),
|
|
78
78
|
alignment: :left
|
|
79
79
|
)
|
|
@@ -88,7 +88,7 @@ class WidgetTextWidth
|
|
|
88
88
|
|
|
89
89
|
widget = @tui.paragraph(
|
|
90
90
|
text:,
|
|
91
|
-
block: @tui.block(borders: [:top],
|
|
91
|
+
block: @tui.block(borders: [:top], border_style: { fg: "gray" }),
|
|
92
92
|
alignment: :center
|
|
93
93
|
)
|
|
94
94
|
|
data/ext/ratatui_ruby/Cargo.lock
CHANGED
data/ext/ratatui_ruby/Cargo.toml
CHANGED
data/ext/ratatui_ruby/extconf.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
|
-
# SPDX-FileCopyrightText:
|
|
5
|
-
# SPDX-License-Identifier:
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
require "mkmf"
|
|
@@ -383,6 +383,7 @@ fn handle_key_event(key: ratatui::crossterm::event::KeyEvent) -> Result<Value, E
|
|
|
383
383
|
if key.kind != ratatui::crossterm::event::KeyEventKind::Press {
|
|
384
384
|
return Ok(ruby.qnil().into_value_with(&ruby));
|
|
385
385
|
}
|
|
386
|
+
|
|
386
387
|
let hash = ruby.hash_new();
|
|
387
388
|
hash.aset(ruby.to_symbol("type"), ruby.to_symbol("key"))?;
|
|
388
389
|
|
|
@@ -53,7 +53,7 @@ pub fn render_node(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Err
|
|
|
53
53
|
"RatatuiRuby::Widgets::Canvas" => widgets::canvas::render(frame, area, node)?,
|
|
54
54
|
"RatatuiRuby::Widgets::Calendar" => widgets::calendar::render(frame, area, node)?,
|
|
55
55
|
"RatatuiRuby::Widgets::Sparkline" => widgets::sparkline::render(frame, area, node)?,
|
|
56
|
-
"RatatuiRuby::Widgets::Chart"
|
|
56
|
+
"RatatuiRuby::Widgets::Chart" => {
|
|
57
57
|
widgets::chart::render(frame, area, node)?;
|
|
58
58
|
}
|
|
59
59
|
"RatatuiRuby::Widgets::RatatuiLogo" => widgets::ratatui_logo::render(frame, area, node),
|
|
@@ -398,14 +398,6 @@ fn parse_borders<'a>(
|
|
|
398
398
|
if let Ok(v) = block_val.funcall::<&str, _, Value>("border_style", ()) {
|
|
399
399
|
if !v.is_nil() {
|
|
400
400
|
block = block.border_style(parse_style(v)?);
|
|
401
|
-
} else if let Ok(color_val) = block_val.funcall::<&str, _, Value>("border_color", ()) {
|
|
402
|
-
if !color_val.is_nil() {
|
|
403
|
-
if let Ok(s) = color_val.funcall::<&str, _, String>("to_s", ()) {
|
|
404
|
-
if let Some(c) = parse_color(&s) {
|
|
405
|
-
block = block.border_style(Style::default().fg(c));
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
401
|
}
|
|
410
402
|
}
|
|
411
403
|
|
|
@@ -8,7 +8,6 @@ use magnus::{prelude::*, Error, Symbol, Value};
|
|
|
8
8
|
use ratatui::{
|
|
9
9
|
layout::Rect,
|
|
10
10
|
symbols,
|
|
11
|
-
text::Span,
|
|
12
11
|
widgets::{Axis, Chart, Dataset, GraphType, LegendPosition},
|
|
13
12
|
Frame,
|
|
14
13
|
};
|
|
@@ -17,12 +16,6 @@ use ratatui::{
|
|
|
17
16
|
pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
18
17
|
let bump = Bump::new();
|
|
19
18
|
let ruby = magnus::Ruby::get().unwrap();
|
|
20
|
-
// SAFETY: Immediate conversion to owned string avoids GC-unsafe borrowed reference.
|
|
21
|
-
let class_name = unsafe { node.class().name() }.into_owned();
|
|
22
|
-
|
|
23
|
-
if class_name == "RatatuiRuby::LineChart" {
|
|
24
|
-
return render_line_chart(frame, area, node);
|
|
25
|
-
}
|
|
26
19
|
|
|
27
20
|
let datasets_val: magnus::RArray = node.funcall("datasets", ())?;
|
|
28
21
|
let x_axis_val: Value = node.funcall("x_axis", ())?;
|
|
@@ -194,117 +187,6 @@ fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
|
|
|
194
187
|
Ok(axis)
|
|
195
188
|
}
|
|
196
189
|
|
|
197
|
-
fn render_line_chart(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
198
|
-
let bump = Bump::new();
|
|
199
|
-
let ruby = magnus::Ruby::get().unwrap();
|
|
200
|
-
let datasets_val: magnus::RArray = node.funcall("datasets", ())?;
|
|
201
|
-
let x_labels_val: magnus::RArray = node.funcall("x_labels", ())?;
|
|
202
|
-
let y_labels_val: magnus::RArray = node.funcall("y_labels", ())?;
|
|
203
|
-
let y_bounds_val: magnus::RArray = node.funcall("y_bounds", ())?;
|
|
204
|
-
let block_val: Value = node.funcall("block", ())?;
|
|
205
|
-
|
|
206
|
-
let mut datasets = Vec::new();
|
|
207
|
-
let mut data_storage: Vec<Vec<(f64, f64)>> = Vec::new();
|
|
208
|
-
|
|
209
|
-
for i in 0..datasets_val.len() {
|
|
210
|
-
let index = isize::try_from(i)
|
|
211
|
-
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
212
|
-
let ds_val: Value = datasets_val.entry(index)?;
|
|
213
|
-
let data_array: magnus::RArray = ds_val.funcall("data", ())?;
|
|
214
|
-
|
|
215
|
-
let mut points = Vec::new();
|
|
216
|
-
for j in 0..data_array.len() {
|
|
217
|
-
let index = isize::try_from(j)
|
|
218
|
-
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
219
|
-
let point_array_val: Value = data_array.entry(index)?;
|
|
220
|
-
let point_array = magnus::RArray::from_value(point_array_val).ok_or_else(|| {
|
|
221
|
-
Error::new(ruby.exception_type_error(), "expected array for point")
|
|
222
|
-
})?;
|
|
223
|
-
let x: f64 = point_array.entry(0)?;
|
|
224
|
-
let y: f64 = point_array.entry(1)?;
|
|
225
|
-
points.push((x, y));
|
|
226
|
-
}
|
|
227
|
-
data_storage.push(points);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
for (i, points) in data_storage.iter().enumerate() {
|
|
231
|
-
let index = isize::try_from(i)
|
|
232
|
-
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
233
|
-
let ds_val: Value = datasets_val.entry(index)?;
|
|
234
|
-
let name: String = ds_val.funcall("name", ())?;
|
|
235
|
-
|
|
236
|
-
let mut ds_style = ratatui::style::Style::default();
|
|
237
|
-
let style_val: Value = ds_val.funcall("style", ())?;
|
|
238
|
-
if !style_val.is_nil() {
|
|
239
|
-
ds_style = parse_style(style_val)?;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
let ds = Dataset::default()
|
|
243
|
-
.name(name)
|
|
244
|
-
.marker(symbols::Marker::Braille)
|
|
245
|
-
.style(ds_style)
|
|
246
|
-
.data(points);
|
|
247
|
-
datasets.push(ds);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
let mut x_labels = Vec::new();
|
|
251
|
-
for i in 0..x_labels_val.len() {
|
|
252
|
-
let index = isize::try_from(i)
|
|
253
|
-
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
254
|
-
let label: String = x_labels_val.entry(index)?;
|
|
255
|
-
x_labels.push(Span::from(label));
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
let mut y_labels = Vec::new();
|
|
259
|
-
for i in 0..y_labels_val.len() {
|
|
260
|
-
let index = isize::try_from(i)
|
|
261
|
-
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
262
|
-
let label: String = y_labels_val.entry(index)?;
|
|
263
|
-
y_labels.push(Span::from(label));
|
|
264
|
-
}
|
|
265
|
-
// Ratatui 0.29+ requires labels to be present for the axis line to render
|
|
266
|
-
if y_labels.is_empty() {
|
|
267
|
-
y_labels.push(Span::from(""));
|
|
268
|
-
y_labels.push(Span::from(""));
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
let y_bounds: [f64; 2] = [y_bounds_val.entry(0)?, y_bounds_val.entry(1)?];
|
|
272
|
-
|
|
273
|
-
let mut min_x = 0.0;
|
|
274
|
-
let mut max_x = 0.0;
|
|
275
|
-
let mut first = true;
|
|
276
|
-
for ds_data in &data_storage {
|
|
277
|
-
for (x, _) in ds_data {
|
|
278
|
-
if first {
|
|
279
|
-
min_x = *x;
|
|
280
|
-
max_x = *x;
|
|
281
|
-
first = false;
|
|
282
|
-
} else {
|
|
283
|
-
if *x < min_x {
|
|
284
|
-
min_x = *x;
|
|
285
|
-
}
|
|
286
|
-
if *x > max_x {
|
|
287
|
-
max_x = *x;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
if (min_x - max_x).abs() < f64::EPSILON {
|
|
293
|
-
max_x = min_x + 1.0;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
let x_axis = Axis::default().labels(x_labels).bounds([min_x, max_x]);
|
|
297
|
-
let y_axis = Axis::default().labels(y_labels).bounds(y_bounds);
|
|
298
|
-
|
|
299
|
-
let mut chart = Chart::new(datasets).x_axis(x_axis).y_axis(y_axis);
|
|
300
|
-
if !block_val.is_nil() {
|
|
301
|
-
chart = chart.block(parse_block(block_val, &bump)?);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
frame.render_widget(chart, area);
|
|
305
|
-
Ok(())
|
|
306
|
-
}
|
|
307
|
-
|
|
308
190
|
#[cfg(test)]
|
|
309
191
|
mod tests {
|
|
310
192
|
use super::*;
|