ratatui_ruby 0.5.0 → 0.7.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 +10 -4
- data/CHANGELOG.md +79 -7
- data/README.md +37 -5
- data/REUSE.toml +2 -7
- data/doc/application_architecture.md +96 -22
- data/doc/application_testing.md +76 -30
- data/doc/contributors/architectural_overhaul/chat_conversations.md +4952 -0
- data/doc/contributors/architectural_overhaul/implementation_plan.md +60 -0
- data/doc/contributors/architectural_overhaul/task.md +37 -0
- data/doc/contributors/design/ruby_frontend.md +288 -56
- data/doc/contributors/design/rust_backend.md +349 -54
- data/doc/contributors/developing_examples.md +134 -49
- data/doc/contributors/index.md +7 -5
- data/doc/contributors/v1.0.0_blockers.md +1729 -0
- data/doc/event_handling.md +11 -3
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart_demo.png +0 -0
- data/doc/images/widget_block_demo.png +0 -0
- data/doc/images/widget_canvas_demo.png +0 -0
- data/doc/images/widget_cell_demo.png +0 -0
- data/doc/images/widget_center_demo.png +0 -0
- data/doc/images/widget_chart_demo.png +0 -0
- data/doc/images/widget_list_demo.png +0 -0
- data/doc/images/widget_overlay_demo.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_sparkline_demo.png +0 -0
- data/doc/images/widget_table_demo.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/index.md +11 -6
- data/doc/interactive_design.md +2 -2
- data/doc/quickstart.md +127 -165
- data/doc/terminal_limitations.md +92 -0
- data/doc/v0.7.0_migration.md +236 -0
- data/doc/why.md +93 -0
- data/examples/app_all_events/README.md +47 -27
- data/examples/app_all_events/app.rb +38 -35
- data/examples/app_all_events/model/app_model.rb +157 -0
- data/examples/app_all_events/model/event_entry.rb +17 -0
- data/examples/app_all_events/model/msg.rb +37 -0
- data/examples/app_all_events/update.rb +73 -0
- data/examples/app_all_events/view/app_view.rb +9 -9
- data/examples/app_all_events/view/controls_view.rb +9 -7
- data/examples/app_all_events/view/counts_view.rb +13 -9
- data/examples/app_all_events/view/live_view.rb +9 -8
- data/examples/app_all_events/view/log_view.rb +11 -16
- data/examples/app_color_picker/README.md +84 -42
- data/examples/app_color_picker/app.rb +24 -62
- data/examples/app_color_picker/controls.rb +90 -0
- data/examples/app_color_picker/copy_dialog.rb +45 -49
- data/examples/app_color_picker/export_pane.rb +126 -0
- data/examples/app_color_picker/input.rb +99 -67
- data/examples/app_color_picker/main_container.rb +178 -0
- data/examples/app_color_picker/palette.rb +55 -26
- data/examples/app_login_form/README.md +49 -0
- data/examples/app_login_form/app.rb +2 -3
- data/examples/app_stateful_interaction/README.md +33 -0
- data/examples/app_stateful_interaction/app.rb +272 -0
- data/examples/timeout_demo.rb +43 -0
- data/examples/verify_quickstart_dsl/README.md +49 -0
- data/examples/verify_quickstart_dsl/app.rb +2 -0
- data/examples/verify_quickstart_layout/README.md +71 -0
- data/examples/verify_quickstart_layout/app.rb +2 -0
- data/examples/verify_quickstart_lifecycle/README.md +56 -0
- data/examples/verify_quickstart_lifecycle/app.rb +10 -4
- data/examples/verify_readme_usage/README.md +43 -0
- data/examples/verify_readme_usage/app.rb +8 -2
- data/examples/widget_barchart_demo/README.md +50 -0
- data/examples/widget_barchart_demo/app.rb +5 -5
- data/examples/widget_block_demo/README.md +36 -0
- data/examples/widget_block_demo/app.rb +256 -0
- data/examples/widget_box_demo/README.md +45 -0
- data/examples/widget_calendar_demo/README.md +39 -0
- data/examples/widget_calendar_demo/app.rb +5 -1
- data/examples/widget_canvas_demo/README.md +27 -0
- data/examples/widget_canvas_demo/app.rb +123 -0
- data/examples/widget_cell_demo/README.md +36 -0
- data/examples/widget_cell_demo/app.rb +31 -24
- data/examples/widget_center_demo/README.md +29 -0
- data/examples/widget_center_demo/app.rb +116 -0
- data/examples/widget_chart_demo/README.md +41 -0
- data/examples/widget_chart_demo/app.rb +7 -2
- data/examples/widget_gauge_demo/README.md +41 -0
- data/examples/widget_layout_split/README.md +44 -0
- data/examples/widget_line_gauge_demo/README.md +41 -0
- data/examples/widget_list_demo/README.md +49 -0
- data/examples/widget_list_demo/app.rb +91 -107
- data/examples/widget_map_demo/README.md +39 -0
- data/examples/{app_map_demo → widget_map_demo}/app.rb +4 -4
- data/examples/widget_overlay_demo/README.md +36 -0
- data/examples/widget_overlay_demo/app.rb +248 -0
- data/examples/widget_popup_demo/README.md +36 -0
- data/examples/widget_ratatui_logo_demo/README.md +34 -0
- data/examples/widget_ratatui_logo_demo/app.rb +1 -1
- data/examples/widget_ratatui_mascot_demo/README.md +34 -0
- data/examples/widget_rect/README.md +38 -0
- data/examples/widget_render/README.md +37 -0
- data/examples/widget_render/app.rb +3 -3
- data/examples/widget_rich_text/README.md +35 -0
- data/examples/widget_rich_text/app.rb +62 -33
- data/examples/widget_scroll_text/README.md +37 -0
- data/examples/widget_scroll_text/app.rb +0 -1
- data/examples/widget_scrollbar_demo/README.md +37 -0
- data/examples/widget_sparkline_demo/README.md +42 -0
- data/examples/widget_sparkline_demo/app.rb +4 -3
- data/examples/widget_style_colors/README.md +34 -0
- data/examples/widget_table_demo/README.md +48 -0
- data/examples/{app_table_select → widget_table_demo}/app.rb +65 -12
- data/examples/widget_tabs_demo/README.md +41 -0
- data/examples/widget_tabs_demo/app.rb +15 -1
- data/examples/widget_text_width/README.md +35 -0
- data/examples/widget_text_width/app.rb +113 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/Cargo.lock +11 -4
- data/ext/ratatui_ruby/Cargo.toml +2 -1
- data/ext/ratatui_ruby/src/events.rs +238 -26
- data/ext/ratatui_ruby/src/frame.rs +116 -3
- data/ext/ratatui_ruby/src/lib.rs +37 -6
- data/ext/ratatui_ruby/src/rendering.rs +22 -21
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/terminal.rs +39 -15
- data/ext/ratatui_ruby/src/text.rs +13 -4
- data/ext/ratatui_ruby/src/widgets/barchart.rs +24 -6
- data/ext/ratatui_ruby/src/widgets/canvas.rs +5 -5
- data/ext/ratatui_ruby/src/widgets/gauge.rs +9 -2
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +9 -2
- data/ext/ratatui_ruby/src/widgets/list.rs +179 -3
- data/ext/ratatui_ruby/src/widgets/list_state.rs +137 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +93 -1
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +191 -34
- data/ext/ratatui_ruby/src/widgets/table_state.rs +121 -0
- data/lib/ratatui_ruby/buffer/cell.rb +168 -0
- data/lib/ratatui_ruby/buffer.rb +15 -0
- data/lib/ratatui_ruby/cell.rb +4 -4
- data/lib/ratatui_ruby/event/key/character.rb +35 -0
- data/lib/ratatui_ruby/event/key/media.rb +44 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +95 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +55 -0
- data/lib/ratatui_ruby/event/key/system.rb +45 -0
- data/lib/ratatui_ruby/event/key.rb +111 -51
- data/lib/ratatui_ruby/event/mouse.rb +3 -3
- data/lib/ratatui_ruby/event/paste.rb +1 -1
- data/lib/ratatui_ruby/frame.rb +100 -4
- data/lib/ratatui_ruby/layout/constraint.rb +95 -0
- data/lib/ratatui_ruby/layout/layout.rb +106 -0
- data/lib/ratatui_ruby/layout/rect.rb +118 -0
- data/lib/ratatui_ruby/layout.rb +19 -0
- data/lib/ratatui_ruby/list_state.rb +88 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/schema/cursor.rb +5 -0
- data/lib/ratatui_ruby/schema/gauge.rb +3 -1
- data/lib/ratatui_ruby/schema/layout.rb +1 -1
- data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/list.rb +25 -4
- data/lib/ratatui_ruby/schema/list_item.rb +41 -0
- data/lib/ratatui_ruby/schema/rect.rb +43 -0
- data/lib/ratatui_ruby/schema/row.rb +66 -0
- data/lib/ratatui_ruby/schema/style.rb +24 -4
- data/lib/ratatui_ruby/schema/table.rb +29 -11
- data/lib/ratatui_ruby/schema/text.rb +96 -3
- data/lib/ratatui_ruby/scrollbar_state.rb +112 -0
- data/lib/ratatui_ruby/style/style.rb +81 -0
- data/lib/ratatui_ruby/style.rb +15 -0
- data/lib/ratatui_ruby/table_state.rb +90 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +169 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +414 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +351 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +127 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +68 -0
- data/lib/ratatui_ruby/test_helper.rb +65 -358
- data/lib/ratatui_ruby/tui/buffer_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/core.rb +38 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +74 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +33 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +20 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +44 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +195 -0
- data/lib/ratatui_ruby/tui.rb +75 -0
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +47 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +25 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +239 -0
- data/lib/ratatui_ruby/widgets/block.rb +192 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +84 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +231 -0
- data/lib/ratatui_ruby/widgets/cell.rb +47 -0
- data/lib/ratatui_ruby/widgets/center.rb +59 -0
- data/lib/ratatui_ruby/widgets/chart.rb +185 -0
- data/lib/ratatui_ruby/widgets/clear.rb +54 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +42 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +72 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +80 -0
- data/lib/ratatui_ruby/widgets/list.rb +127 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +43 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +43 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +99 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +31 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +36 -0
- data/lib/ratatui_ruby/widgets/row.rb +68 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +143 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +68 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +134 -0
- data/lib/ratatui_ruby/widgets/table.rb +141 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +85 -0
- data/lib/ratatui_ruby/widgets.rb +40 -0
- data/lib/ratatui_ruby.rb +64 -57
- data/sig/examples/app_all_events/view.rbs +1 -1
- data/sig/examples/app_all_events/view_state.rbs +1 -1
- data/sig/examples/app_stateful_interaction/app.rbs +33 -0
- data/sig/examples/widget_block_demo/app.rbs +32 -0
- data/sig/examples/{app_map_demo → widget_map_demo}/app.rbs +2 -2
- data/sig/examples/{app_table_select → widget_table_demo}/app.rbs +2 -2
- data/sig/examples/{widget_table_flex → widget_text_width}/app.rbs +2 -3
- data/sig/ratatui_ruby/event.rbs +11 -1
- data/sig/ratatui_ruby/frame.rbs +2 -0
- data/sig/ratatui_ruby/list_state.rbs +13 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -2
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +3 -3
- data/sig/ratatui_ruby/schema/gauge.rbs +2 -2
- data/sig/ratatui_ruby/schema/line_gauge.rbs +2 -2
- data/sig/ratatui_ruby/schema/list.rbs +4 -2
- data/sig/ratatui_ruby/schema/list_item.rbs +10 -0
- data/sig/ratatui_ruby/schema/rect.rbs +3 -0
- data/sig/ratatui_ruby/schema/row.rbs +22 -0
- data/sig/ratatui_ruby/schema/style.rbs +3 -3
- data/sig/ratatui_ruby/schema/table.rbs +3 -1
- data/sig/ratatui_ruby/schema/text.rbs +9 -6
- data/sig/ratatui_ruby/scrollbar_state.rbs +18 -0
- data/sig/ratatui_ruby/session.rbs +41 -48
- data/sig/ratatui_ruby/table_state.rbs +15 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +16 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +12 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +64 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +14 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +22 -0
- data/sig/ratatui_ruby/test_helper.rbs +5 -4
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/core.rbs +14 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +19 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +12 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +10 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +14 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui.rbs +19 -0
- data/tasks/autodoc/examples.rb +79 -0
- data/tasks/autodoc.rake +7 -35
- data/tasks/bump/changelog.rb +3 -3
- data/tasks/bump/links.rb +67 -0
- data/tasks/sourcehut.rake +64 -21
- data/tasks/terminal_preview/app_screenshot.rb +13 -3
- data/tasks/terminal_preview/saved_screenshot.rb +4 -3
- metadata +169 -48
- data/doc/contributors/dwim_dx.md +0 -366
- data/doc/images/app_analytics.png +0 -0
- data/doc/images/app_custom_widget.png +0 -0
- data/doc/images/app_mouse_events.png +0 -0
- data/doc/images/app_table_select.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_list_styles.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/examples/app_all_events/model/events.rb +0 -180
- data/examples/app_all_events/model/highlight.rb +0 -57
- data/examples/app_all_events/test/snapshots/after_focus_lost.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_focus_regained.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_key_a.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_mouse_click.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_multiple_events.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_paste.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_resize.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_right_click.txt +0 -24
- data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +0 -24
- data/examples/app_all_events/test/snapshots/initial_state.txt +0 -24
- data/examples/app_all_events/view_state.rb +0 -42
- data/examples/app_color_picker/scene.rb +0 -201
- data/examples/widget_block_padding/app.rb +0 -67
- data/examples/widget_block_titles/app.rb +0 -69
- data/examples/widget_list_styles/app.rb +0 -141
- data/examples/widget_table_flex/app.rb +0 -95
- data/lib/ratatui_ruby/session/autodoc.rb +0 -417
- data/lib/ratatui_ruby/session.rb +0 -163
- data/sig/examples/widget_block_padding/app.rbs +0 -11
- data/sig/examples/widget_block_titles/app.rbs +0 -11
- data/sig/examples/widget_list_styles/app.rbs +0 -11
- data/tasks/autodoc/inventory.rb +0 -61
- data/tasks/autodoc/notice.rb +0 -26
- data/tasks/autodoc/rbs.rb +0 -38
- data/tasks/autodoc/rdoc.rb +0 -45
- data/tasks/bump/comparison_links.rb +0 -41
- /data/doc/images/{app_map_demo.png → widget_map_demo.png} +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "timeout"
|
|
4
|
-
require "minitest/mock"
|
|
5
3
|
require "fileutils"
|
|
4
|
+
require_relative "test_helper/terminal"
|
|
5
|
+
require_relative "test_helper/snapshot"
|
|
6
|
+
require_relative "test_helper/event_injection"
|
|
7
|
+
require_relative "test_helper/style_assertions"
|
|
8
|
+
require_relative "test_helper/test_doubles"
|
|
6
9
|
|
|
7
10
|
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
8
11
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -11,374 +14,78 @@ module RatatuiRuby
|
|
|
11
14
|
##
|
|
12
15
|
# Helpers for testing RatatuiRuby applications.
|
|
13
16
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
17
|
+
# Writing TUI tests by hand is tedious. You need a headless terminal, event
|
|
18
|
+
# injection, snapshot comparisons, and style assertions. Wiring all that up
|
|
19
|
+
# yourself is error-prone.
|
|
16
20
|
#
|
|
17
|
-
#
|
|
21
|
+
# This module bundles everything you need. Include it in your test class and
|
|
22
|
+
# start writing tests immediately.
|
|
23
|
+
#
|
|
24
|
+
# == Included Mixins
|
|
25
|
+
#
|
|
26
|
+
# [Terminal] Sets up a headless terminal and queries its buffer.
|
|
27
|
+
# [Snapshot] Compares the screen against stored reference files.
|
|
28
|
+
# [EventInjection] Simulates keypresses, mouse clicks, and resize events.
|
|
29
|
+
# [StyleAssertions] Checks foreground color, background color, and text modifiers.
|
|
30
|
+
# [TestDoubles] Provides mocks and stubs for testing views in isolation.
|
|
31
|
+
#
|
|
32
|
+
# == Example
|
|
18
33
|
#
|
|
19
34
|
# require "ratatui_ruby/test_helper"
|
|
20
35
|
#
|
|
21
|
-
# class
|
|
36
|
+
# class TestMyApp < Minitest::Test
|
|
22
37
|
# include RatatuiRuby::TestHelper
|
|
23
38
|
#
|
|
24
|
-
# def
|
|
39
|
+
# def test_initial_render
|
|
25
40
|
# with_test_terminal(80, 24) do
|
|
26
|
-
#
|
|
27
|
-
#
|
|
41
|
+
# MyApp.new.run_once
|
|
42
|
+
# assert_snapshot("initial")
|
|
43
|
+
# end
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# def test_themes
|
|
47
|
+
# with_test_terminal do
|
|
48
|
+
# app = ThemeDemo.new
|
|
49
|
+
# app.run_once
|
|
50
|
+
# assert_rich_snapshot("default_theme")
|
|
51
|
+
#
|
|
52
|
+
# inject_key("t", modifiers: [:ctrl])
|
|
53
|
+
# app.run_once
|
|
54
|
+
# assert_rich_snapshot("dark_theme")
|
|
55
|
+
#
|
|
56
|
+
# inject_key("t", modifiers: [:ctrl])
|
|
57
|
+
# app.run_once
|
|
58
|
+
# assert_rich_snapshot("high_contrast_theme")
|
|
59
|
+
# end
|
|
60
|
+
# end
|
|
61
|
+
#
|
|
62
|
+
# def test_highlighter_applies_selection_style
|
|
63
|
+
# with_test_terminal(40, 5) do
|
|
64
|
+
# RatatuiRuby.draw do |frame|
|
|
65
|
+
# highlighter = MyApp::UI::Highlighter.new(:yellow)
|
|
66
|
+
# highlighter.render_at(frame, 0, 2, "Selected Item")
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# assert_fg_color(:yellow, 0, 2)
|
|
70
|
+
# assert_bold(0, 2)
|
|
28
71
|
# end
|
|
29
72
|
# end
|
|
30
73
|
#
|
|
31
|
-
# def
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
74
|
+
# def test_view_in_isolation
|
|
75
|
+
# frame = MockFrame.new
|
|
76
|
+
# area = StubRect.new(width: 60, height: 20)
|
|
77
|
+
#
|
|
78
|
+
# MyView.new.call(state, tui, frame, area)
|
|
79
|
+
#
|
|
80
|
+
# widget = frame.rendered_widgets.first[:widget]
|
|
81
|
+
# assert_equal "Dashboard", widget.block.title
|
|
35
82
|
# end
|
|
36
83
|
# end
|
|
37
84
|
module TestHelper
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# +height+:: height of the test terminal (default: 24)
|
|
44
|
-
#
|
|
45
|
-
# +timeout+:: maximum execution time in seconds (default: 2). Pass nil to disable.
|
|
46
|
-
#
|
|
47
|
-
# If a block is given, it is executed within the test terminal context.
|
|
48
|
-
def with_test_terminal(width = 80, height = 24, **opts)
|
|
49
|
-
RatatuiRuby.init_test_terminal(width, height)
|
|
50
|
-
# Flush any lingering events from previous tests
|
|
51
|
-
while (event = RatatuiRuby.poll_event) && !event.none?; end
|
|
52
|
-
|
|
53
|
-
RatatuiRuby.stub :init_terminal, nil do
|
|
54
|
-
RatatuiRuby.stub :restore_terminal, nil do
|
|
55
|
-
@_ratatui_test_terminal_active = true
|
|
56
|
-
timeout = opts.fetch(:timeout, 2)
|
|
57
|
-
if timeout
|
|
58
|
-
Timeout.timeout(timeout) do
|
|
59
|
-
yield
|
|
60
|
-
end
|
|
61
|
-
else
|
|
62
|
-
yield
|
|
63
|
-
end
|
|
64
|
-
ensure
|
|
65
|
-
@_ratatui_test_terminal_active = false
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
ensure
|
|
69
|
-
RatatuiRuby.restore_terminal
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
##
|
|
73
|
-
# Returns the current content of the terminal buffer as an array of strings.
|
|
74
|
-
# Each string represents a row in the terminal.
|
|
75
|
-
#
|
|
76
|
-
# buffer_content
|
|
77
|
-
# # => ["Row 1 text", "Row 2 text", ...]
|
|
78
|
-
def buffer_content
|
|
79
|
-
RatatuiRuby.get_buffer_content.split("\n")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
##
|
|
83
|
-
# Returns the current cursor position as a hash with +:x+ and +:y+ keys.
|
|
84
|
-
#
|
|
85
|
-
# cursor_position
|
|
86
|
-
# # => { x: 0, y: 0 }
|
|
87
|
-
def cursor_position
|
|
88
|
-
x, y = RatatuiRuby.get_cursor_position
|
|
89
|
-
{ x:, y: }
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
##
|
|
93
|
-
# Injects an event into the event queue for testing.
|
|
94
|
-
#
|
|
95
|
-
# Pass any RatatuiRuby::Event object. The event will be returned by
|
|
96
|
-
# the next call to RatatuiRuby.poll_event.
|
|
97
|
-
#
|
|
98
|
-
# Raises a +RuntimeError+ if called outside of a +with_test_terminal+ block.
|
|
99
|
-
#
|
|
100
|
-
# == Examples
|
|
101
|
-
#
|
|
102
|
-
# with_test_terminal do
|
|
103
|
-
# # Key events
|
|
104
|
-
# inject_event(RatatuiRuby::Event::Key.new(code: "q"))
|
|
105
|
-
# inject_event(RatatuiRuby::Event::Key.new(code: "s", modifiers: ["ctrl"]))
|
|
106
|
-
#
|
|
107
|
-
# # Mouse events
|
|
108
|
-
# inject_event(RatatuiRuby::Event::Mouse.new(kind: "down", button: "left", x: 10, y: 5))
|
|
109
|
-
#
|
|
110
|
-
# # Resize events
|
|
111
|
-
# inject_event(RatatuiRuby::Event::Resize.new(width: 120, height: 40))
|
|
112
|
-
#
|
|
113
|
-
# # Paste events
|
|
114
|
-
# inject_event(RatatuiRuby::Event::Paste.new(content: "Hello"))
|
|
115
|
-
#
|
|
116
|
-
# # Focus events
|
|
117
|
-
# inject_event(RatatuiRuby::Event::FocusGained.new)
|
|
118
|
-
# inject_event(RatatuiRuby::Event::FocusLost.new)
|
|
119
|
-
# end
|
|
120
|
-
def inject_event(event)
|
|
121
|
-
unless @_ratatui_test_terminal_active
|
|
122
|
-
raise "Events must be injected inside a `with_test_terminal` block. " \
|
|
123
|
-
"Calling this method outside the block causes a race condition where the event " \
|
|
124
|
-
"is flushed before the application starts."
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
case event
|
|
128
|
-
when RatatuiRuby::Event::Key
|
|
129
|
-
RatatuiRuby.inject_test_event("key", { code: event.code, modifiers: event.modifiers })
|
|
130
|
-
when RatatuiRuby::Event::Mouse
|
|
131
|
-
RatatuiRuby.inject_test_event("mouse", {
|
|
132
|
-
kind: event.kind,
|
|
133
|
-
button: event.button,
|
|
134
|
-
x: event.x,
|
|
135
|
-
y: event.y,
|
|
136
|
-
modifiers: event.modifiers,
|
|
137
|
-
})
|
|
138
|
-
when RatatuiRuby::Event::Resize
|
|
139
|
-
RatatuiRuby.inject_test_event("resize", { width: event.width, height: event.height })
|
|
140
|
-
when RatatuiRuby::Event::Paste
|
|
141
|
-
RatatuiRuby.inject_test_event("paste", { content: event.content })
|
|
142
|
-
when RatatuiRuby::Event::FocusGained
|
|
143
|
-
RatatuiRuby.inject_test_event("focus_gained", {})
|
|
144
|
-
when RatatuiRuby::Event::FocusLost
|
|
145
|
-
RatatuiRuby.inject_test_event("focus_lost", {})
|
|
146
|
-
else
|
|
147
|
-
raise ArgumentError, "Unknown event type: #{event.class}"
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
##
|
|
152
|
-
# Injects a mouse event.
|
|
153
|
-
#
|
|
154
|
-
# inject_mouse(x: 10, y: 5, kind: :down, button: :left)
|
|
155
|
-
def inject_mouse(x:, y:, kind: :down, modifiers: [], button: :left)
|
|
156
|
-
event = RatatuiRuby::Event::Mouse.new(
|
|
157
|
-
kind: kind.to_s,
|
|
158
|
-
x:,
|
|
159
|
-
y:,
|
|
160
|
-
button: button.to_s,
|
|
161
|
-
modifiers:
|
|
162
|
-
)
|
|
163
|
-
inject_event(event)
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
##
|
|
167
|
-
# Injects a mouse left click (down) event.
|
|
168
|
-
#
|
|
169
|
-
# inject_click(x: 10, y: 5)
|
|
170
|
-
def inject_click(x:, y:, modifiers: [])
|
|
171
|
-
inject_mouse(x:, y:, kind: :down, modifiers:, button: :left)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
##
|
|
175
|
-
# Injects a mouse right click (down) event.
|
|
176
|
-
#
|
|
177
|
-
# inject_right_click(x: 10, y: 5)
|
|
178
|
-
def inject_right_click(x:, y:, modifiers: [])
|
|
179
|
-
inject_mouse(x:, y:, kind: :down, modifiers:, button: :right)
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
##
|
|
183
|
-
# Injects a mouse drag event.
|
|
184
|
-
#
|
|
185
|
-
# inject_drag(x: 10, y: 5)
|
|
186
|
-
def inject_drag(x:, y:, modifiers: [], button: :left)
|
|
187
|
-
inject_mouse(x:, y:, kind: :drag, modifiers:, button:)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
##
|
|
191
|
-
# Injects multiple Key events into the queue.
|
|
192
|
-
#
|
|
193
|
-
# Supports multiple formats for convenience:
|
|
194
|
-
#
|
|
195
|
-
# * String: Converted to a Key event with that code.
|
|
196
|
-
# * Symbol: Parsed as modifier_code (e.g., <tt>:ctrl_c</tt>, <tt>:enter</tt>).
|
|
197
|
-
# * Hash: Passed to Key.new constructor.
|
|
198
|
-
# * Key: Passed directly.
|
|
199
|
-
#
|
|
200
|
-
# == Examples
|
|
201
|
-
#
|
|
202
|
-
# with_test_terminal do
|
|
203
|
-
# inject_keys("a", "b", "c")
|
|
204
|
-
# inject_keys(:enter, :esc)
|
|
205
|
-
# inject_keys(:ctrl_c, :alt_shift_left)
|
|
206
|
-
# inject_keys("j", { code: "k", modifiers: ["ctrl"] })
|
|
207
|
-
# end
|
|
208
|
-
def inject_keys(*args)
|
|
209
|
-
args.each do |arg|
|
|
210
|
-
event = case arg
|
|
211
|
-
when String
|
|
212
|
-
RatatuiRuby::Event::Key.new(code: arg)
|
|
213
|
-
when Symbol
|
|
214
|
-
parts = arg.to_s.split("_")
|
|
215
|
-
code = parts.pop
|
|
216
|
-
modifiers = parts
|
|
217
|
-
RatatuiRuby::Event::Key.new(code:, modifiers:)
|
|
218
|
-
when Hash
|
|
219
|
-
RatatuiRuby::Event::Key.new(**arg)
|
|
220
|
-
when RatatuiRuby::Event::Key
|
|
221
|
-
arg
|
|
222
|
-
else
|
|
223
|
-
raise ArgumentError, "Invalid key argument: #{arg.inspect}. Expected String, Symbol, Hash, or Key event."
|
|
224
|
-
end
|
|
225
|
-
inject_event(event)
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
alias inject_key inject_keys
|
|
229
|
-
|
|
230
|
-
##
|
|
231
|
-
# Returns the cell attributes at the given coordinates.
|
|
232
|
-
#
|
|
233
|
-
# get_cell(0, 0)
|
|
234
|
-
# # => { "symbol" => "H", "fg" => :red, "bg" => nil }
|
|
235
|
-
def get_cell(x, y)
|
|
236
|
-
RatatuiRuby.get_cell_at(x, y)
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
##
|
|
240
|
-
# Asserts that the cell at the given coordinates has the expected attributes.
|
|
241
|
-
#
|
|
242
|
-
# assert_cell_style(0, 0, char: "H", fg: :red)
|
|
243
|
-
def assert_cell_style(x, y, **expected_attributes)
|
|
244
|
-
cell = get_cell(x, y)
|
|
245
|
-
expected_attributes.each do |key, value|
|
|
246
|
-
actual_value = cell.public_send(key)
|
|
247
|
-
if value.nil?
|
|
248
|
-
assert_nil actual_value, "Expected cell at (#{x}, #{y}) to have #{key}=nil, but got #{actual_value.inspect}"
|
|
249
|
-
else
|
|
250
|
-
assert_equal value, actual_value, "Expected cell at (#{x}, #{y}) to have #{key}=#{value.inspect}, but got #{actual_value.inspect}"
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
##
|
|
256
|
-
# Mock frame for unit testing views.
|
|
257
|
-
#
|
|
258
|
-
# Captures widgets passed to +render_widget+ for inspection.
|
|
259
|
-
# Does not render anything—purely captures the output.
|
|
260
|
-
#
|
|
261
|
-
# == Examples
|
|
262
|
-
#
|
|
263
|
-
# frame = MockFrame.new
|
|
264
|
-
# View::Log.new.call(state, tui, frame, area)
|
|
265
|
-
# widget = frame.rendered_widgets.first[:widget]
|
|
266
|
-
# assert_equal "Event Log", widget.block.title
|
|
267
|
-
MockFrame = Data.define(:rendered_widgets) do
|
|
268
|
-
def initialize(rendered_widgets: [])
|
|
269
|
-
super
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
def render_widget(widget, area)
|
|
273
|
-
rendered_widgets << { widget:, area: }
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
|
-
##
|
|
278
|
-
# Stub area for unit testing views.
|
|
279
|
-
#
|
|
280
|
-
# Provides the minimal interface views expect (+width+, +height+).
|
|
281
|
-
#
|
|
282
|
-
# == Examples
|
|
283
|
-
#
|
|
284
|
-
# area = StubRect.new(width: 60, height: 20)
|
|
285
|
-
StubRect = Data.define(:x, :y, :width, :height) do
|
|
286
|
-
def initialize(x: 0, y: 0, width: 80, height: 24)
|
|
287
|
-
super
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
##
|
|
292
|
-
# Asserts that the current screen content matches a stored snapshot.
|
|
293
|
-
#
|
|
294
|
-
# This method simplifies snapshot testing by automatically resolving the snapshot path
|
|
295
|
-
# relative to the test file calling this method. It assumes a "snapshots" directory
|
|
296
|
-
# exists in the same directory as the test file.
|
|
297
|
-
#
|
|
298
|
-
# # In test/test_login.rb
|
|
299
|
-
# assert_snapshot("login_screen")
|
|
300
|
-
# # Look for: test/snapshots/login_screen.txt
|
|
301
|
-
#
|
|
302
|
-
# # With normalization block
|
|
303
|
-
# assert_snapshot("clock") do |actual|
|
|
304
|
-
# actual.map { |l| l.gsub(/\d{2}:\d{2}/, "XX:XX") }
|
|
305
|
-
# end
|
|
306
|
-
#
|
|
307
|
-
# [name] String name of the snapshot (without extension).
|
|
308
|
-
# [msg] String optional failure message.
|
|
309
|
-
def assert_snapshot(name, msg = nil, &)
|
|
310
|
-
# Get the path of the test file calling this method
|
|
311
|
-
caller_path = caller_locations(1, 1).first.path
|
|
312
|
-
snapshot_dir = File.join(File.dirname(caller_path), "snapshots")
|
|
313
|
-
snapshot_path = File.join(snapshot_dir, "#{name}.txt")
|
|
314
|
-
|
|
315
|
-
assert_screen_matches(snapshot_path, msg, &)
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
##
|
|
319
|
-
# Asserts that the current screen content matches the expected content.
|
|
320
|
-
#
|
|
321
|
-
# Users need to verify that the entire TUI screen looks exactly as expected.
|
|
322
|
-
# Manually checking every cell or line is tedious and error-prone.
|
|
323
|
-
#
|
|
324
|
-
# This helper compares the current buffer content against an expected string (file path)
|
|
325
|
-
# or array of strings. It supports automatic snapshot creation and updating via
|
|
326
|
-
# the +UPDATE_SNAPSHOTS+ environment variable.
|
|
327
|
-
#
|
|
328
|
-
# Use it to verify complex UI states, layouts, and renderings.
|
|
329
|
-
#
|
|
330
|
-
# == Usage
|
|
331
|
-
#
|
|
332
|
-
# # Direct comparison
|
|
333
|
-
# assert_screen_matches(["Line 1", "Line 2"])
|
|
334
|
-
#
|
|
335
|
-
# # File comparison
|
|
336
|
-
# assert_screen_matches("test/snapshots/login.txt")
|
|
337
|
-
#
|
|
338
|
-
# # With normalization (e.g., masking dynamic data)
|
|
339
|
-
# assert_screen_matches("test/snapshots/dashboard.txt") do |lines|
|
|
340
|
-
# lines.map { |l| l.gsub(/User ID: \d+/, "User ID: XXX") }
|
|
341
|
-
# end
|
|
342
|
-
#
|
|
343
|
-
# [expected] String (file path) or Array<String> (content).
|
|
344
|
-
# [msg] String optional failure message.
|
|
345
|
-
def assert_screen_matches(expected, msg = nil)
|
|
346
|
-
actual_lines = buffer_content
|
|
347
|
-
|
|
348
|
-
if block_given?
|
|
349
|
-
actual_lines = yield(actual_lines)
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
if expected.is_a?(String)
|
|
353
|
-
# Snapshot file mode
|
|
354
|
-
snapshot_path = expected
|
|
355
|
-
update_snapshots = ENV["UPDATE_SNAPSHOTS"] == "1" || ENV["UPDATE_SNAPSHOTS"] == "true"
|
|
356
|
-
|
|
357
|
-
if !File.exist?(snapshot_path) || update_snapshots
|
|
358
|
-
FileUtils.mkdir_p(File.dirname(snapshot_path))
|
|
359
|
-
File.write(snapshot_path, "#{actual_lines.join("\n")}\n")
|
|
360
|
-
if update_snapshots
|
|
361
|
-
puts "Updated snapshot: #{snapshot_path}"
|
|
362
|
-
else
|
|
363
|
-
puts "Created snapshot: #{snapshot_path}"
|
|
364
|
-
end
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
expected_lines = File.readlines(snapshot_path, chomp: true)
|
|
368
|
-
else
|
|
369
|
-
# Direct comparison mode
|
|
370
|
-
expected_lines = expected
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
msg ||= "Screen content mismatch"
|
|
374
|
-
|
|
375
|
-
assert_equal expected_lines.size, actual_lines.size, "#{msg}: Line count mismatch"
|
|
376
|
-
|
|
377
|
-
expected_lines.each_with_index do |expected_line, i|
|
|
378
|
-
actual_line = actual_lines[i]
|
|
379
|
-
assert_equal expected_line, actual_line,
|
|
380
|
-
"#{msg}: Line #{i + 1} mismatch.\nExpected: #{expected_line.inspect}\nActual: #{actual_line.inspect}"
|
|
381
|
-
end
|
|
382
|
-
end
|
|
85
|
+
include Terminal
|
|
86
|
+
include Snapshot
|
|
87
|
+
include EventInjection
|
|
88
|
+
include StyleAssertions
|
|
89
|
+
include TestDoubles
|
|
383
90
|
end
|
|
384
91
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
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 TUI
|
|
8
|
+
# Buffer inspection factory methods for Session.
|
|
9
|
+
#
|
|
10
|
+
# Provides convenient access to Buffer::Cell for testing
|
|
11
|
+
# and buffer inspection purposes.
|
|
12
|
+
module BufferFactories
|
|
13
|
+
# Creates a Buffer::Cell (for testing).
|
|
14
|
+
# @return [Buffer::Cell]
|
|
15
|
+
def cell(...)
|
|
16
|
+
Buffer::Cell.new(...)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class TUI
|
|
8
|
+
# Canvas shape factory methods for Session.
|
|
9
|
+
#
|
|
10
|
+
# Provides convenient access to Widgets::Shape::* classes
|
|
11
|
+
# for creating custom drawings on Canvas widgets.
|
|
12
|
+
module CanvasFactories
|
|
13
|
+
# Creates a map shape for Canvas.
|
|
14
|
+
# @return [Widgets::Shape::Map]
|
|
15
|
+
def shape_map(...)
|
|
16
|
+
Widgets::Shape::Map.new(...)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Creates a line shape for Canvas.
|
|
20
|
+
# @return [Widgets::Shape::Line]
|
|
21
|
+
def shape_line(...)
|
|
22
|
+
Widgets::Shape::Line.new(...)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Creates a point (single pixel) shape for Canvas.
|
|
26
|
+
# @return [Widgets::Shape::Point]
|
|
27
|
+
def shape_point(...)
|
|
28
|
+
Widgets::Shape::Point.new(...)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Creates a circle shape for Canvas.
|
|
32
|
+
# @return [Widgets::Shape::Circle]
|
|
33
|
+
def shape_circle(...)
|
|
34
|
+
Widgets::Shape::Circle.new(...)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Creates a rectangle shape for Canvas.
|
|
38
|
+
# @return [Widgets::Shape::Rectangle]
|
|
39
|
+
def shape_rectangle(...)
|
|
40
|
+
Widgets::Shape::Rectangle.new(...)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
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 TUI
|
|
8
|
+
# Core terminal methods delegated to RatatuiRuby module.
|
|
9
|
+
#
|
|
10
|
+
# These are the fundamental operations for the render loop:
|
|
11
|
+
# drawing UI, polling events, and inspecting the buffer.
|
|
12
|
+
module Core
|
|
13
|
+
# Draws the given UI node tree to the terminal.
|
|
14
|
+
# @see RatatuiRuby.draw
|
|
15
|
+
def draw(tree = nil, &)
|
|
16
|
+
RatatuiRuby.draw(tree, &)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Checks for user input.
|
|
20
|
+
# @see RatatuiRuby.poll_event
|
|
21
|
+
def poll_event(timeout: 0.016)
|
|
22
|
+
RatatuiRuby.poll_event(timeout:)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Inspects the terminal buffer at specific coordinates.
|
|
26
|
+
# @see RatatuiRuby.get_cell_at
|
|
27
|
+
def get_cell_at(x, y)
|
|
28
|
+
RatatuiRuby.get_cell_at(x, y)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Creates a Draw::CellCmd for placing a cell at coordinates.
|
|
32
|
+
# @return [Draw::CellCmd]
|
|
33
|
+
def draw_cell(x, y, cell)
|
|
34
|
+
Draw.cell(x, y, cell)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
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 TUI
|
|
8
|
+
# Layout factory methods for Session.
|
|
9
|
+
#
|
|
10
|
+
# Provides convenient access to Layout::Rect, Layout::Constraint,
|
|
11
|
+
# and Layout::Layout without fully qualifying the class names.
|
|
12
|
+
module LayoutFactories
|
|
13
|
+
# Creates a Layout::Rect.
|
|
14
|
+
# @return [Layout::Rect]
|
|
15
|
+
def rect(...)
|
|
16
|
+
Layout::Rect.new(...)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Creates a Layout::Constraint.
|
|
20
|
+
# @return [Layout::Constraint]
|
|
21
|
+
def constraint(...)
|
|
22
|
+
Layout::Constraint.new(...)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Creates a Layout::Constraint.length.
|
|
26
|
+
# @return [Layout::Constraint]
|
|
27
|
+
def constraint_length(n)
|
|
28
|
+
Layout::Constraint.length(n)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Creates a Layout::Constraint.percentage.
|
|
32
|
+
# @return [Layout::Constraint]
|
|
33
|
+
def constraint_percentage(n)
|
|
34
|
+
Layout::Constraint.percentage(n)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Creates a Layout::Constraint.min.
|
|
38
|
+
# @return [Layout::Constraint]
|
|
39
|
+
def constraint_min(n)
|
|
40
|
+
Layout::Constraint.min(n)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Creates a Layout::Constraint.max.
|
|
44
|
+
# @return [Layout::Constraint]
|
|
45
|
+
def constraint_max(n)
|
|
46
|
+
Layout::Constraint.max(n)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Creates a Layout::Constraint.fill.
|
|
50
|
+
# @return [Layout::Constraint]
|
|
51
|
+
def constraint_fill(n = 1)
|
|
52
|
+
Layout::Constraint.fill(n)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Creates a Layout::Constraint.ratio.
|
|
56
|
+
# @return [Layout::Constraint]
|
|
57
|
+
def constraint_ratio(numerator, denominator)
|
|
58
|
+
Layout::Constraint.ratio(numerator, denominator)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Creates a Layout::Layout.
|
|
62
|
+
# @return [Layout::Layout]
|
|
63
|
+
def layout(...)
|
|
64
|
+
Layout::Layout.new(...)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Splits an area using Layout::Layout.split.
|
|
68
|
+
# @return [Array<Layout::Rect>]
|
|
69
|
+
def layout_split(area, direction: :vertical, constraints:, flex: :legacy)
|
|
70
|
+
Layout::Layout.split(area, direction:, constraints:, flex:)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
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 TUI
|
|
8
|
+
# State object factory methods for Session.
|
|
9
|
+
#
|
|
10
|
+
# Provides convenient access to stateful widget state objects
|
|
11
|
+
# (ListState, TableState, ScrollbarState) without fully
|
|
12
|
+
# qualifying the class names.
|
|
13
|
+
module StateFactories
|
|
14
|
+
# Creates a ListState.
|
|
15
|
+
# @return [ListState]
|
|
16
|
+
def list_state(...)
|
|
17
|
+
ListState.new(...)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Creates a TableState.
|
|
21
|
+
# @return [TableState]
|
|
22
|
+
def table_state(...)
|
|
23
|
+
TableState.new(...)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Creates a ScrollbarState.
|
|
27
|
+
# @return [ScrollbarState]
|
|
28
|
+
def scrollbar_state(...)
|
|
29
|
+
ScrollbarState.new(...)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
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 TUI
|
|
8
|
+
# Style factory methods for Session.
|
|
9
|
+
#
|
|
10
|
+
# Provides convenient access to Style::Style without fully
|
|
11
|
+
# qualifying the class name.
|
|
12
|
+
module StyleFactories
|
|
13
|
+
# Creates a Style::Style.
|
|
14
|
+
# @return [Style::Style]
|
|
15
|
+
def style(...)
|
|
16
|
+
Style::Style.new(...)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|