ratatui_ruby 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/AGENTS.md +87 -171
- data/CHANGELOG.md +38 -1
- data/README.md +8 -3
- data/REUSE.toml +20 -0
- data/doc/application_architecture.md +105 -45
- data/doc/application_testing.md +5 -3
- data/doc/contributors/design/ruby_frontend.md +9 -5
- data/doc/contributors/developing_examples.md +76 -18
- data/doc/contributors/documentation_style.md +7 -0
- data/doc/contributors/index.md +2 -0
- data/doc/event_handling.md +10 -4
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart_demo.png +0 -0
- data/doc/images/widget_block_padding.png +0 -0
- data/doc/images/widget_block_titles.png +0 -0
- data/doc/images/widget_box_demo.png +0 -0
- data/doc/images/widget_calendar_demo.png +0 -0
- data/doc/images/widget_cell_demo.png +0 -0
- data/doc/images/widget_chart_demo.png +0 -0
- data/doc/images/widget_gauge_demo.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge_demo.png +0 -0
- data/doc/images/widget_list_demo.png +0 -0
- data/doc/images/widget_ratatui_logo_demo.png +0 -0
- data/doc/images/widget_ratatui_mascot_demo.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_scrollbar_demo.png +0 -0
- data/doc/images/widget_sparkline_demo.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table_flex.png +0 -0
- data/doc/images/widget_tabs_demo.png +0 -0
- data/doc/interactive_design.md +25 -30
- data/doc/quickstart.md +147 -120
- data/examples/app_all_events/README.md +81 -0
- data/examples/app_all_events/app.rb +93 -0
- data/examples/app_all_events/model/event_color_cycle.rb +41 -0
- data/examples/app_all_events/model/event_entry.rb +75 -0
- data/examples/app_all_events/model/events.rb +180 -0
- data/examples/app_all_events/model/highlight.rb +57 -0
- data/examples/app_all_events/model/timestamp.rb +54 -0
- data/examples/app_all_events/test/snapshots/after_focus_lost.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_focus_regained.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_horizontal_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_key_a.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_key_ctrl_x.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_mouse_click.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_mouse_drag.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_multiple_events.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_paste.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_right_click.txt +24 -0
- data/examples/app_all_events/test/snapshots/after_vertical_resize.txt +24 -0
- data/examples/app_all_events/test/snapshots/initial_state.txt +24 -0
- data/examples/app_all_events/view/app_view.rb +78 -0
- data/examples/app_all_events/view/controls_view.rb +50 -0
- data/examples/app_all_events/view/counts_view.rb +55 -0
- data/examples/app_all_events/view/live_view.rb +69 -0
- data/examples/app_all_events/view/log_view.rb +60 -0
- data/examples/app_all_events/view.rb +7 -0
- data/examples/app_all_events/view_state.rb +42 -0
- data/examples/app_color_picker/README.md +94 -0
- data/examples/app_color_picker/app.rb +112 -0
- data/examples/app_color_picker/clipboard.rb +84 -0
- data/examples/app_color_picker/color.rb +191 -0
- data/examples/app_color_picker/copy_dialog.rb +170 -0
- data/examples/app_color_picker/harmony.rb +56 -0
- data/examples/app_color_picker/input.rb +142 -0
- data/examples/app_color_picker/palette.rb +80 -0
- data/examples/app_color_picker/scene.rb +201 -0
- data/examples/{login_form → app_login_form}/app.rb +39 -42
- data/examples/{map_demo → app_map_demo}/app.rb +24 -21
- data/examples/{table_select → app_table_select}/app.rb +68 -65
- data/examples/{quickstart_dsl → verify_quickstart_dsl}/app.rb +15 -6
- data/examples/verify_quickstart_layout/app.rb +69 -0
- data/examples/{quickstart_lifecycle → verify_quickstart_lifecycle}/app.rb +19 -10
- data/examples/verify_readme_usage/app.rb +34 -0
- data/examples/widget_barchart_demo/app.rb +238 -0
- data/examples/{block_padding → widget_block_padding}/app.rb +17 -13
- data/examples/{block_titles → widget_block_titles}/app.rb +25 -17
- data/examples/{box_demo → widget_box_demo}/app.rb +99 -65
- data/examples/widget_calendar_demo/app.rb +109 -0
- data/examples/widget_cell_demo/app.rb +104 -0
- data/examples/widget_chart_demo/app.rb +213 -0
- data/examples/widget_gauge_demo/app.rb +212 -0
- data/examples/widget_layout_split/app.rb +246 -0
- data/examples/widget_line_gauge_demo/app.rb +217 -0
- data/examples/widget_list_demo/app.rb +382 -0
- data/examples/widget_list_styles/app.rb +141 -0
- data/examples/widget_popup_demo/app.rb +104 -0
- data/examples/widget_ratatui_logo_demo/app.rb +103 -0
- data/examples/widget_ratatui_mascot_demo/app.rb +93 -0
- data/examples/widget_rect/app.rb +205 -0
- data/examples/widget_render/app.rb +184 -0
- data/examples/widget_rich_text/app.rb +137 -0
- data/examples/widget_scroll_text/app.rb +108 -0
- data/examples/widget_scrollbar_demo/app.rb +153 -0
- data/examples/widget_sparkline_demo/app.rb +274 -0
- data/examples/widget_style_colors/app.rb +19 -21
- data/examples/widget_table_flex/app.rb +95 -0
- data/examples/widget_tabs_demo/app.rb +167 -0
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/src/events.rs +121 -36
- data/ext/ratatui_ruby/src/frame.rs +115 -0
- data/ext/ratatui_ruby/src/lib.rs +79 -26
- data/ext/ratatui_ruby/src/rendering.rs +8 -4
- data/ext/ratatui_ruby/src/style.rs +138 -57
- data/ext/ratatui_ruby/src/terminal.rs +5 -9
- data/ext/ratatui_ruby/src/text.rs +13 -6
- data/ext/ratatui_ruby/src/widgets/barchart.rs +56 -54
- data/ext/ratatui_ruby/src/widgets/block.rs +7 -6
- data/ext/ratatui_ruby/src/widgets/canvas.rs +21 -3
- data/ext/ratatui_ruby/src/widgets/chart.rs +20 -10
- data/ext/ratatui_ruby/src/widgets/layout.rs +9 -4
- data/ext/ratatui_ruby/src/widgets/list.rs +32 -9
- data/ext/ratatui_ruby/src/widgets/overlay.rs +2 -1
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +1 -1
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +19 -8
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +17 -10
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +4 -2
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +14 -11
- data/ext/ratatui_ruby/src/widgets/table.rs +8 -4
- data/ext/ratatui_ruby/src/widgets/tabs.rs +11 -11
- data/lib/ratatui_ruby/cell.rb +3 -3
- data/lib/ratatui_ruby/event/key.rb +1 -1
- data/lib/ratatui_ruby/event/none.rb +43 -0
- data/lib/ratatui_ruby/event.rb +56 -4
- data/lib/ratatui_ruby/frame.rb +87 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +11 -11
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +1 -5
- data/lib/ratatui_ruby/schema/bar_chart.rb +217 -217
- data/lib/ratatui_ruby/schema/block.rb +163 -168
- data/lib/ratatui_ruby/schema/calendar.rb +66 -67
- data/lib/ratatui_ruby/schema/canvas.rb +63 -63
- data/lib/ratatui_ruby/schema/center.rb +46 -46
- data/lib/ratatui_ruby/schema/chart.rb +135 -143
- data/lib/ratatui_ruby/schema/clear.rb +42 -42
- data/lib/ratatui_ruby/schema/constraint.rb +76 -76
- data/lib/ratatui_ruby/schema/cursor.rb +25 -25
- data/lib/ratatui_ruby/schema/gauge.rb +53 -53
- data/lib/ratatui_ruby/schema/layout.rb +87 -87
- data/lib/ratatui_ruby/schema/line_gauge.rb +62 -62
- data/lib/ratatui_ruby/schema/list.rb +86 -84
- data/lib/ratatui_ruby/schema/overlay.rb +31 -31
- data/lib/ratatui_ruby/schema/paragraph.rb +80 -80
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +10 -6
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +10 -5
- data/lib/ratatui_ruby/schema/rect.rb +60 -60
- data/lib/ratatui_ruby/schema/scrollbar.rb +119 -119
- data/lib/ratatui_ruby/schema/shape/label.rb +1 -1
- data/lib/ratatui_ruby/schema/sparkline.rb +111 -110
- data/lib/ratatui_ruby/schema/style.rb +46 -46
- data/lib/ratatui_ruby/schema/table.rb +112 -119
- data/lib/ratatui_ruby/schema/tabs.rb +66 -67
- data/lib/ratatui_ruby/session/autodoc.rb +417 -0
- data/lib/ratatui_ruby/session.rb +40 -23
- data/lib/ratatui_ruby/test_helper.rb +185 -19
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +65 -39
- data/{examples/sparkline_demo → sig/examples/app_all_events}/app.rbs +3 -2
- data/sig/examples/app_all_events/model/event_entry.rbs +16 -0
- data/sig/examples/app_all_events/model/events.rbs +15 -0
- data/sig/examples/app_all_events/model/timestamp.rbs +11 -0
- data/sig/examples/app_all_events/view/app_view.rbs +8 -0
- data/sig/examples/app_all_events/view/controls_view.rbs +6 -0
- data/sig/examples/app_all_events/view/counts_view.rbs +6 -0
- data/sig/examples/app_all_events/view/live_view.rbs +6 -0
- data/sig/examples/app_all_events/view/log_view.rbs +6 -0
- data/sig/examples/app_all_events/view.rbs +8 -0
- data/sig/examples/app_all_events/view_state.rbs +15 -0
- data/{examples/list_demo → sig/examples/app_color_picker}/app.rbs +2 -2
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_map_demo/app.rbs +11 -0
- data/sig/examples/app_table_select/app.rbs +11 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +11 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +11 -0
- data/sig/examples/verify_readme_usage/app.rbs +11 -0
- data/sig/examples/widget_block_padding/app.rbs +11 -0
- data/sig/examples/widget_block_titles/app.rbs +11 -0
- data/sig/examples/widget_box_demo/app.rbs +11 -0
- data/sig/examples/widget_calendar_demo/app.rbs +11 -0
- data/sig/examples/widget_cell_demo/app.rbs +11 -0
- data/sig/examples/widget_chart_demo/app.rbs +11 -0
- data/{examples/gauge_demo → sig/examples/widget_gauge_demo}/app.rbs +4 -0
- data/sig/examples/widget_layout_split/app.rbs +10 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +11 -0
- data/sig/examples/widget_list_demo/app.rbs +12 -0
- data/sig/examples/widget_list_styles/app.rbs +11 -0
- data/sig/examples/widget_popup_demo/app.rbs +11 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +11 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +11 -0
- data/sig/examples/widget_rect/app.rbs +12 -0
- data/sig/examples/widget_render/app.rbs +10 -0
- data/sig/examples/widget_rich_text/app.rbs +11 -0
- data/sig/examples/widget_scroll_text/app.rbs +11 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +11 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +10 -0
- data/{examples → sig/examples}/widget_style_colors/app.rbs +1 -1
- data/sig/examples/widget_table_flex/app.rbs +11 -0
- data/sig/ratatui_ruby/frame.rbs +9 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +3 -2
- data/sig/ratatui_ruby/schema/draw.rbs +4 -0
- data/sig/ratatui_ruby/schema/layout.rbs +1 -1
- data/sig/ratatui_ruby/session.rbs +94 -0
- data/tasks/autodoc/inventory.rb +61 -0
- data/tasks/autodoc/member.rb +56 -0
- data/tasks/autodoc/name.rb +19 -0
- data/tasks/autodoc/notice.rb +26 -0
- data/tasks/autodoc/rbs.rb +38 -0
- data/tasks/autodoc/rdoc.rb +45 -0
- data/tasks/autodoc.rake +47 -0
- data/tasks/bump/history.rb +2 -2
- data/tasks/doc.rake +600 -6
- data/tasks/example_viewer.html.erb +172 -0
- data/tasks/lint.rake +8 -4
- data/tasks/resources/index.html.erb +6 -0
- data/tasks/sourcehut.rake +4 -4
- data/tasks/terminal_preview/app_screenshot.rb +1 -3
- data/tasks/terminal_preview/crash_report.rb +7 -9
- data/tasks/terminal_preview/launcher_script.rb +4 -6
- data/tasks/terminal_preview/preview_collection.rb +4 -6
- data/tasks/terminal_preview/safety_confirmation.rb +3 -5
- data/tasks/terminal_preview/saved_screenshot.rb +7 -9
- data/tasks/terminal_preview/terminal_window.rb +7 -9
- data/tasks/test.rake +1 -1
- data/tasks/website/index_page.rb +3 -3
- data/tasks/website/version.rb +10 -10
- data/tasks/website/version_menu.rb +10 -12
- data/tasks/website/versioned_documentation.rb +49 -17
- data/tasks/website/website.rb +6 -8
- data/tasks/website.rake +4 -4
- metadata +156 -125
- data/LICENSES/BSD-2-Clause.txt +0 -9
- data/doc/contributors/better_dx.md +0 -543
- data/doc/contributors/example_analysis.md +0 -82
- data/doc/images/all_events.png +0 -0
- data/doc/images/block_padding.png +0 -0
- data/doc/images/block_titles.png +0 -0
- data/doc/images/box_demo.png +0 -0
- data/doc/images/calendar_demo.png +0 -0
- data/doc/images/cell_demo.png +0 -0
- data/doc/images/chart_demo.png +0 -0
- data/doc/images/flex_layout.png +0 -0
- data/doc/images/gauge_demo.png +0 -0
- data/doc/images/line_gauge_demo.png +0 -0
- data/doc/images/list_demo.png +0 -0
- data/doc/images/readme_usage.png +0 -0
- data/doc/images/scrollbar_demo.png +0 -0
- data/doc/images/sparkline_demo.png +0 -0
- data/doc/images/table_flex.png +0 -0
- data/examples/all_events/app.rb +0 -169
- data/examples/all_events/app.rbs +0 -7
- data/examples/all_events/test_app.rb +0 -139
- data/examples/analytics/app.rb +0 -258
- data/examples/analytics/app.rbs +0 -7
- data/examples/analytics/test_app.rb +0 -132
- data/examples/block_padding/app.rbs +0 -7
- data/examples/block_padding/test_app.rb +0 -31
- data/examples/block_titles/app.rbs +0 -7
- data/examples/block_titles/test_app.rb +0 -34
- data/examples/box_demo/app.rbs +0 -7
- data/examples/box_demo/test_app.rb +0 -88
- data/examples/calendar_demo/app.rb +0 -101
- data/examples/calendar_demo/app.rbs +0 -7
- data/examples/calendar_demo/test_app.rb +0 -108
- data/examples/cell_demo/app.rb +0 -108
- data/examples/cell_demo/app.rbs +0 -7
- data/examples/cell_demo/test_app.rb +0 -36
- data/examples/chart_demo/app.rb +0 -203
- data/examples/chart_demo/app.rbs +0 -7
- data/examples/chart_demo/test_app.rb +0 -102
- data/examples/custom_widget/app.rb +0 -51
- data/examples/custom_widget/app.rbs +0 -7
- data/examples/custom_widget/test_app.rb +0 -30
- data/examples/flex_layout/app.rb +0 -156
- data/examples/flex_layout/app.rbs +0 -7
- data/examples/flex_layout/test_app.rb +0 -65
- data/examples/gauge_demo/app.rb +0 -182
- data/examples/gauge_demo/test_app.rb +0 -120
- data/examples/hit_test/app.rb +0 -175
- data/examples/hit_test/app.rbs +0 -7
- data/examples/hit_test/test_app.rb +0 -102
- data/examples/line_gauge_demo/app.rb +0 -190
- data/examples/line_gauge_demo/app.rbs +0 -7
- data/examples/line_gauge_demo/test_app.rb +0 -129
- data/examples/list_demo/app.rb +0 -253
- data/examples/list_demo/test_app.rb +0 -237
- data/examples/list_styles/app.rb +0 -140
- data/examples/list_styles/app.rbs +0 -7
- data/examples/list_styles/test_app.rb +0 -157
- data/examples/login_form/app.rbs +0 -7
- data/examples/login_form/test_app.rb +0 -51
- data/examples/map_demo/app.rbs +0 -7
- data/examples/map_demo/test_app.rb +0 -149
- data/examples/mouse_events/app.rb +0 -97
- data/examples/mouse_events/app.rbs +0 -7
- data/examples/mouse_events/test_app.rb +0 -53
- data/examples/popup_demo/app.rb +0 -103
- data/examples/popup_demo/app.rbs +0 -7
- data/examples/popup_demo/test_app.rb +0 -54
- data/examples/quickstart_dsl/app.rbs +0 -7
- data/examples/quickstart_dsl/test_app.rb +0 -29
- data/examples/quickstart_lifecycle/app.rbs +0 -7
- data/examples/quickstart_lifecycle/test_app.rb +0 -29
- data/examples/ratatui_logo_demo/app.rb +0 -79
- data/examples/ratatui_logo_demo/app.rbs +0 -7
- data/examples/ratatui_logo_demo/test_app.rb +0 -51
- data/examples/ratatui_mascot_demo/app.rb +0 -84
- data/examples/ratatui_mascot_demo/app.rbs +0 -7
- data/examples/ratatui_mascot_demo/test_app.rb +0 -47
- data/examples/readme_usage/app.rb +0 -29
- data/examples/readme_usage/app.rbs +0 -7
- data/examples/readme_usage/test_app.rb +0 -29
- data/examples/rich_text/app.rb +0 -141
- data/examples/rich_text/app.rbs +0 -7
- data/examples/rich_text/test_app.rb +0 -166
- data/examples/scroll_text/app.rb +0 -103
- data/examples/scroll_text/app.rbs +0 -7
- data/examples/scroll_text/test_app.rb +0 -110
- data/examples/scrollbar_demo/app.rb +0 -143
- data/examples/scrollbar_demo/app.rbs +0 -7
- data/examples/scrollbar_demo/test_app.rb +0 -77
- data/examples/sparkline_demo/app.rb +0 -240
- data/examples/sparkline_demo/test_app.rb +0 -107
- data/examples/table_flex/app.rb +0 -65
- data/examples/table_flex/app.rbs +0 -7
- data/examples/table_flex/test_app.rb +0 -36
- data/examples/table_select/app.rbs +0 -7
- data/examples/table_select/test_app.rb +0 -180
- data/examples/widget_style_colors/test_app.rb +0 -48
- /data/doc/images/{analytics.png → app_analytics.png} +0 -0
- /data/doc/images/{custom_widget.png → app_custom_widget.png} +0 -0
- /data/doc/images/{login_form.png → app_login_form.png} +0 -0
- /data/doc/images/{map_demo.png → app_map_demo.png} +0 -0
- /data/doc/images/{mouse_events.png → app_mouse_events.png} +0 -0
- /data/doc/images/{table_select.png → app_table_select.png} +0 -0
- /data/doc/images/{quickstart_dsl.png → verify_quickstart_dsl.png} +0 -0
- /data/doc/images/{ratatui_logo_demo.png → verify_quickstart_layout.png} +0 -0
- /data/doc/images/{quickstart_lifecycle.png → verify_quickstart_lifecycle.png} +0 -0
- /data/doc/images/{list_styles.png → widget_list_styles.png} +0 -0
- /data/doc/images/{popup_demo.png → widget_popup_demo.png} +0 -0
- /data/doc/images/{hit_test.png → widget_rect.png} +0 -0
- /data/doc/images/{rich_text.png → widget_rich_text.png} +0 -0
- /data/doc/images/{scroll_text.png → widget_scroll_text.png} +0 -0
|
@@ -1,543 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
-
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
-
-->
|
|
5
|
-
|
|
6
|
-
# Improving DX for Layout & Hit Testing
|
|
7
|
-
|
|
8
|
-
## Problem Statement
|
|
9
|
-
|
|
10
|
-
Interactive TUI applications require hit testing: determining which UI region the user clicked. Current RatatuiRuby practice duplicates layout logic between rendering and input handling.
|
|
11
|
-
|
|
12
|
-
### Current Pattern (Duplication)
|
|
13
|
-
|
|
14
|
-
```ruby
|
|
15
|
-
def run
|
|
16
|
-
loop do
|
|
17
|
-
calculate_layout # Phase 1: Manually calculate rects
|
|
18
|
-
render # Phase 2: Build UI tree (repeating the same layout logic)
|
|
19
|
-
handle_input # Phase 3: Use cached rects from Phase 1
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def calculate_layout
|
|
24
|
-
full_area = RatatuiRuby::Rect.new(x: 0, y: 0, width: 80, height: 24)
|
|
25
|
-
|
|
26
|
-
@main_area, @control_area = RatatuiRuby::Layout.split(
|
|
27
|
-
full_area,
|
|
28
|
-
direction: :vertical,
|
|
29
|
-
constraints: [
|
|
30
|
-
RatatuiRuby::Constraint.fill(1),
|
|
31
|
-
RatatuiRuby::Constraint.length(7)
|
|
32
|
-
]
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
@left_rect, @right_rect = RatatuiRuby::Layout.split(
|
|
36
|
-
@main_area,
|
|
37
|
-
direction: :horizontal,
|
|
38
|
-
constraints: [
|
|
39
|
-
RatatuiRuby::Constraint.percentage(50),
|
|
40
|
-
RatatuiRuby::Constraint.percentage(50)
|
|
41
|
-
]
|
|
42
|
-
)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def render
|
|
46
|
-
# Rebuilds the SAME layout internally, but we can't access those rects
|
|
47
|
-
layout = RatatuiRuby::Layout.new(
|
|
48
|
-
direction: :vertical,
|
|
49
|
-
constraints: [
|
|
50
|
-
RatatuiRuby::Constraint.fill(1),
|
|
51
|
-
RatatuiRuby::Constraint.length(7)
|
|
52
|
-
],
|
|
53
|
-
children: [...]
|
|
54
|
-
)
|
|
55
|
-
RatatuiRuby.draw(layout)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def handle_input
|
|
59
|
-
event = RatatuiRuby.poll_event
|
|
60
|
-
if @left_rect&.contains?(event.x, event.y)
|
|
61
|
-
# hit test using cached rect
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Problems:**
|
|
67
|
-
1. **Duplication**: Layout constraints are written twice—once in `calculate_layout`, once in the UI tree.
|
|
68
|
-
2. **Fragility**: If layout changes in `render`, the cached rects in `@left_rect` become stale. The user must remember to update both places.
|
|
69
|
-
3. **Maintainability**: Adding new UI regions requires changes in two places and explicit rect caching.
|
|
70
|
-
4. **Performance**: Layout is calculated twice per frame (once manually, once internally during render).
|
|
71
|
-
|
|
72
|
-
### Ideal Pattern (No Duplication)
|
|
73
|
-
|
|
74
|
-
```ruby
|
|
75
|
-
def run
|
|
76
|
-
loop do
|
|
77
|
-
render # Single source of truth
|
|
78
|
-
break if handle_input == :quit
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def render
|
|
83
|
-
layout = RatatuiRuby::Layout.new(
|
|
84
|
-
direction: :vertical,
|
|
85
|
-
constraints: [
|
|
86
|
-
RatatuiRuby::Constraint.fill(1),
|
|
87
|
-
RatatuiRuby::Constraint.length(7)
|
|
88
|
-
],
|
|
89
|
-
children: [...]
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
@layout_info = RatatuiRuby.draw(layout) # Returns layout metadata
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def handle_input
|
|
96
|
-
event = RatatuiRuby.poll_event
|
|
97
|
-
# Query the layout info returned from draw()
|
|
98
|
-
if @layout_info[:left_panel]&.contains?(event.x, event.y)
|
|
99
|
-
# No manual caching needed; rects come from the same render pass
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Proposed Solution
|
|
105
|
-
|
|
106
|
-
Extend `RatatuiRuby.draw()` to return a `LayoutInfo` object containing the rectangles where widgets were rendered.
|
|
107
|
-
|
|
108
|
-
### API Changes
|
|
109
|
-
|
|
110
|
-
#### Option A: Return a Plain Hash (Simpler)
|
|
111
|
-
|
|
112
|
-
```ruby
|
|
113
|
-
# Layout.rb with optional layout_id parameter
|
|
114
|
-
class Layout < Data
|
|
115
|
-
def self.new(
|
|
116
|
-
direction: :vertical,
|
|
117
|
-
constraints: [],
|
|
118
|
-
children: [],
|
|
119
|
-
flex: :legacy,
|
|
120
|
-
layout_id: nil # NEW: Optional semantic identifier
|
|
121
|
-
)
|
|
122
|
-
# ...
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Block.rb with optional layout_id parameter
|
|
127
|
-
class Block < Data
|
|
128
|
-
def self.new(
|
|
129
|
-
title: nil,
|
|
130
|
-
borders: [],
|
|
131
|
-
border_type: :rounded,
|
|
132
|
-
style: nil,
|
|
133
|
-
layout_id: nil # NEW: Optional semantic identifier
|
|
134
|
-
)
|
|
135
|
-
# ...
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Usage in app:
|
|
140
|
-
layout = RatatuiRuby::Layout.new(
|
|
141
|
-
direction: :vertical,
|
|
142
|
-
constraints: [...],
|
|
143
|
-
layout_id: :main, # Tag this layout
|
|
144
|
-
children: [
|
|
145
|
-
RatatuiRuby::Block.new(
|
|
146
|
-
title: "Left",
|
|
147
|
-
layout_id: :left_panel, # Tag this block
|
|
148
|
-
...
|
|
149
|
-
),
|
|
150
|
-
RatatuiRuby::Block.new(
|
|
151
|
-
title: "Right",
|
|
152
|
-
layout_id: :right_panel,
|
|
153
|
-
...
|
|
154
|
-
)
|
|
155
|
-
]
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
layout_info = RatatuiRuby.draw(layout)
|
|
159
|
-
|
|
160
|
-
# layout_info is a Hash:
|
|
161
|
-
# {
|
|
162
|
-
# left_panel: #<Rect x=0 y=0 width=40 height=24>,
|
|
163
|
-
# right_panel: #<Rect x=40 y=0 width=40 height=24>,
|
|
164
|
-
# main: #<Rect x=0 y=0 width=80 height=24>
|
|
165
|
-
# }
|
|
166
|
-
|
|
167
|
-
if layout_info[:left_panel]&.contains?(event.x, event.y)
|
|
168
|
-
handle_left_click
|
|
169
|
-
end
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
#### Option B: Return a LayoutInfo Class (More Structured)
|
|
173
|
-
|
|
174
|
-
```ruby
|
|
175
|
-
class LayoutInfo
|
|
176
|
-
attr_reader :rects # Hash of layout_id => Rect
|
|
177
|
-
|
|
178
|
-
def [](key)
|
|
179
|
-
rects[key]
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def get(key)
|
|
183
|
-
rects[key]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def contains?(key, x, y)
|
|
187
|
-
rects[key]&.contains?(x, y)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Usage:
|
|
192
|
-
layout_info = RatatuiRuby.draw(layout)
|
|
193
|
-
if layout_info.contains?(:left_panel, event.x, event.y)
|
|
194
|
-
handle_left_click
|
|
195
|
-
end
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
**Recommendation:** Start with Option A (Plain Hash). It's simpler, aligns with RatatuiRuby's minimal design, and can evolve to Option B if needed.
|
|
199
|
-
|
|
200
|
-
### Implementation Sketch
|
|
201
|
-
|
|
202
|
-
#### Ruby Side
|
|
203
|
-
|
|
204
|
-
1. **Add `layout_id` parameter** to `Layout` and `Block` (and optionally other container widgets like `Center`, `Overlay`).
|
|
205
|
-
2. **Update `.rbs` type signatures** to document the new optional parameter.
|
|
206
|
-
3. **Update `RatatuiRuby.draw()` signature** to return `Hash[Symbol | String, Rect] | nil` (or return both render status and layout info as needed).
|
|
207
|
-
|
|
208
|
-
```ruby
|
|
209
|
-
# sig/ratatui_ruby/ratatui_ruby.rbs
|
|
210
|
-
def self.draw: (widget, ?return_layout: bool) -> (nil | Hash[Symbol | String, Rect])
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
#### Rust Side
|
|
214
|
-
|
|
215
|
-
1. **Track layout IDs during render:** When the Rust renderer encounters a widget with a `layout_id`, record its rendered rectangle.
|
|
216
|
-
2. **Return layout info as a Ruby Hash:** Construct a Ruby Hash mapping `layout_id` (String or Symbol) to `Rect` objects.
|
|
217
|
-
3. **Wire into `lib.rs`:** Modify the `draw` function to return this Hash instead of `nil`.
|
|
218
|
-
|
|
219
|
-
**Pseudo-code for `rendering.rs`:**
|
|
220
|
-
|
|
221
|
-
```rust
|
|
222
|
-
pub fn render_node(
|
|
223
|
-
frame: &mut Frame,
|
|
224
|
-
area: Rect,
|
|
225
|
-
node: Value,
|
|
226
|
-
layout_map: &mut HashMap<Value, Rect>, // Collect rects as we go
|
|
227
|
-
) -> Result<(), Error> {
|
|
228
|
-
// Extract layout_id if present
|
|
229
|
-
let layout_id: Option<Value> = node.funcall("layout_id", ()).ok();
|
|
230
|
-
|
|
231
|
-
if let Some(id) = layout_id {
|
|
232
|
-
layout_map.insert(id.clone(), area);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ... render the widget ...
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// In lib.rs, wrap the result:
|
|
239
|
-
pub fn draw(node: Value) -> Result<Value, Error> {
|
|
240
|
-
let mut layout_map = HashMap::new();
|
|
241
|
-
render_node(&mut frame, full_area, node, &mut layout_map)?;
|
|
242
|
-
|
|
243
|
-
// Convert HashMap to Ruby Hash
|
|
244
|
-
let result_hash = RHash::new();
|
|
245
|
-
for (key, rect) in layout_map {
|
|
246
|
-
result_hash.aset(key, rect)?;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
Ok(result_hash.into())
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
### Backward Compatibility
|
|
254
|
-
|
|
255
|
-
**No breaking changes:**
|
|
256
|
-
- `layout_id` is optional on all widgets.
|
|
257
|
-
- `RatatuiRuby.draw()` continues to render correctly.
|
|
258
|
-
- **Behavior**: If `layout_id` is omitted, that region is simply not included in the returned Hash.
|
|
259
|
-
- **Return value**: If no widgets have `layout_id`, returns an empty Hash (or `nil` if we want to preserve existing return type).
|
|
260
|
-
|
|
261
|
-
**Recommendation**: Return `nil` if `layout_id` is not used anywhere in the tree (preserves current behavior of returning nothing). Return a Hash if any widget has a `layout_id`.
|
|
262
|
-
|
|
263
|
-
## Example: Before and After
|
|
264
|
-
|
|
265
|
-
### Before (Current)
|
|
266
|
-
|
|
267
|
-
```ruby
|
|
268
|
-
class ColorPickerApp
|
|
269
|
-
def initialize
|
|
270
|
-
@input = "#F96302"
|
|
271
|
-
@current_color = parse_color(@input)
|
|
272
|
-
@error_message = ""
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def run
|
|
276
|
-
RatatuiRuby.run do
|
|
277
|
-
loop do
|
|
278
|
-
calculate_layout # Manual layout calculation
|
|
279
|
-
render
|
|
280
|
-
result = handle_input
|
|
281
|
-
break if result == :quit
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def calculate_layout
|
|
287
|
-
terminal_size = RatatuiRuby.terminal_size
|
|
288
|
-
width, height = terminal_size
|
|
289
|
-
|
|
290
|
-
full_area = RatatuiRuby::Rect.new(x: 0, y: 0, width: width, height: height)
|
|
291
|
-
|
|
292
|
-
input_area, rest = RatatuiRuby::Layout.split(full_area,
|
|
293
|
-
direction: :vertical,
|
|
294
|
-
constraints: [
|
|
295
|
-
RatatuiRuby::Constraint.length(3),
|
|
296
|
-
RatatuiRuby::Constraint.fill(1)
|
|
297
|
-
]
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
color_area, control_area = RatatuiRuby::Layout.split(rest,
|
|
301
|
-
direction: :vertical,
|
|
302
|
-
constraints: [
|
|
303
|
-
RatatuiRuby::Constraint.length(14),
|
|
304
|
-
RatatuiRuby::Constraint.fill(1)
|
|
305
|
-
]
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
harmony_area, @export_area_rect = RatatuiRuby::Layout.split(color_area,
|
|
309
|
-
direction: :vertical,
|
|
310
|
-
constraints: [
|
|
311
|
-
RatatuiRuby::Constraint.length(7),
|
|
312
|
-
RatatuiRuby::Constraint.fill(1)
|
|
313
|
-
]
|
|
314
|
-
)
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def render
|
|
318
|
-
main_ui = RatatuiRuby::Layout.new(
|
|
319
|
-
direction: :vertical,
|
|
320
|
-
constraints: [
|
|
321
|
-
RatatuiRuby::Constraint.length(3),
|
|
322
|
-
RatatuiRuby::Constraint.length(14),
|
|
323
|
-
RatatuiRuby::Constraint.fill(1)
|
|
324
|
-
],
|
|
325
|
-
children: [
|
|
326
|
-
build_input_section,
|
|
327
|
-
build_color_section,
|
|
328
|
-
build_controls_section
|
|
329
|
-
]
|
|
330
|
-
)
|
|
331
|
-
RatatuiRuby.draw(main_ui)
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
def handle_input
|
|
335
|
-
event = RatatuiRuby.poll_event
|
|
336
|
-
case event
|
|
337
|
-
in {type: :mouse, kind: "down", button: "left", x:, y:}
|
|
338
|
-
if @export_area_rect&.contains?(x, y) # Using cached rect
|
|
339
|
-
@copy_dialog_text = @current_color.to_hex.upcase
|
|
340
|
-
@copy_dialog_active = true
|
|
341
|
-
end
|
|
342
|
-
# ...
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
**Problems:**
|
|
349
|
-
- `calculate_layout` duplicates the exact same layout structure as `render`.
|
|
350
|
-
- Changes to layout in `render` require manual updates to `calculate_layout`.
|
|
351
|
-
- Fragile: rect caching is manual and easy to forget.
|
|
352
|
-
|
|
353
|
-
### After (With `layout_id`)
|
|
354
|
-
|
|
355
|
-
```ruby
|
|
356
|
-
class ColorPickerApp
|
|
357
|
-
def initialize
|
|
358
|
-
@input = "#F96302"
|
|
359
|
-
@current_color = parse_color(@input)
|
|
360
|
-
@error_message = ""
|
|
361
|
-
@layout_info = {} # Will be populated by draw()
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
def run
|
|
365
|
-
RatatuiRuby.run do
|
|
366
|
-
loop do
|
|
367
|
-
render
|
|
368
|
-
result = handle_input
|
|
369
|
-
break if result == :quit
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
def render
|
|
375
|
-
main_ui = RatatuiRuby::Layout.new(
|
|
376
|
-
direction: :vertical,
|
|
377
|
-
layout_id: :main, # Tag the main layout
|
|
378
|
-
constraints: [
|
|
379
|
-
RatatuiRuby::Constraint.length(3),
|
|
380
|
-
RatatuiRuby::Constraint.length(14),
|
|
381
|
-
RatatuiRuby::Constraint.fill(1)
|
|
382
|
-
],
|
|
383
|
-
children: [
|
|
384
|
-
build_input_section,
|
|
385
|
-
build_color_section(layout_id: :color_section), # Tag child layouts
|
|
386
|
-
build_controls_section
|
|
387
|
-
]
|
|
388
|
-
)
|
|
389
|
-
@layout_info = RatatuiRuby.draw(main_ui) || {} # Capture layout info
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
def build_color_section(layout_id: nil)
|
|
393
|
-
RatatuiRuby::Layout.new(
|
|
394
|
-
direction: :vertical,
|
|
395
|
-
layout_id: layout_id,
|
|
396
|
-
constraints: [
|
|
397
|
-
RatatuiRuby::Constraint.length(7),
|
|
398
|
-
RatatuiRuby::Constraint.fill(1)
|
|
399
|
-
],
|
|
400
|
-
children: [
|
|
401
|
-
build_harmonies,
|
|
402
|
-
RatatuiRuby::Block.new(
|
|
403
|
-
title: "Export Formats",
|
|
404
|
-
layout_id: :export_formats, # Tag the export block
|
|
405
|
-
borders: [:all],
|
|
406
|
-
children: [
|
|
407
|
-
build_export_content
|
|
408
|
-
]
|
|
409
|
-
)
|
|
410
|
-
]
|
|
411
|
-
)
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
def handle_input
|
|
415
|
-
event = RatatuiRuby.poll_event
|
|
416
|
-
case event
|
|
417
|
-
in {type: :mouse, kind: "down", button: "left", x:, y:}
|
|
418
|
-
if @layout_info[:export_formats]&.contains?(x, y) # No manual caching!
|
|
419
|
-
@copy_dialog_text = @current_color.to_hex.upcase
|
|
420
|
-
@copy_dialog_active = true
|
|
421
|
-
end
|
|
422
|
-
# ...
|
|
423
|
-
end
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
**Benefits:**
|
|
429
|
-
- **Single source of truth**: Layout is defined once in `render`, not duplicated in `calculate_layout`.
|
|
430
|
-
- **Automatic tracking**: As you modify the UI tree, rects are automatically updated by the same render pass.
|
|
431
|
-
- **No manual caching**: Use `@layout_info` directly from `draw()`.
|
|
432
|
-
- **Declarative**: Tag regions with semantic IDs, making hit testing code self-documenting.
|
|
433
|
-
|
|
434
|
-
## Design Alignment
|
|
435
|
-
|
|
436
|
-
### Immediate-Mode Rendering
|
|
437
|
-
|
|
438
|
-
This proposal **preserves** immediate-mode principles:
|
|
439
|
-
|
|
440
|
-
- **Every frame**, the app constructs a fresh UI tree from current state.
|
|
441
|
-
- **Every frame**, `draw()` consumes that tree and renders it.
|
|
442
|
-
- **Returns**: Layout metadata computed during the same render pass.
|
|
443
|
-
|
|
444
|
-
The key insight: Returning layout info is **not** retained state; it's a **by-product** of the render, computed fresh each frame.
|
|
445
|
-
|
|
446
|
-
### Data-Driven UI
|
|
447
|
-
|
|
448
|
-
Widgets remain immutable data objects; adding `layout_id` is just an optional annotation:
|
|
449
|
-
|
|
450
|
-
```ruby
|
|
451
|
-
# Still pure data:
|
|
452
|
-
widget = RatatuiRuby::Block.new(
|
|
453
|
-
title: "Foo",
|
|
454
|
-
layout_id: :my_widget, # Just metadata, not behavior
|
|
455
|
-
borders: [:all]
|
|
456
|
-
)
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
No rendering logic moves to Ruby.
|
|
460
|
-
|
|
461
|
-
### Rust Backend Alignment
|
|
462
|
-
|
|
463
|
-
In Rust Ratatui, the `Frame` tracks where widgets are rendered:
|
|
464
|
-
|
|
465
|
-
```rust
|
|
466
|
-
let mut frame = Terminal::new(backend)?;
|
|
467
|
-
frame.render_widget(widget, area); // Frame knows where this widget is now
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
Returning layout info from `draw()` mirrors this: the Rust backend knows where things ended up, and returns that information to Ruby.
|
|
471
|
-
|
|
472
|
-
## Alternatives Considered
|
|
473
|
-
|
|
474
|
-
### Alternative 1: Widgets Maintain Their Own State
|
|
475
|
-
|
|
476
|
-
Store rects on mutable widget objects. **Rejected** because:
|
|
477
|
-
- Violates immediate-mode and data-driven design.
|
|
478
|
-
- Requires mutable state tracking in Rust.
|
|
479
|
-
- Complicates the simplicity of immutable data objects.
|
|
480
|
-
|
|
481
|
-
### Alternative 2: Full Frame Object
|
|
482
|
-
|
|
483
|
-
Return a `Frame` object (similar to Ratatui's Frame) that tracks all rendering details. **Rejected** because:
|
|
484
|
-
- Over-engineered for the current need.
|
|
485
|
-
- RatatuiRuby is intentionally minimal.
|
|
486
|
-
- Overkill if the app only cares about hit testing a few regions.
|
|
487
|
-
|
|
488
|
-
### Alternative 3: Callback-Based Rendering
|
|
489
|
-
|
|
490
|
-
Allow widgets to register callbacks when rendered. **Rejected** because:
|
|
491
|
-
- Adds complexity and statefulness.
|
|
492
|
-
- Less idiomatic for Ruby.
|
|
493
|
-
- Harder to reason about in immediate-mode loop.
|
|
494
|
-
|
|
495
|
-
### Alternative 4: Hit Testing DSL
|
|
496
|
-
|
|
497
|
-
Provide a declarative hit testing layer separate from rendering. **Rejected** because:
|
|
498
|
-
- Duplicates layout info (still two sources of truth).
|
|
499
|
-
- Unnecessary indirection.
|
|
500
|
-
|
|
501
|
-
## Impact Assessment
|
|
502
|
-
|
|
503
|
-
### User-Facing Changes
|
|
504
|
-
|
|
505
|
-
- **New optional parameter**: `layout_id` on `Layout`, `Block`, and similar container widgets.
|
|
506
|
-
- **New return value**: `RatatuiRuby.draw()` optionally returns a Hash of rects.
|
|
507
|
-
- **Zero breaking changes**: Existing apps without `layout_id` work unchanged.
|
|
508
|
-
|
|
509
|
-
### Documentation Updates
|
|
510
|
-
|
|
511
|
-
- **Update RDoc** for `Layout` and `Block` to document `layout_id`.
|
|
512
|
-
- **Add example**: `examples/hit_test/app.rb` (or new example) showing the pattern.
|
|
513
|
-
- **Update `doc/interactive_design.md`** with the new approach.
|
|
514
|
-
|
|
515
|
-
### Testing
|
|
516
|
-
|
|
517
|
-
- **Unit tests (Rust)**: Verify that rects are collected and returned correctly.
|
|
518
|
-
- **Integration tests (Ruby)**: Verify hit testing works with returned layout info.
|
|
519
|
-
- **Example app**: Ensure the color picker and hit test examples demonstrate the pattern.
|
|
520
|
-
|
|
521
|
-
## Timeline & Scope
|
|
522
|
-
|
|
523
|
-
**Scope**: Pre-1.0 feature. Fits RatatuiRuby's design philosophy and solves a real pain point.
|
|
524
|
-
|
|
525
|
-
**Estimated effort**:
|
|
526
|
-
- Rust backend: 4–6 hours (add `layout_id` extraction, rect collection, Hash construction)
|
|
527
|
-
- Ruby side: 2–3 hours (add parameter to widget classes, update `.rbs`, docs)
|
|
528
|
-
- Testing & examples: 2–3 hours
|
|
529
|
-
- **Total**: ~10 hours
|
|
530
|
-
|
|
531
|
-
**Risk**: Low. The change is additive (optional parameter, new return value). Backward compatible.
|
|
532
|
-
|
|
533
|
-
## Recommendation
|
|
534
|
-
|
|
535
|
-
**Approve**. This proposal:
|
|
536
|
-
|
|
537
|
-
1. Eliminates a real pain point (layout duplication).
|
|
538
|
-
2. Aligns with immediate-mode and data-driven design.
|
|
539
|
-
3. Mirrors how Rust Ratatui works (Frame tracks layout).
|
|
540
|
-
4. Requires no breaking changes.
|
|
541
|
-
5. Is low-risk and achievable pre-1.0.
|
|
542
|
-
|
|
543
|
-
Implement as **Option A (Plain Hash)** first. It's simpler and sufficient for hit testing. Evolve to `LayoutInfo` class if more complex queries are needed later.
|
|
@@ -1,82 +0,0 @@
|
|
|
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/images/all_events.png
DELETED
|
Binary file
|
|
Binary file
|
data/doc/images/block_titles.png
DELETED
|
Binary file
|
data/doc/images/box_demo.png
DELETED
|
Binary file
|
|
Binary file
|
data/doc/images/cell_demo.png
DELETED
|
Binary file
|
data/doc/images/chart_demo.png
DELETED
|
Binary file
|
data/doc/images/flex_layout.png
DELETED
|
Binary file
|
data/doc/images/gauge_demo.png
DELETED
|
Binary file
|
|
Binary file
|
data/doc/images/list_demo.png
DELETED
|
Binary file
|
data/doc/images/readme_usage.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/doc/images/table_flex.png
DELETED
|
Binary file
|