ratatui_ruby 0.3.1 → 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 +14 -12
- data/.builds/ruby-3.3.yml +14 -12
- data/.builds/ruby-3.4.yml +14 -12
- data/.builds/ruby-4.0.0.yml +14 -12
- data/AGENTS.md +89 -132
- data/CHANGELOG.md +223 -1
- data/README.md +23 -16
- data/REUSE.toml +20 -0
- data/doc/application_architecture.md +176 -0
- data/doc/application_testing.md +17 -10
- data/doc/contributors/design/ruby_frontend.md +11 -7
- data/doc/contributors/developing_examples.md +261 -0
- data/doc/contributors/documentation_style.md +104 -0
- data/doc/contributors/dwim_dx.md +366 -0
- data/doc/contributors/index.md +2 -0
- data/doc/custom.css +14 -0
- data/doc/event_handling.md +125 -0
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_analytics.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_custom_widget.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_map_demo.png +0 -0
- data/doc/images/app_mouse_events.png +0 -0
- data/doc/images/app_table_select.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart_demo.png +0 -0
- data/doc/images/widget_block_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_list_styles.png +0 -0
- data/doc/images/widget_popup_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_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar_demo.png +0 -0
- data/doc/images/widget_sparkline_demo.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/index.md +1 -0
- data/doc/interactive_design.md +116 -0
- data/doc/quickstart.md +186 -84
- 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/{lib/ratatui_ruby/output.rb → examples/app_all_events/view.rb} +1 -1
- 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/app_login_form/app.rb +108 -0
- data/examples/app_map_demo/app.rb +93 -0
- data/examples/app_table_select/app.rb +201 -0
- data/examples/verify_quickstart_dsl/app.rb +45 -0
- data/examples/verify_quickstart_layout/app.rb +69 -0
- data/examples/verify_quickstart_lifecycle/app.rb +48 -0
- data/examples/verify_readme_usage/app.rb +34 -0
- data/examples/widget_barchart_demo/app.rb +238 -0
- data/examples/widget_block_padding/app.rb +67 -0
- data/examples/widget_block_titles/app.rb +69 -0
- data/examples/widget_box_demo/app.rb +250 -0
- 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 +102 -0
- data/examples/widget_table_flex/app.rb +95 -0
- data/examples/widget_tabs_demo/app.rb +167 -0
- data/ext/ratatui_ruby/Cargo.lock +889 -115
- data/ext/ratatui_ruby/Cargo.toml +4 -3
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +7 -0
- data/ext/ratatui_ruby/src/events.rs +293 -219
- data/ext/ratatui_ruby/src/frame.rs +115 -0
- data/ext/ratatui_ruby/src/lib.rs +105 -24
- data/ext/ratatui_ruby/src/rendering.rs +94 -10
- data/ext/ratatui_ruby/src/style.rs +357 -93
- data/ext/ratatui_ruby/src/terminal.rs +121 -31
- data/ext/ratatui_ruby/src/text.rs +178 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +99 -24
- data/ext/ratatui_ruby/src/widgets/block.rs +32 -3
- data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
- data/ext/ratatui_ruby/src/widgets/canvas.rs +44 -9
- data/ext/ratatui_ruby/src/widgets/chart.rs +79 -27
- data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
- data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
- data/ext/ratatui_ruby/src/widgets/layout.rs +223 -15
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +114 -11
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +4 -2
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +51 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +61 -7
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +73 -6
- data/ext/ratatui_ruby/src/widgets/table.rs +177 -64
- data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
- data/lib/ratatui_ruby/cell.rb +166 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
- data/lib/ratatui_ruby/event/key.rb +211 -0
- data/lib/ratatui_ruby/event/mouse.rb +124 -0
- data/lib/ratatui_ruby/event/none.rb +43 -0
- data/lib/ratatui_ruby/event/paste.rb +71 -0
- data/lib/ratatui_ruby/event/resize.rb +80 -0
- data/lib/ratatui_ruby/event.rb +131 -0
- data/lib/ratatui_ruby/frame.rb +87 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +23 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +226 -17
- data/lib/ratatui_ruby/schema/block.rb +178 -11
- data/lib/ratatui_ruby/schema/calendar.rb +70 -14
- data/lib/ratatui_ruby/schema/canvas.rb +213 -46
- data/lib/ratatui_ruby/schema/center.rb +46 -8
- data/lib/ratatui_ruby/schema/chart.rb +134 -32
- data/lib/ratatui_ruby/schema/clear.rb +22 -53
- data/lib/ratatui_ruby/schema/constraint.rb +72 -12
- data/lib/ratatui_ruby/schema/cursor.rb +23 -5
- data/lib/ratatui_ruby/schema/draw.rb +53 -0
- data/lib/ratatui_ruby/schema/gauge.rb +56 -12
- data/lib/ratatui_ruby/schema/layout.rb +91 -9
- data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
- data/lib/ratatui_ruby/schema/list.rb +92 -16
- data/lib/ratatui_ruby/schema/overlay.rb +29 -3
- data/lib/ratatui_ruby/schema/paragraph.rb +82 -25
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +29 -0
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +34 -0
- data/lib/ratatui_ruby/schema/rect.rb +59 -10
- data/lib/ratatui_ruby/schema/scrollbar.rb +127 -19
- data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +120 -12
- data/lib/ratatui_ruby/schema/style.rb +39 -11
- data/lib/ratatui_ruby/schema/table.rb +109 -18
- data/lib/ratatui_ruby/schema/tabs.rb +71 -10
- data/lib/ratatui_ruby/schema/text.rb +90 -0
- data/lib/ratatui_ruby/session/autodoc.rb +417 -0
- data/lib/ratatui_ruby/session.rb +163 -0
- data/lib/ratatui_ruby/test_helper.rb +322 -13
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +184 -38
- data/sig/examples/app_all_events/app.rbs +11 -0
- 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/sig/examples/app_color_picker/app.rbs +12 -0
- 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/sig/examples/widget_gauge_demo/app.rbs +11 -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/sig/examples/widget_style_colors/app.rbs +14 -0
- data/sig/examples/widget_table_flex/app.rbs +11 -0
- data/sig/ratatui_ruby/event.rbs +69 -0
- data/sig/ratatui_ruby/frame.rbs +9 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +5 -3
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
- data/sig/ratatui_ruby/schema/block.rbs +5 -4
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
- data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
- data/sig/ratatui_ruby/schema/center.rbs +3 -3
- data/sig/ratatui_ruby/schema/chart.rbs +8 -5
- data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
- data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
- data/sig/ratatui_ruby/schema/draw.rbs +27 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
- data/sig/ratatui_ruby/schema/layout.rbs +11 -1
- data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
- data/sig/ratatui_ruby/schema/list.rbs +5 -1
- data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +8 -0
- data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
- data/sig/ratatui_ruby/schema/rect.rbs +2 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
- data/sig/ratatui_ruby/schema/table.rbs +8 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
- data/sig/ratatui_ruby/schema/text.rbs +22 -0
- 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/build.yml.erb +13 -11
- data/tasks/resources/index.html.erb +6 -0
- data/tasks/sourcehut.rake +4 -4
- data/tasks/terminal_preview/app_screenshot.rb +33 -0
- data/tasks/terminal_preview/crash_report.rb +52 -0
- data/tasks/terminal_preview/example_app.rb +25 -0
- data/tasks/terminal_preview/launcher_script.rb +46 -0
- data/tasks/terminal_preview/preview_collection.rb +58 -0
- data/tasks/terminal_preview/preview_timing.rb +22 -0
- data/tasks/terminal_preview/safety_confirmation.rb +56 -0
- data/tasks/terminal_preview/saved_screenshot.rb +53 -0
- data/tasks/terminal_preview/system_appearance.rb +11 -0
- data/tasks/terminal_preview/terminal_window.rb +136 -0
- data/tasks/terminal_preview/window_id.rb +14 -0
- data/tasks/terminal_preview.rake +28 -0
- data/tasks/test.rake +2 -2
- 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 +206 -54
- data/LICENSES/BSD-2-Clause.txt +0 -9
- data/doc/images/examples-analytics.rb.png +0 -0
- data/doc/images/examples-box_demo.rb.png +0 -0
- data/doc/images/examples-calendar_demo.rb.png +0 -0
- data/doc/images/examples-chart_demo.rb.png +0 -0
- data/doc/images/examples-custom_widget.rb.png +0 -0
- data/doc/images/examples-dashboard.rb.png +0 -0
- data/doc/images/examples-list_styles.rb.png +0 -0
- data/doc/images/examples-login_form.rb.png +0 -0
- data/doc/images/examples-map_demo.rb.png +0 -0
- data/doc/images/examples-mouse_events.rb.png +0 -0
- data/doc/images/examples-popup_demo.rb.gif +0 -0
- data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
- data/doc/images/examples-scroll_text.rb.png +0 -0
- data/doc/images/examples-scrollbar_demo.rb.png +0 -0
- data/doc/images/examples-stock_ticker.rb.png +0 -0
- data/doc/images/examples-system_monitor.rb.png +0 -0
- data/doc/images/examples-table_select.rb.png +0 -0
- data/examples/analytics.rb +0 -88
- data/examples/box_demo.rb +0 -71
- data/examples/calendar_demo.rb +0 -55
- data/examples/chart_demo.rb +0 -84
- data/examples/custom_widget.rb +0 -43
- data/examples/dashboard.rb +0 -72
- data/examples/list_styles.rb +0 -66
- data/examples/login_form.rb +0 -115
- data/examples/map_demo.rb +0 -58
- data/examples/mouse_events.rb +0 -95
- data/examples/popup_demo.rb +0 -105
- data/examples/quickstart_dsl.rb +0 -30
- data/examples/quickstart_lifecycle.rb +0 -40
- data/examples/readme_usage.rb +0 -21
- data/examples/scroll_text.rb +0 -74
- data/examples/scrollbar_demo.rb +0 -75
- data/examples/stock_ticker.rb +0 -93
- data/examples/system_monitor.rb +0 -94
- data/examples/table_select.rb +0 -70
- data/examples/test_analytics.rb +0 -65
- data/examples/test_box_demo.rb +0 -38
- data/examples/test_calendar_demo.rb +0 -66
- data/examples/test_dashboard.rb +0 -38
- data/examples/test_list_styles.rb +0 -61
- data/examples/test_login_form.rb +0 -63
- data/examples/test_map_demo.rb +0 -100
- data/examples/test_popup_demo.rb +0 -62
- data/examples/test_scroll_text.rb +0 -130
- data/examples/test_stock_ticker.rb +0 -39
- data/examples/test_system_monitor.rb +0 -40
- data/examples/test_table_select.rb +0 -37
- data/ext/ratatui_ruby/src/buffer.rs +0 -54
- data/lib/ratatui_ruby/dsl.rb +0 -64
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# Reports a mouse interaction.
|
|
9
|
+
#
|
|
10
|
+
# Modern terminals support rich pointer input, but the protocols are complex and varied.
|
|
11
|
+
# Handling clicks, drags, and scrolls requires robust parsing.
|
|
12
|
+
#
|
|
13
|
+
# This event simplifies the complexity. It tells you exactly *what* happened (+kind+),
|
|
14
|
+
# *where* it happened (+x+, +y+), and *which* button was involved.
|
|
15
|
+
#
|
|
16
|
+
# Use this to build interactive UIs. Implement click handlers, draggable sliders, or
|
|
17
|
+
# scrollable viewports with confidence.
|
|
18
|
+
#
|
|
19
|
+
# === Example
|
|
20
|
+
#
|
|
21
|
+
# if event.mouse? && event.down? && event.button == "left"
|
|
22
|
+
# puts "Left click at #{event.x}, #{event.y}"
|
|
23
|
+
# end
|
|
24
|
+
class Mouse < Event
|
|
25
|
+
# The kind of event (<tt>"down"</tt>, <tt>"up"</tt>, <tt>"drag"</tt>, <tt>"moved"</tt>, <tt>"scroll_up"</tt>, <tt>"scroll_down"</tt>).
|
|
26
|
+
#
|
|
27
|
+
# puts event.kind # => "down"
|
|
28
|
+
attr_reader :kind
|
|
29
|
+
# X coordinate (column).
|
|
30
|
+
#
|
|
31
|
+
# puts event.x # => 10
|
|
32
|
+
attr_reader :x
|
|
33
|
+
# Y coordinate (row).
|
|
34
|
+
#
|
|
35
|
+
# puts event.y # => 5
|
|
36
|
+
attr_reader :y
|
|
37
|
+
# The button pressed (<tt>"left"</tt>, <tt>"right"</tt>, <tt>"middle"</tt>, <tt>"none"</tt>).
|
|
38
|
+
#
|
|
39
|
+
# puts event.button # => "left"
|
|
40
|
+
#
|
|
41
|
+
# Can be <tt>nil</tt>, which is treated as <tt>"none"</tt>.
|
|
42
|
+
attr_reader :button
|
|
43
|
+
# List of active modifiers.
|
|
44
|
+
#
|
|
45
|
+
# puts event.modifiers # => ["ctrl"]
|
|
46
|
+
attr_reader :modifiers
|
|
47
|
+
|
|
48
|
+
# Returns true for Mouse events.
|
|
49
|
+
#
|
|
50
|
+
# event.mouse? # => true
|
|
51
|
+
# event.key? # => false
|
|
52
|
+
# event.resize? # => false
|
|
53
|
+
def mouse?
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Creates a new Mouse event.
|
|
58
|
+
#
|
|
59
|
+
# [kind]
|
|
60
|
+
# Event kind (String).
|
|
61
|
+
# [x]
|
|
62
|
+
# X coordinate (Integer).
|
|
63
|
+
# [y]
|
|
64
|
+
# Y coordinate (Integer).
|
|
65
|
+
# [button]
|
|
66
|
+
# Button name (String or <tt>nil</tt>).
|
|
67
|
+
# [modifiers]
|
|
68
|
+
# List of modifiers (Array<String>).
|
|
69
|
+
def initialize(kind:, x:, y:, button:, modifiers: [])
|
|
70
|
+
@kind = kind
|
|
71
|
+
@x = x
|
|
72
|
+
@y = y
|
|
73
|
+
@button = button || "none"
|
|
74
|
+
@modifiers = modifiers.sort
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns true if mouse button was pressed down.
|
|
78
|
+
def down?
|
|
79
|
+
@kind == "down"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Returns true if mouse button was released.
|
|
83
|
+
def up?
|
|
84
|
+
@kind == "up"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns true if mouse is being dragged.
|
|
88
|
+
def drag?
|
|
89
|
+
@kind == "drag"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns true if scroll wheel moved up.
|
|
93
|
+
def scroll_up?
|
|
94
|
+
@kind == "scroll_up"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Returns true if scroll wheel moved down.
|
|
98
|
+
#
|
|
99
|
+
# if event.scroll_down?
|
|
100
|
+
# scroll_offset += 1
|
|
101
|
+
# end
|
|
102
|
+
def scroll_down?
|
|
103
|
+
@kind == "scroll_down"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Deconstructs the event for pattern matching.
|
|
107
|
+
#
|
|
108
|
+
# case event
|
|
109
|
+
# in type: :mouse, kind: "down", x:, y:
|
|
110
|
+
# puts "Click at #{x}, #{y}"
|
|
111
|
+
# end
|
|
112
|
+
def deconstruct_keys(keys)
|
|
113
|
+
{ type: :mouse, kind: @kind, x: @x, y: @y, button: @button, modifiers: @modifiers }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
##
|
|
117
|
+
# Compares this event with another for equality.
|
|
118
|
+
def ==(other)
|
|
119
|
+
return false unless other.is_a?(Mouse)
|
|
120
|
+
kind == other.kind && x == other.x && y == other.y && button == other.button && modifiers == other.modifiers
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# {Null object}[https://en.wikipedia.org/wiki/Null_object_pattern] for absent events.
|
|
9
|
+
#
|
|
10
|
+
# Event loops poll for input 60 times per second. Usually nothing is happening.
|
|
11
|
+
# If <tt>RatatuiRuby.poll_event</tt> returned <tt>nil</tt>, you would need
|
|
12
|
+
# nil-checks: <tt>event&.key?</tt>, <tt>next unless event</tt>.
|
|
13
|
+
#
|
|
14
|
+
# This class eliminates that friction. It responds to every predicate with
|
|
15
|
+
# <tt>false</tt>. Call <tt>none?</tt> to detect it explicitly. Pattern-match on
|
|
16
|
+
# <tt>type: :none</tt> for exhaustive dispatch.
|
|
17
|
+
#
|
|
18
|
+
# Use it to simplify your event loop. No guards. Optional `else` clauses.
|
|
19
|
+
#
|
|
20
|
+
# See Martin Fowler's {Special Case}[https://martinfowler.com/eaaCatalog/specialCase.html] pattern.
|
|
21
|
+
#
|
|
22
|
+
# === Predicate Example
|
|
23
|
+
#
|
|
24
|
+
# event = RatatuiRuby.poll_event
|
|
25
|
+
# break if event.ctrl_c?
|
|
26
|
+
# redraw if event.none?
|
|
27
|
+
#
|
|
28
|
+
# === Pattern Matching Example
|
|
29
|
+
#
|
|
30
|
+
# redraw if RatatuiRuby.poll_event in type: :none
|
|
31
|
+
class None < Event
|
|
32
|
+
# Returns true for None events.
|
|
33
|
+
def none?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Deconstructs the event for pattern matching.
|
|
38
|
+
def deconstruct_keys(keys)
|
|
39
|
+
{ type: :none }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# Encapsulates pasted text.
|
|
9
|
+
#
|
|
10
|
+
# Users frequently paste text into terminals. Without specific handling, a paste appears as
|
|
11
|
+
# a flood of rapid keystrokes, often triggering accidental commands or confusing the input state.
|
|
12
|
+
#
|
|
13
|
+
# This event makes pasting safe. It groups the entire inserted block into a single atomic action.
|
|
14
|
+
#
|
|
15
|
+
# Handle this event to support bulk text insertion cleanly. Insert the +content+ directly into
|
|
16
|
+
# your field or buffer without triggering per-character logic.
|
|
17
|
+
#
|
|
18
|
+
# === Examples
|
|
19
|
+
#
|
|
20
|
+
# Using predicates:
|
|
21
|
+
# if event.paste?
|
|
22
|
+
# puts "Pasted: #{event.content}"
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# Using pattern matching:
|
|
26
|
+
# case event
|
|
27
|
+
# in type: :paste, content:
|
|
28
|
+
# puts "Pasted: #{content}"
|
|
29
|
+
# end
|
|
30
|
+
class Paste < Event
|
|
31
|
+
# The pasted content.
|
|
32
|
+
#
|
|
33
|
+
# puts event.content # => "https://example.com"
|
|
34
|
+
attr_reader :content
|
|
35
|
+
|
|
36
|
+
# Returns true for Paste events.
|
|
37
|
+
#
|
|
38
|
+
# event.paste? # => true
|
|
39
|
+
# event.key? # => false
|
|
40
|
+
# event.resize? # => false
|
|
41
|
+
def paste?
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Creates a new Paste event.
|
|
46
|
+
#
|
|
47
|
+
# [content]
|
|
48
|
+
# Pasted text (String).
|
|
49
|
+
def initialize(content:)
|
|
50
|
+
@content = content
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Deconstructs the event for pattern matching.
|
|
54
|
+
#
|
|
55
|
+
# case event
|
|
56
|
+
# in type: :paste, content:
|
|
57
|
+
# puts "User pasted: #{content}"
|
|
58
|
+
# end
|
|
59
|
+
def deconstruct_keys(keys)
|
|
60
|
+
{ type: :paste, content: @content }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# Compares this event with another for equality.
|
|
65
|
+
def ==(other)
|
|
66
|
+
return false unless other.is_a?(Paste)
|
|
67
|
+
content == other.content
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# Signals a change in terminal dimensions.
|
|
9
|
+
#
|
|
10
|
+
# The terminal window is dynamic, not static. The user changes its dimensions at will,
|
|
11
|
+
# usually breaking a fixed layout.
|
|
12
|
+
#
|
|
13
|
+
# This event captures the new state. It delivers the updated +width+ and +height+
|
|
14
|
+
# immediately after the change.
|
|
15
|
+
#
|
|
16
|
+
# Use these dimensions to drive your layout logic. Recalculate constraints. Reallocate space.
|
|
17
|
+
# Fill the new canvas completely to maintain a responsive design.
|
|
18
|
+
#
|
|
19
|
+
# === Examples
|
|
20
|
+
#
|
|
21
|
+
# Using predicates:
|
|
22
|
+
# if event.resize?
|
|
23
|
+
# puts "Resized to #{event.width}x#{event.height}"
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# Using pattern matching:
|
|
27
|
+
# case event
|
|
28
|
+
# in type: :resize, width:, height:
|
|
29
|
+
# puts "Resized to #{width}x#{height}"
|
|
30
|
+
# end
|
|
31
|
+
class Resize < Event
|
|
32
|
+
# New terminal width in columns.
|
|
33
|
+
#
|
|
34
|
+
# puts event.width # => 80
|
|
35
|
+
attr_reader :width
|
|
36
|
+
|
|
37
|
+
# New terminal height in rows.
|
|
38
|
+
#
|
|
39
|
+
# puts event.height # => 24
|
|
40
|
+
attr_reader :height
|
|
41
|
+
|
|
42
|
+
# Returns true for Resize events.
|
|
43
|
+
#
|
|
44
|
+
# event.resize? # => true
|
|
45
|
+
# event.key? # => false
|
|
46
|
+
# event.mouse? # => false
|
|
47
|
+
def resize?
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Creates a new Resize event.
|
|
52
|
+
#
|
|
53
|
+
# [width]
|
|
54
|
+
# New width (Integer).
|
|
55
|
+
# [height]
|
|
56
|
+
# New height (Integer).
|
|
57
|
+
def initialize(width:, height:)
|
|
58
|
+
@width = width
|
|
59
|
+
@height = height
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Deconstructs the event for pattern matching.
|
|
63
|
+
#
|
|
64
|
+
# case event
|
|
65
|
+
# in type: :resize, width:, height:
|
|
66
|
+
# puts "Resized to #{width}x#{height}"
|
|
67
|
+
# end
|
|
68
|
+
def deconstruct_keys(keys)
|
|
69
|
+
{ type: :resize, width: @width, height: @height }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Compares this event with another for equality.
|
|
74
|
+
def ==(other)
|
|
75
|
+
return false unless other.is_a?(Resize)
|
|
76
|
+
width == other.width && height == other.height
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
# Base class for all RatatuiRuby events.
|
|
8
|
+
#
|
|
9
|
+
# Events represent terminal input: keyboard, mouse, resize, paste, focus changes.
|
|
10
|
+
# Returned by RatatuiRuby.poll_event. All events support Ruby 3.0+ pattern matching.
|
|
11
|
+
#
|
|
12
|
+
# == Event Types
|
|
13
|
+
#
|
|
14
|
+
# * <tt>Key</tt> — keyboard input
|
|
15
|
+
# * <tt>Mouse</tt> — mouse clicks, movement, wheel
|
|
16
|
+
# * <tt>Resize</tt> — terminal resized
|
|
17
|
+
# * <tt>Paste</tt> — clipboard paste
|
|
18
|
+
# * <tt>FocusGained</tt> — terminal gained focus
|
|
19
|
+
# * <tt>FocusLost</tt> — terminal lost focus
|
|
20
|
+
# * <tt>None</tt> — no event available (Null Object)
|
|
21
|
+
#
|
|
22
|
+
# == Pattern Matching (Exhaustive)
|
|
23
|
+
#
|
|
24
|
+
# Use <tt>case...in</tt> to dispatch on every possible event type. This ensures
|
|
25
|
+
# you handle every case without needing an +else+ clause:
|
|
26
|
+
#
|
|
27
|
+
# case RatatuiRuby.poll_event
|
|
28
|
+
# in { type: :key, code: "q" }
|
|
29
|
+
# break
|
|
30
|
+
# in { type: :key, code: code, modifiers: }
|
|
31
|
+
# handle_key(code, modifiers)
|
|
32
|
+
# in { type: :mouse, kind: "down", x:, y: }
|
|
33
|
+
# handle_click(x, y)
|
|
34
|
+
# in { type: :mouse, kind:, x:, y: }
|
|
35
|
+
# # handle other mouse activities
|
|
36
|
+
# in { type: :resize, width:, height: }
|
|
37
|
+
# handle_resize(width, height)
|
|
38
|
+
# in { type: :paste, content: }
|
|
39
|
+
# handle_paste(content)
|
|
40
|
+
# in { type: :focus_gained }
|
|
41
|
+
# handle_focus_gain
|
|
42
|
+
# in { type: :focus_lost }
|
|
43
|
+
# handle_focus_loss
|
|
44
|
+
# in { type: :none }
|
|
45
|
+
# # Idle
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# == Predicates
|
|
49
|
+
#
|
|
50
|
+
# Check event types with predicates without pattern matching:
|
|
51
|
+
#
|
|
52
|
+
# event = RatatuiRuby.poll_event
|
|
53
|
+
# if event.key?
|
|
54
|
+
# puts "Key pressed"
|
|
55
|
+
# elsif event.none?
|
|
56
|
+
# # Idle
|
|
57
|
+
# elsif event.mouse?
|
|
58
|
+
# puts "Mouse event"
|
|
59
|
+
# end
|
|
60
|
+
class Event
|
|
61
|
+
# Returns true if this is a None event.
|
|
62
|
+
def none?
|
|
63
|
+
false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns true if this is a Key event.
|
|
67
|
+
def key?
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Returns true if this is a Mouse event.
|
|
72
|
+
def mouse?
|
|
73
|
+
false
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Returns true if this is a Resize event.
|
|
77
|
+
def resize?
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns true if this is a Paste event.
|
|
82
|
+
def paste?
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Returns true if this is a FocusGained event.
|
|
87
|
+
def focus_gained?
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns true if this is a FocusLost event.
|
|
92
|
+
def focus_lost?
|
|
93
|
+
false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Responds to dynamic predicate methods for key checks.
|
|
97
|
+
# All non-Key events return false for any key predicate.
|
|
98
|
+
def method_missing(name, *args, &block)
|
|
99
|
+
if name.to_s.end_with?("?")
|
|
100
|
+
false
|
|
101
|
+
else
|
|
102
|
+
super
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Declares that this class responds to dynamic predicate methods.
|
|
107
|
+
def respond_to_missing?(name, *args)
|
|
108
|
+
name.to_s.end_with?("?") || super
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Deconstructs the event for pattern matching.
|
|
112
|
+
#
|
|
113
|
+
# Keys argument is unused but required by the protocol.
|
|
114
|
+
#
|
|
115
|
+
# case event
|
|
116
|
+
# in type: :key, code:
|
|
117
|
+
# puts "Key: #{code}"
|
|
118
|
+
# end
|
|
119
|
+
def deconstruct_keys(keys)
|
|
120
|
+
{}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
require_relative "event/none"
|
|
126
|
+
require_relative "event/key"
|
|
127
|
+
require_relative "event/mouse"
|
|
128
|
+
require_relative "event/resize"
|
|
129
|
+
require_relative "event/paste"
|
|
130
|
+
require_relative "event/focus_gained"
|
|
131
|
+
require_relative "event/focus_lost"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
# Provides access to the terminal buffer for rendering widgets.
|
|
8
|
+
#
|
|
9
|
+
# Rendering in immediate-mode TUIs requires knowing the terminal dimensions and
|
|
10
|
+
# placing widgets at specific positions. Without explicit control, layout
|
|
11
|
+
# calculations become duplicated between rendering and hit testing.
|
|
12
|
+
#
|
|
13
|
+
# This class exposes the terminal frame during a draw call. It provides the
|
|
14
|
+
# current area and methods to render widgets at precise locations.
|
|
15
|
+
#
|
|
16
|
+
# Use it inside a <tt>RatatuiRuby.draw</tt> block to render widgets with full
|
|
17
|
+
# control over placement.
|
|
18
|
+
#
|
|
19
|
+
# === Examples
|
|
20
|
+
#
|
|
21
|
+
# Basic usage with a single widget:
|
|
22
|
+
#
|
|
23
|
+
# RatatuiRuby.draw do |frame|
|
|
24
|
+
# paragraph = RatatuiRuby::Paragraph.new(text: "Hello, world!")
|
|
25
|
+
# frame.render_widget(paragraph, frame.area)
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# Using Layout.split for multi-region layouts:
|
|
29
|
+
#
|
|
30
|
+
# RatatuiRuby.draw do |frame|
|
|
31
|
+
# sidebar, main = RatatuiRuby::Layout.split(
|
|
32
|
+
# frame.area,
|
|
33
|
+
# direction: :horizontal,
|
|
34
|
+
# constraints: [
|
|
35
|
+
# RatatuiRuby::Constraint.length(20),
|
|
36
|
+
# RatatuiRuby::Constraint.fill(1)
|
|
37
|
+
# ]
|
|
38
|
+
# )
|
|
39
|
+
#
|
|
40
|
+
# frame.render_widget(sidebar_widget, sidebar)
|
|
41
|
+
# frame.render_widget(main_widget, main)
|
|
42
|
+
#
|
|
43
|
+
# # Store rects for hit testing — no duplication!
|
|
44
|
+
# @regions = { sidebar: sidebar, main: main }
|
|
45
|
+
# end
|
|
46
|
+
class Frame
|
|
47
|
+
##
|
|
48
|
+
# :method: area
|
|
49
|
+
# :call-seq: area() -> Rect
|
|
50
|
+
#
|
|
51
|
+
# Returns the full terminal area as a Rect.
|
|
52
|
+
#
|
|
53
|
+
# The returned Rect represents the entire drawable area of the terminal.
|
|
54
|
+
# Use it as the starting point for layout calculations.
|
|
55
|
+
#
|
|
56
|
+
# === Example
|
|
57
|
+
#
|
|
58
|
+
# RatatuiRuby.draw do |frame|
|
|
59
|
+
# puts "Terminal size: #{frame.area.width}x#{frame.area.height}"
|
|
60
|
+
# end
|
|
61
|
+
#
|
|
62
|
+
# (Native method implemented in Rust)
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# :method: render_widget
|
|
66
|
+
# :call-seq: render_widget(widget, area) -> nil
|
|
67
|
+
#
|
|
68
|
+
# Renders a widget at the specified area.
|
|
69
|
+
#
|
|
70
|
+
# Widgets in RatatuiRuby are immutable Data objects. This method takes a
|
|
71
|
+
# widget and a Rect, rendering the widget's content within that region.
|
|
72
|
+
#
|
|
73
|
+
# [widget]
|
|
74
|
+
# The widget to render (Paragraph, Layout, List, Table, etc.).
|
|
75
|
+
# [area]
|
|
76
|
+
# A Rect specifying where to render the widget.
|
|
77
|
+
#
|
|
78
|
+
# === Example
|
|
79
|
+
#
|
|
80
|
+
# RatatuiRuby.draw do |frame|
|
|
81
|
+
# para = RatatuiRuby::Paragraph.new(text: "Content")
|
|
82
|
+
# frame.render_widget(para, frame.area)
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
# (Native method implemented in Rust)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class BarChart
|
|
8
|
+
# A bar in a grouped bar chart.
|
|
9
|
+
#
|
|
10
|
+
# === Examples
|
|
11
|
+
#
|
|
12
|
+
# BarChart::Bar.new(value: 10, style: Style.new(fg: :red), label: "A")
|
|
13
|
+
class Bar < Data.define(:value, :label, :style, :value_style, :text_value)
|
|
14
|
+
##
|
|
15
|
+
# :attr_reader: value
|
|
16
|
+
# The value of the bar (Integer).
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# :attr_reader: label
|
|
20
|
+
# The label of the bar (optional String).
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# :attr_reader: style
|
|
24
|
+
# The style of the bar (optional Style).
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# :attr_reader: value_style
|
|
28
|
+
# The style of the value (optional Style).
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# :attr_reader: text_value
|
|
32
|
+
# The text to display as the value (optional String).
|
|
33
|
+
|
|
34
|
+
def initialize(value:, label: nil, style: nil, value_style: nil, text_value: nil)
|
|
35
|
+
super(
|
|
36
|
+
value: Integer(value),
|
|
37
|
+
label:,
|
|
38
|
+
style:,
|
|
39
|
+
value_style:,
|
|
40
|
+
text_value:
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class BarChart
|
|
8
|
+
# A group of bars in a grouped bar chart.
|
|
9
|
+
#
|
|
10
|
+
# === Examples
|
|
11
|
+
#
|
|
12
|
+
# BarChart::BarGroup.new(label: "Q1", bars: [BarChart::Bar.new(value: 10), BarChart::Bar.new(value: 20)])
|
|
13
|
+
class BarGroup < Data.define(:label, :bars)
|
|
14
|
+
##
|
|
15
|
+
# :attr_reader: label
|
|
16
|
+
# The label of the group (String).
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# :attr_reader: bars
|
|
20
|
+
# The bars in the group (Array of Bar).
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|