ratatui_ruby 0.3.1 → 0.4.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 +54 -13
- data/CHANGELOG.md +186 -1
- data/README.md +17 -15
- data/doc/application_architecture.md +116 -0
- data/doc/application_testing.md +12 -7
- data/doc/contributors/better_dx.md +543 -0
- data/doc/contributors/design/ruby_frontend.md +1 -1
- data/doc/contributors/developing_examples.md +203 -0
- data/doc/contributors/documentation_style.md +97 -0
- data/doc/contributors/dwim_dx.md +366 -0
- data/doc/contributors/example_analysis.md +82 -0
- data/doc/custom.css +14 -0
- data/doc/event_handling.md +119 -0
- data/doc/images/all_events.png +0 -0
- data/doc/images/analytics.png +0 -0
- data/doc/images/block_padding.png +0 -0
- data/doc/images/block_titles.png +0 -0
- data/doc/images/box_demo.png +0 -0
- data/doc/images/calendar_demo.png +0 -0
- data/doc/images/cell_demo.png +0 -0
- data/doc/images/chart_demo.png +0 -0
- data/doc/images/custom_widget.png +0 -0
- data/doc/images/flex_layout.png +0 -0
- data/doc/images/gauge_demo.png +0 -0
- data/doc/images/hit_test.png +0 -0
- data/doc/images/line_gauge_demo.png +0 -0
- data/doc/images/list_demo.png +0 -0
- data/doc/images/list_styles.png +0 -0
- data/doc/images/login_form.png +0 -0
- data/doc/images/map_demo.png +0 -0
- data/doc/images/mouse_events.png +0 -0
- data/doc/images/popup_demo.png +0 -0
- data/doc/images/quickstart_dsl.png +0 -0
- data/doc/images/quickstart_lifecycle.png +0 -0
- data/doc/images/ratatui_logo_demo.png +0 -0
- data/doc/images/readme_usage.png +0 -0
- data/doc/images/rich_text.png +0 -0
- data/doc/images/scroll_text.png +0 -0
- data/doc/images/scrollbar_demo.png +0 -0
- data/doc/images/sparkline_demo.png +0 -0
- data/doc/images/table_flex.png +0 -0
- data/doc/images/table_select.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/index.md +1 -0
- data/doc/interactive_design.md +121 -0
- data/doc/quickstart.md +147 -72
- data/examples/all_events/app.rb +169 -0
- data/examples/all_events/app.rbs +7 -0
- data/examples/all_events/test_app.rb +139 -0
- data/examples/analytics/app.rb +258 -0
- data/examples/analytics/app.rbs +7 -0
- data/examples/analytics/test_app.rb +132 -0
- data/examples/block_padding/app.rb +63 -0
- data/examples/block_padding/app.rbs +7 -0
- data/examples/block_padding/test_app.rb +31 -0
- data/examples/block_titles/app.rb +61 -0
- data/examples/block_titles/app.rbs +7 -0
- data/examples/block_titles/test_app.rb +34 -0
- data/examples/box_demo/app.rb +216 -0
- data/examples/box_demo/app.rbs +7 -0
- data/examples/box_demo/test_app.rb +88 -0
- data/examples/calendar_demo/app.rb +101 -0
- data/examples/calendar_demo/app.rbs +7 -0
- data/examples/calendar_demo/test_app.rb +108 -0
- data/examples/cell_demo/app.rb +108 -0
- data/examples/cell_demo/app.rbs +7 -0
- data/examples/cell_demo/test_app.rb +36 -0
- data/examples/chart_demo/app.rb +203 -0
- data/examples/chart_demo/app.rbs +7 -0
- data/examples/chart_demo/test_app.rb +102 -0
- data/examples/custom_widget/app.rb +51 -0
- data/examples/custom_widget/app.rbs +7 -0
- data/examples/custom_widget/test_app.rb +30 -0
- data/examples/flex_layout/app.rb +156 -0
- data/examples/flex_layout/app.rbs +7 -0
- data/examples/flex_layout/test_app.rb +65 -0
- data/examples/gauge_demo/app.rb +182 -0
- data/examples/gauge_demo/app.rbs +7 -0
- data/examples/gauge_demo/test_app.rb +120 -0
- data/examples/hit_test/app.rb +175 -0
- data/examples/hit_test/app.rbs +7 -0
- data/examples/hit_test/test_app.rb +102 -0
- data/examples/line_gauge_demo/app.rb +190 -0
- data/examples/line_gauge_demo/app.rbs +7 -0
- data/examples/line_gauge_demo/test_app.rb +129 -0
- data/examples/list_demo/app.rb +253 -0
- data/examples/list_demo/app.rbs +12 -0
- data/examples/list_demo/test_app.rb +237 -0
- data/examples/list_styles/app.rb +140 -0
- data/examples/list_styles/app.rbs +7 -0
- data/examples/list_styles/test_app.rb +157 -0
- data/examples/{login_form.rb → login_form/app.rb} +12 -16
- data/examples/login_form/app.rbs +7 -0
- data/examples/login_form/test_app.rb +51 -0
- data/examples/map_demo/app.rb +90 -0
- data/examples/map_demo/app.rbs +7 -0
- data/examples/map_demo/test_app.rb +149 -0
- data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
- data/examples/mouse_events/app.rbs +7 -0
- data/examples/mouse_events/test_app.rb +53 -0
- data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
- data/examples/popup_demo/app.rbs +7 -0
- data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
- data/examples/quickstart_dsl/app.rb +36 -0
- data/examples/quickstart_dsl/app.rbs +7 -0
- data/examples/quickstart_dsl/test_app.rb +29 -0
- data/examples/quickstart_lifecycle/app.rb +39 -0
- data/examples/quickstart_lifecycle/app.rbs +7 -0
- data/examples/quickstart_lifecycle/test_app.rb +29 -0
- data/examples/ratatui_logo_demo/app.rb +79 -0
- data/examples/ratatui_logo_demo/app.rbs +7 -0
- data/examples/ratatui_logo_demo/test_app.rb +51 -0
- data/examples/ratatui_mascot_demo/app.rb +84 -0
- data/examples/ratatui_mascot_demo/app.rbs +7 -0
- data/examples/ratatui_mascot_demo/test_app.rb +47 -0
- data/examples/readme_usage/app.rb +29 -0
- data/examples/readme_usage/app.rbs +7 -0
- data/examples/readme_usage/test_app.rb +29 -0
- data/examples/rich_text/app.rb +141 -0
- data/examples/rich_text/app.rbs +7 -0
- data/examples/rich_text/test_app.rb +166 -0
- data/examples/scroll_text/app.rb +103 -0
- data/examples/scroll_text/app.rbs +7 -0
- data/examples/scroll_text/test_app.rb +110 -0
- data/examples/scrollbar_demo/app.rb +143 -0
- data/examples/scrollbar_demo/app.rbs +7 -0
- data/examples/scrollbar_demo/test_app.rb +77 -0
- data/examples/sparkline_demo/app.rb +240 -0
- data/examples/sparkline_demo/app.rbs +10 -0
- data/examples/sparkline_demo/test_app.rb +107 -0
- data/examples/table_flex/app.rb +65 -0
- data/examples/table_flex/app.rbs +7 -0
- data/examples/table_flex/test_app.rb +36 -0
- data/examples/table_select/app.rb +198 -0
- data/examples/table_select/app.rbs +7 -0
- data/examples/table_select/test_app.rb +180 -0
- data/examples/widget_style_colors/app.rb +104 -0
- data/examples/widget_style_colors/app.rbs +14 -0
- data/examples/widget_style_colors/test_app.rb +48 -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 +218 -229
- data/ext/ratatui_ruby/src/lib.rs +38 -10
- data/ext/ratatui_ruby/src/rendering.rs +90 -10
- data/ext/ratatui_ruby/src/style.rs +281 -98
- data/ext/ratatui_ruby/src/terminal.rs +119 -25
- data/ext/ratatui_ruby/src/text.rs +171 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
- data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
- data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
- data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
- data/ext/ratatui_ruby/src/widgets/chart.rs +69 -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 +218 -15
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
- data/ext/ratatui_ruby/src/widgets/table.rs +173 -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/paste.rb +71 -0
- data/lib/ratatui_ruby/event/resize.rb +80 -0
- data/lib/ratatui_ruby/event.rb +79 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
- data/lib/ratatui_ruby/schema/block.rb +186 -14
- data/lib/ratatui_ruby/schema/calendar.rb +74 -17
- data/lib/ratatui_ruby/schema/canvas.rb +215 -48
- data/lib/ratatui_ruby/schema/center.rb +49 -11
- data/lib/ratatui_ruby/schema/chart.rb +151 -41
- data/lib/ratatui_ruby/schema/clear.rb +41 -72
- data/lib/ratatui_ruby/schema/constraint.rb +82 -22
- data/lib/ratatui_ruby/schema/cursor.rb +27 -9
- data/lib/ratatui_ruby/schema/draw.rb +53 -0
- data/lib/ratatui_ruby/schema/gauge.rb +59 -15
- data/lib/ratatui_ruby/schema/layout.rb +95 -13
- data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
- data/lib/ratatui_ruby/schema/list.rb +93 -19
- data/lib/ratatui_ruby/schema/overlay.rb +34 -8
- data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
- data/lib/ratatui_ruby/schema/rect.rb +64 -15
- data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
- data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
- data/lib/ratatui_ruby/schema/style.rb +49 -21
- data/lib/ratatui_ruby/schema/table.rb +119 -21
- data/lib/ratatui_ruby/schema/tabs.rb +75 -13
- data/lib/ratatui_ruby/schema/text.rb +90 -0
- data/lib/ratatui_ruby/session.rb +146 -0
- data/lib/ratatui_ruby/test_helper.rb +156 -13
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +143 -23
- data/sig/ratatui_ruby/event.rbs +69 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
- 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 +23 -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/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
- 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/tasks/resources/build.yml.erb +13 -11
- data/tasks/terminal_preview/app_screenshot.rb +35 -0
- data/tasks/terminal_preview/crash_report.rb +54 -0
- data/tasks/terminal_preview/example_app.rb +25 -0
- data/tasks/terminal_preview/launcher_script.rb +48 -0
- data/tasks/terminal_preview/preview_collection.rb +60 -0
- data/tasks/terminal_preview/preview_timing.rb +22 -0
- data/tasks/terminal_preview/safety_confirmation.rb +58 -0
- data/tasks/terminal_preview/saved_screenshot.rb +55 -0
- data/tasks/terminal_preview/system_appearance.rb +11 -0
- data/tasks/terminal_preview/terminal_window.rb +138 -0
- data/tasks/terminal_preview/window_id.rb +14 -0
- data/tasks/terminal_preview.rake +28 -0
- data/tasks/test.rake +1 -1
- metadata +174 -53
- 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/map_demo.rb +0 -58
- 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_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,82 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Example Analysis
|
|
7
|
+
|
|
8
|
+
The `examples/` directory contains examples in three distinct categories, each serving a different purpose.
|
|
9
|
+
|
|
10
|
+
## Category 1: Widget Showcases
|
|
11
|
+
|
|
12
|
+
Single-widget (or widget-focused) examples that exhaustively demonstrate a widget's configuration options through interactive attribute cycling.
|
|
13
|
+
|
|
14
|
+
These examples follow the pattern described in `developing_examples.md`: they expose all major widget parameters as hotkey-controllable options so users can interactively explore the behavior. They render at most two widgets side-by-side or vertically stacked for comparison purposes, all in service of the primary widget.
|
|
15
|
+
|
|
16
|
+
**Examples:**
|
|
17
|
+
- `box_demo`: Demonstrates Block widget variations (border types, styles, padding, titles).
|
|
18
|
+
- `gauge_demo`: Demonstrates Gauge with adjustable ratio, color, Unicode flag, and label modes.
|
|
19
|
+
- `list_demo`: Demonstrates List with items, highlight styles, symbols, spacing, direction, and scroll padding.
|
|
20
|
+
- `line_gauge_demo`: Demonstrates LineGauge widget attributes.
|
|
21
|
+
- `sparkline_demo`: Demonstrates Sparkline with direction, style, absent value markers, and bar sets.
|
|
22
|
+
- `scrollbar_demo`: Demonstrates Scrollbar with orientation and theme cycling.
|
|
23
|
+
- `chart_demo`: Demonstrates Chart widget attributes.
|
|
24
|
+
- `calendar_demo`: Demonstrates Calendar widget.
|
|
25
|
+
- `cell_demo`: Demonstrates Cell widget.
|
|
26
|
+
- `block_titles`: Demonstrates Block title positioning and alignment.
|
|
27
|
+
- `block_padding`: Demonstrates Block padding (uniform and directional).
|
|
28
|
+
- `ratatui_logo_demo`: Demonstrates RatatuiLogo with style cycling.
|
|
29
|
+
- `ratatui_mascot_demo`: Demonstrates RatatuiMascot with style cycling.
|
|
30
|
+
- `list_styles`: Demonstrates List styling variations.
|
|
31
|
+
- `popup_demo`: Demonstrates Popup widget positioning and behavior.
|
|
32
|
+
- `scroll_text`: Demonstrates text scrolling behavior.
|
|
33
|
+
|
|
34
|
+
## Category 2: Real-Application Showcases
|
|
35
|
+
|
|
36
|
+
Examples that function as proof-of-concept TUI applications, demonstrating how to build moderately complex, interactive programs.
|
|
37
|
+
|
|
38
|
+
These are not API documentation—they do not systematically cycle through all widget parameters. Instead, they showcase composing multiple widgets to solve realistic problems (dashboards, forms, data views). They serve as inspiration for developers building their own applications and do not strictly follow the single-focus pattern.
|
|
39
|
+
|
|
40
|
+
**Examples:**
|
|
41
|
+
- `analytics`: Multi-widget analytics dashboard with Tabs and BarChart, demonstrating tab navigation and multi-chart layouts.
|
|
42
|
+
- `login_form`: Form UI with text input, cursor positioning, and popup feedback using Paragraph, Overlay, Center, and Cursor.
|
|
43
|
+
- `table_select`: Interactive table viewer with row/column selection, simulating a process monitor application.
|
|
44
|
+
- `hit_test`: Demonstrates layout caching pattern for hit testing with split panels and mouse interaction.
|
|
45
|
+
- `map_demo`: Canvas-based world map visualization with animated shapes and interactive marker cycling.
|
|
46
|
+
- `mouse_events`: Multi-panel event display app showcasing all mouse event types.
|
|
47
|
+
- `all_events`: Multi-panel dashboard displaying all event types (key, mouse, resize, paste, focus).
|
|
48
|
+
- `flex_layout`: Layout demo showcasing Layout flex modes (space_between, space_evenly, fill, ratio).
|
|
49
|
+
- `custom_widget`: Demonstrates custom widget implementation with a diagonal line widget.
|
|
50
|
+
|
|
51
|
+
## Category 3: Documentation-Verification Examples
|
|
52
|
+
|
|
53
|
+
Examples that are verbatim copies (or near copies) of code snippets from documentation files, added to the `examples/` directory to ensure they remain executable and don't rot.
|
|
54
|
+
|
|
55
|
+
These serve as automated documentation tests: if the example code changes but the documentation does not, tests will fail and reveal the inconsistency.
|
|
56
|
+
|
|
57
|
+
**Examples:**
|
|
58
|
+
- `quickstart_lifecycle`: Copy of the lifecycle example from `README.md` or quickstart documentation.
|
|
59
|
+
- `quickstart_dsl`: Copy of the DSL-style example from quickstart documentation.
|
|
60
|
+
- `readme_usage`: Copy of the simple "Hello, Ratatui!" example from `README.md`.
|
|
61
|
+
|
|
62
|
+
## TODO
|
|
63
|
+
|
|
64
|
+
- [ ] **Establish a naming prefix convention** to disambiguate categories alphabetically without requiring subdirectories. Suggested prefixes:
|
|
65
|
+
- `app_` (application): `app_analytics`, `app_login_form`, etc.
|
|
66
|
+
- `verify_` (doc/documentation): `verify_quickstart_lifecycle`, `verify_readme_usage`, etc.
|
|
67
|
+
- `widget_` (widget showcase): `widget_gauge_demo`, `widget_list_demo`, etc.
|
|
68
|
+
- Apply this retroactively to all examples via directory renames (includes renaming screenshots in `doc/images/`, updating markdown image references in documentation, updating links in markdown files, and ensuring the `ExampleApp.all` list reflects the new names).
|
|
69
|
+
|
|
70
|
+
- [ ] **Split `analytics`** (demonstrates both Tabs and BarChart interactively). Create `widget_tabs_demo` and move BarChart demo to it, or extract BarChart into its own single-widget showcase.
|
|
71
|
+
|
|
72
|
+
- [ ] **Split `all_events`** (demonstrates multiple event types in a 2×2 grid). Consider extracting each event type into its own single-widget panel, or clarify whether this remains a "showcase of everything" vs. a focused event-demo.
|
|
73
|
+
|
|
74
|
+
- [ ] **Split `flex_layout`** (demonstrates Layout flex modes with multiple examples). This is borderline—it's quasi-documentation-verification of Layout behavior. Consider whether it belongs as `verify_flex_layout` or if it should remain as a showcase.
|
|
75
|
+
|
|
76
|
+
- [ ] **Reassign `mouse_events`**: Currently straddles Category 2 (real app) and Category 3 (doc verification). Clarify its purpose: is it an app showcase or documenting mouse event structure? If doc-verification, move to Category 3 and rename to `verify_mouse_events`.
|
|
77
|
+
|
|
78
|
+
- [ ] **Reassign `hit_test`**: Currently categorized as Category 2 but serves partly to document the "Cached Layout Pattern". Consider renaming to `verify_hit_test` if it should be documentation-verification, or ensure it's purely a showcase of an application pattern.
|
|
79
|
+
|
|
80
|
+
- [ ] **Verify `custom_widget`**: Currently Category 2 (real app), but is it actually a documented pattern or example code? If it's meant to verify custom widget documentation, rename to `verify_custom_widget`.
|
|
81
|
+
|
|
82
|
+
- [ ] **Update documentation** (developing_examples.md) to reflect the new naming convention and clarify which category each example should belong to.
|
data/doc/custom.css
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
img {
|
|
2
7
|
max-width: 100%;
|
|
3
8
|
height: auto;
|
|
@@ -5,4 +10,13 @@ img {
|
|
|
5
10
|
|
|
6
11
|
.theme-toggle {
|
|
7
12
|
margin-left: 0 !important;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Terminal Previews (Native PNGs)
|
|
16
|
+
* The images already contain the window chrome and shadows.
|
|
17
|
+
* We just need to center them and ensure they scale down on mobile.
|
|
18
|
+
*/
|
|
19
|
+
img[src*="images/"] {
|
|
20
|
+
display: block;
|
|
21
|
+
margin: 2em auto;
|
|
8
22
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Event Handling in RatatuiRuby
|
|
2
|
+
|
|
3
|
+
`ratatui_ruby` provides a rich, object-oriented event system that supports multiple coding styles, from simple boolean predicates to modern Ruby pattern matching.
|
|
4
|
+
|
|
5
|
+
Events are retrieved using `RatatuiRuby.poll_event`. This method returns an instance of a subclass of `RatatuiRuby::Event` (e.g., `RatatuiRuby::Event::Key`, `RatatuiRuby::Event::Mouse`) or `nil` if no event is available.
|
|
6
|
+
|
|
7
|
+
## 1. Symbol and String Comparison (Simplest)
|
|
8
|
+
|
|
9
|
+
For simple key events, `RatatuiRuby::Event::Key` objects can be compared directly to Symbols or Strings. This is often the quickest way to get started.
|
|
10
|
+
|
|
11
|
+
* **String**: Matches the key character (e.g., "a", "q").
|
|
12
|
+
* **Symbol**: Matches special keys (e.g., `:enter`, `:esc`) or modifier combinations (e.g., `:ctrl_c`).
|
|
13
|
+
|
|
14
|
+
> [!NOTE]
|
|
15
|
+
> On macOS, the **Option** key is mapped to `alt`. The **Command** key is typically intercepted by the terminal emulator and may not be sent to the application, or it may be mapped to Meta/Alt depending on your terminal settings.
|
|
16
|
+
|
|
17
|
+
For a complete list of supported keys, modifiers, and event types, please refer to the [API Documentation for RatatuiRuby::Event](file:///Users/kerrick/Developer/ratatui_ruby/lib/ratatui_ruby/event.rb).
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
event = RatatuiRuby.poll_event
|
|
21
|
+
next unless event
|
|
22
|
+
|
|
23
|
+
# 1. Check for quit keys
|
|
24
|
+
if event == "q" || event == :ctrl_c
|
|
25
|
+
break
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# 2. Check for special key
|
|
29
|
+
if event == :enter
|
|
30
|
+
submit_form
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 2. Predicate Methods (Intermediate)
|
|
35
|
+
|
|
36
|
+
If you need more control or logic (e.g. `if/elsif`), or need to handle non-key events like Resize or Mouse, use the predicate methods.
|
|
37
|
+
|
|
38
|
+
### Polymorphic Predicates
|
|
39
|
+
|
|
40
|
+
Safe to call on *any* event object. They return `true` only for the matching event type.
|
|
41
|
+
|
|
42
|
+
Available: `key?`, `mouse?`, `resize?`, `paste?`, `focus_gained?`, `focus_lost?`.
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
event = RatatuiRuby.poll_event
|
|
46
|
+
|
|
47
|
+
if event.key?
|
|
48
|
+
handle_keypress(event)
|
|
49
|
+
elsif event.mouse?
|
|
50
|
+
handle_click(event)
|
|
51
|
+
elsif event.resize?
|
|
52
|
+
resize_layout(event.width, event.height)
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Helper Predicates
|
|
57
|
+
|
|
58
|
+
Specific to certain event classes to simplify checks.
|
|
59
|
+
|
|
60
|
+
#### `RatatuiRuby::Event::Key`
|
|
61
|
+
* `ctrl?`, `alt?`, `shift?`: Check if modifier is held.
|
|
62
|
+
* `text?`: Returns `true` if the event is a printable character (length == 1).
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
if event.key? && event.ctrl? && event.code == "s"
|
|
66
|
+
save_file
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### `RatatuiRuby::Event::Mouse`
|
|
71
|
+
* `down?`, `up?`, `drag?`: Check mouse action.
|
|
72
|
+
* `scroll_up?`, `scroll_down?`: Check scroll direction.
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
if event.mouse? && event.scroll_up?
|
|
76
|
+
scroll_view(-1)
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 3. Pattern Matching (Powerful)
|
|
81
|
+
|
|
82
|
+
For complex applications, Ruby 3.0+ Pattern Matching with the `type:` discriminator is the most idiomatic and concise approach.
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
loop do
|
|
86
|
+
case RatatuiRuby.poll_event
|
|
87
|
+
|
|
88
|
+
# Match specific key code
|
|
89
|
+
in type: :key, code: "q"
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
# Match complex combo
|
|
93
|
+
in type: :key, code: "c", modifiers: ["ctrl"]
|
|
94
|
+
break
|
|
95
|
+
|
|
96
|
+
# Capture variables
|
|
97
|
+
in type: :key, code: "up" | "down" => direction
|
|
98
|
+
move_cursor(direction)
|
|
99
|
+
|
|
100
|
+
# Match mouse events
|
|
101
|
+
in type: :mouse, kind: "down", x:, y:
|
|
102
|
+
handle_click(x, y)
|
|
103
|
+
|
|
104
|
+
else
|
|
105
|
+
# Ignore
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Summary of Event Classes
|
|
111
|
+
|
|
112
|
+
| Event Class | Discriminator (`type:`) | Attributes | Predicate |
|
|
113
|
+
| :--- | :--- | :--- | :--- |
|
|
114
|
+
| `RatatuiRuby::Event::Key` | `:key` | `code`, `modifiers` | `key?` |
|
|
115
|
+
| `RatatuiRuby::Event::Mouse` | `:mouse` | `kind`, `x`, `y`, `button`, `modifiers` | `mouse?` |
|
|
116
|
+
| `RatatuiRuby::Event::Resize` | `:resize` | `width`, `height` | `resize?` |
|
|
117
|
+
| `RatatuiRuby::Event::Paste` | `:paste` | `content` | `paste?` |
|
|
118
|
+
| `RatatuiRuby::Event::FocusGained` | `:focus_gained` | (none) | `focus_gained?` |
|
|
119
|
+
| `RatatuiRuby::Event::FocusLost` | `:focus_lost` | (none) | `focus_lost?` |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/index.md
CHANGED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Interactive TUI Design Patterns
|
|
7
|
+
|
|
8
|
+
Canonical patterns for building responsive, interactive terminal user interfaces with ratatui_ruby.
|
|
9
|
+
|
|
10
|
+
## The Cached Layout Pattern
|
|
11
|
+
|
|
12
|
+
**Context:** In immediate-mode TUI development, you render once per event loop. The render happens, the user clicks, you respond. This cycle repeats 60 times a second.
|
|
13
|
+
|
|
14
|
+
**Problem:** Your layout has constraints. When you render, you calculate where each widget goes. When the user clicks, you need to know which widget was under the cursor. Two separate calculations means two separate constraint definitions. Change the layout once and forget to update the hit test logic—bugs happen.
|
|
15
|
+
|
|
16
|
+
**Solution:** Calculate layout once. Cache the results. Reuse them everywhere.
|
|
17
|
+
|
|
18
|
+
### The Three-Phase Lifecycle
|
|
19
|
+
|
|
20
|
+
Structure your event loop into three clear phases:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
def run
|
|
24
|
+
RatatuiRuby.run do
|
|
25
|
+
loop do
|
|
26
|
+
calculate_layout # Phase 1: Geometry (once per frame)
|
|
27
|
+
render # Phase 2: Draw
|
|
28
|
+
break if handle_input == :quit # Phase 3: Input
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Phase 1: Layout Calculation**
|
|
35
|
+
|
|
36
|
+
Call this before rendering and event handling. It's the single source of truth for geometry:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
def calculate_layout
|
|
40
|
+
full_area = RatatuiRuby::Rect.new(x: 0, y: 0, width: 80, height: 24)
|
|
41
|
+
|
|
42
|
+
# Main area vs sidebar (70% / 30%)
|
|
43
|
+
main_area, @sidebar_area = RatatuiRuby::Layout.split(
|
|
44
|
+
full_area,
|
|
45
|
+
direction: :horizontal,
|
|
46
|
+
constraints: [
|
|
47
|
+
RatatuiRuby::Constraint.percentage(70),
|
|
48
|
+
RatatuiRuby::Constraint.percentage(30),
|
|
49
|
+
]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Within main area, left vs right panels
|
|
53
|
+
@left_rect, @right_rect = RatatuiRuby::Layout.split(
|
|
54
|
+
main_area,
|
|
55
|
+
direction: :horizontal,
|
|
56
|
+
constraints: [
|
|
57
|
+
RatatuiRuby::Constraint.percentage(@left_ratio),
|
|
58
|
+
RatatuiRuby::Constraint.percentage(100 - @left_ratio)
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Phase 2: Rendering**
|
|
65
|
+
|
|
66
|
+
Reuse the cached rects. Build and draw:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
def render
|
|
70
|
+
left_panel = build_widget(@left_rect)
|
|
71
|
+
right_panel = build_widget(@right_rect)
|
|
72
|
+
# ... draw ...
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Phase 3: Event Handling**
|
|
77
|
+
|
|
78
|
+
Reuse the cached rects. Test clicks:
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
def handle_input
|
|
82
|
+
event = RatatuiRuby.poll_event
|
|
83
|
+
return unless event
|
|
84
|
+
|
|
85
|
+
case event
|
|
86
|
+
in type: :mouse, kind: "down", x:, y:
|
|
87
|
+
if @left_rect.contains?(x, y)
|
|
88
|
+
handle_left_click
|
|
89
|
+
elsif @right_rect.contains?(x, y)
|
|
90
|
+
handle_right_click
|
|
91
|
+
end
|
|
92
|
+
else
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Why This Matters
|
|
99
|
+
|
|
100
|
+
- **Single source of truth:** Change constraints once. They apply everywhere.
|
|
101
|
+
- **No duplication:** Write `Layout.split(area, constraints:)` once. Use the result in render and input.
|
|
102
|
+
- **Testable:** Layout geometry is explicit. You can verify it.
|
|
103
|
+
- **Foundation for components:** In Gem 1.5, the `Component` class automates this caching. This pattern teaches the mental model.
|
|
104
|
+
|
|
105
|
+
## Layout.split
|
|
106
|
+
|
|
107
|
+
`Layout.split` computes layout geometry without rendering. It returns an array of `Rect` objects.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
rects = Layout.split(
|
|
111
|
+
area,
|
|
112
|
+
direction: :horizontal,
|
|
113
|
+
constraints: [Constraint.percentage(70), Constraint.percentage(30)]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
left, right = rects
|
|
117
|
+
# left is a Rect describing the left 70% of the area
|
|
118
|
+
# right is a Rect describing the right 30% of the area
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Use it to establish the single source of truth in `calculate_layout`. Reuse the results in both render and event handling.
|