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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
7
|
require "ratatui_ruby"
|
|
8
|
+
require "faker" # Use Faker for large, realistic datasets
|
|
8
9
|
|
|
9
10
|
# Demonstrates a selectable list of items with interactive attribute cycling.
|
|
10
11
|
#
|
|
@@ -24,111 +25,18 @@ require "ratatui_ruby"
|
|
|
24
25
|
class WidgetListDemo
|
|
25
26
|
# Initializes the demo with example data and default configuration.
|
|
26
27
|
def initialize
|
|
27
|
-
|
|
28
|
+
Faker::Config.random = Random.new(12345)
|
|
29
|
+
@selected_index = 6 # Start at C# to avoid highlighting the rich text examples
|
|
30
|
+
@tui_for_setup = nil
|
|
28
31
|
|
|
29
32
|
@item_sets = [
|
|
30
|
-
{
|
|
31
|
-
name: "Large List",
|
|
32
|
-
items: (1..200).map { |i| "Item #{i}" },
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "Colors",
|
|
36
|
-
items: [
|
|
37
|
-
"Red",
|
|
38
|
-
"Orange",
|
|
39
|
-
"Yellow",
|
|
40
|
-
"Green",
|
|
41
|
-
"Cyan",
|
|
42
|
-
"Blue",
|
|
43
|
-
"Indigo",
|
|
44
|
-
"Violet",
|
|
45
|
-
"Scarlet",
|
|
46
|
-
"Crimson",
|
|
47
|
-
"Maroon",
|
|
48
|
-
"Brown",
|
|
49
|
-
"Tan",
|
|
50
|
-
"Beige",
|
|
51
|
-
"Khaki",
|
|
52
|
-
"Gold",
|
|
53
|
-
"Silver",
|
|
54
|
-
"White",
|
|
55
|
-
"Gray",
|
|
56
|
-
"Black",
|
|
57
|
-
"Pink",
|
|
58
|
-
"Magenta",
|
|
59
|
-
"Turquoise",
|
|
60
|
-
"Teal",
|
|
61
|
-
"Coral",
|
|
62
|
-
"Salmon",
|
|
63
|
-
"Peach",
|
|
64
|
-
"Lavender",
|
|
65
|
-
"Lilac",
|
|
66
|
-
"Olive",
|
|
67
|
-
"Lime",
|
|
68
|
-
"Navy",
|
|
69
|
-
"Charcoal",
|
|
70
|
-
"Ivory",
|
|
71
|
-
"Azure",
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: "Fruits",
|
|
76
|
-
items: [
|
|
77
|
-
"Apple",
|
|
78
|
-
"Apricot",
|
|
79
|
-
"Avocado",
|
|
80
|
-
"Banana",
|
|
81
|
-
"Blueberry",
|
|
82
|
-
"Blackberry",
|
|
83
|
-
"Cherry",
|
|
84
|
-
"Cranberry",
|
|
85
|
-
"Cucumber",
|
|
86
|
-
"Date",
|
|
87
|
-
"Dragonfruit",
|
|
88
|
-
"Elderberry",
|
|
89
|
-
"Fig",
|
|
90
|
-
"Grape",
|
|
91
|
-
"Grapefruit",
|
|
92
|
-
"Guava",
|
|
93
|
-
"Honeydew",
|
|
94
|
-
"Huckleberry",
|
|
95
|
-
"Jackfruit",
|
|
96
|
-
"Kiwi",
|
|
97
|
-
"Kumquat",
|
|
98
|
-
"Lemon",
|
|
99
|
-
"Lime",
|
|
100
|
-
"Lychee",
|
|
101
|
-
"Mango",
|
|
102
|
-
"Melon",
|
|
103
|
-
"Mulberry",
|
|
104
|
-
"Nectarine",
|
|
105
|
-
"Olive",
|
|
106
|
-
"Orange",
|
|
107
|
-
"Papaya",
|
|
108
|
-
"Passion Fruit",
|
|
109
|
-
"Peach",
|
|
110
|
-
"Pear",
|
|
111
|
-
"Persimmon",
|
|
112
|
-
"Pineapple",
|
|
113
|
-
"Plum",
|
|
114
|
-
"Pomegranate",
|
|
115
|
-
"Prune",
|
|
116
|
-
"Rambutan",
|
|
117
|
-
"Raspberry",
|
|
118
|
-
"Starfruit",
|
|
119
|
-
"Strawberry",
|
|
120
|
-
"Tangerine",
|
|
121
|
-
"Watermelon",
|
|
122
|
-
"Ugli Fruit",
|
|
123
|
-
],
|
|
124
|
-
},
|
|
125
33
|
{
|
|
126
34
|
name: "Programming",
|
|
127
35
|
items: [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
36
|
+
:ruby_styled, # Will be replaced with rich text in run()
|
|
37
|
+
:rust_styled, # Will be replaced with rich text in run()
|
|
38
|
+
:python_styled, # Will be replaced with rich text in run()
|
|
39
|
+
:javascript_styled, # Will be replaced with rich text in run()
|
|
132
40
|
"Go",
|
|
133
41
|
"C++",
|
|
134
42
|
"C#",
|
|
@@ -175,6 +83,24 @@ class WidgetListDemo
|
|
|
175
83
|
"BASIC",
|
|
176
84
|
],
|
|
177
85
|
},
|
|
86
|
+
{
|
|
87
|
+
name: "Large List",
|
|
88
|
+
items: (1..200).map { |i| "Item #{i}" },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "Colors",
|
|
92
|
+
items: begin
|
|
93
|
+
Faker::Color.unique.clear
|
|
94
|
+
Array.new(100) { Faker::Color.color_name }
|
|
95
|
+
end,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "Fruits",
|
|
99
|
+
items: begin
|
|
100
|
+
Faker::Food.unique.clear
|
|
101
|
+
Array.new(100) { Faker::Food.fruits }
|
|
102
|
+
end,
|
|
103
|
+
},
|
|
178
104
|
]
|
|
179
105
|
@item_set_index = 0
|
|
180
106
|
|
|
@@ -192,7 +118,7 @@ class WidgetListDemo
|
|
|
192
118
|
{ name: "Always", spacing: :always },
|
|
193
119
|
{ name: "Never", spacing: :never },
|
|
194
120
|
]
|
|
195
|
-
@highlight_spacing_index =
|
|
121
|
+
@highlight_spacing_index = 1
|
|
196
122
|
|
|
197
123
|
@repeat_modes = [
|
|
198
124
|
{ name: "Off", repeat: false },
|
|
@@ -205,7 +131,15 @@ class WidgetListDemo
|
|
|
205
131
|
{ name: "1 item", padding: 1 },
|
|
206
132
|
{ name: "2 items", padding: 2 },
|
|
207
133
|
]
|
|
208
|
-
@scroll_padding_index =
|
|
134
|
+
@scroll_padding_index = 1
|
|
135
|
+
|
|
136
|
+
# Offset mode configurations to demonstrate offset + selection interaction
|
|
137
|
+
@offset_modes = [
|
|
138
|
+
{ name: "Auto (No Offset)", offset: nil, allow_selection: true },
|
|
139
|
+
{ name: "Offset Only", offset: 10, allow_selection: false },
|
|
140
|
+
{ name: "Selection + Offset (Conflict)", offset: 0, allow_selection: true },
|
|
141
|
+
]
|
|
142
|
+
@offset_mode_index = 0
|
|
209
143
|
end
|
|
210
144
|
|
|
211
145
|
# Runs the demo application.
|
|
@@ -214,8 +148,45 @@ class WidgetListDemo
|
|
|
214
148
|
def run
|
|
215
149
|
RatatuiRuby.run do |tui|
|
|
216
150
|
@tui = tui
|
|
151
|
+
|
|
152
|
+
# Create rich text for "Ruby" - each letter with a different red style
|
|
153
|
+
ruby_line = @tui.text_line(spans: [
|
|
154
|
+
@tui.text_span(content: "R", style: @tui.style(fg: :red, modifiers: [:underlined])),
|
|
155
|
+
@tui.text_span(content: "u", style: @tui.style(fg: :light_red, modifiers: [:bold])),
|
|
156
|
+
@tui.text_span(content: "b", style: @tui.style(fg: :red, modifiers: [:italic])),
|
|
157
|
+
@tui.text_span(content: "y", style: @tui.style(fg: :light_red, modifiers: [:reversed])),
|
|
158
|
+
])
|
|
159
|
+
|
|
160
|
+
# Create rich text for "Rust" - single styled Span
|
|
161
|
+
rust_span = @tui.text_span(
|
|
162
|
+
content: "Rust",
|
|
163
|
+
style: @tui.style(fg: :magenta, modifiers: [:bold, :underlined])
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Create ListItem for "Python" - demonstrates content + row background
|
|
167
|
+
python_item = @tui.list_item(
|
|
168
|
+
content: @tui.text_span(content: "Python", style: @tui.style(fg: :yellow)),
|
|
169
|
+
style: @tui.style(bg: :dark_gray)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Create ListItem for "JavaScript" - demonstrates styled text with row background
|
|
173
|
+
javascript_item = @tui.list_item(
|
|
174
|
+
content: @tui.text_line(spans: [
|
|
175
|
+
@tui.text_span(content: "Java", style: @tui.style(fg: :yellow, modifiers: [:bold])),
|
|
176
|
+
@tui.text_span(content: "Script", style: @tui.style(fg: :light_yellow, modifiers: [:italic])),
|
|
177
|
+
]),
|
|
178
|
+
style: @tui.style(bg: :blue)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Replace the styled placeholders
|
|
182
|
+
@item_sets[0][:items][0] = ruby_line
|
|
183
|
+
@item_sets[0][:items][1] = rust_span
|
|
184
|
+
@item_sets[0][:items][2] = python_item
|
|
185
|
+
@item_sets[0][:items][3] = javascript_item
|
|
186
|
+
|
|
217
187
|
# Initialize styles that require @tui
|
|
218
188
|
@highlight_styles = [
|
|
189
|
+
{ name: "Blue on White Bold", style: @tui.style(fg: :blue, bg: :white, modifiers: [:bold]) },
|
|
219
190
|
{ name: "Blue Bold", style: @tui.style(fg: :blue, modifiers: [:bold]) },
|
|
220
191
|
{ name: "Yellow on Black", style: @tui.style(fg: :yellow, bg: :black) },
|
|
221
192
|
{ name: "Green Italic", style: @tui.style(fg: :green, modifiers: [:italic]) },
|
|
@@ -245,7 +216,6 @@ class WidgetListDemo
|
|
|
245
216
|
# :nodoc:
|
|
246
217
|
private def render
|
|
247
218
|
items = @item_sets[@item_set_index][:items]
|
|
248
|
-
selection_label = @selected_index.nil? ? "none" : @selected_index.to_s
|
|
249
219
|
direction_config = @direction_configs[@direction_index]
|
|
250
220
|
spacing_config = @highlight_spacing_configs[@highlight_spacing_index]
|
|
251
221
|
repeat_config = @repeat_modes[@repeat_index]
|
|
@@ -253,6 +223,13 @@ class WidgetListDemo
|
|
|
253
223
|
highlight_symbol = @highlight_symbol_names[@highlight_symbol_index]
|
|
254
224
|
base_style_config = @base_styles[@base_style_index]
|
|
255
225
|
scroll_padding_config = @scroll_padding_configs[@scroll_padding_index]
|
|
226
|
+
offset_mode_config = @offset_modes[@offset_mode_index]
|
|
227
|
+
|
|
228
|
+
# Determine selection/offset based on mode
|
|
229
|
+
effective_selection = offset_mode_config[:allow_selection] ? @selected_index : nil
|
|
230
|
+
effective_offset = offset_mode_config[:offset]
|
|
231
|
+
selection_label = effective_selection.nil? ? "none" : effective_selection.to_s
|
|
232
|
+
offset_label = effective_offset.nil? ? "auto" : effective_offset.to_s
|
|
256
233
|
|
|
257
234
|
@tui.draw do |frame|
|
|
258
235
|
# Split into main content and control panel
|
|
@@ -261,7 +238,7 @@ class WidgetListDemo
|
|
|
261
238
|
direction: :vertical,
|
|
262
239
|
constraints: [
|
|
263
240
|
@tui.constraint_fill(1),
|
|
264
|
-
@tui.constraint_length(
|
|
241
|
+
@tui.constraint_length(8),
|
|
265
242
|
]
|
|
266
243
|
)
|
|
267
244
|
|
|
@@ -282,7 +259,8 @@ class WidgetListDemo
|
|
|
282
259
|
# Render list
|
|
283
260
|
list = @tui.list(
|
|
284
261
|
items:,
|
|
285
|
-
selected_index:
|
|
262
|
+
selected_index: effective_selection,
|
|
263
|
+
offset: effective_offset,
|
|
286
264
|
style: base_style_config[:style],
|
|
287
265
|
highlight_style: highlight_style_config[:style],
|
|
288
266
|
highlight_symbol:,
|
|
@@ -291,7 +269,7 @@ class WidgetListDemo
|
|
|
291
269
|
direction: direction_config[:direction],
|
|
292
270
|
scroll_padding: scroll_padding_config[:padding],
|
|
293
271
|
block: @tui.block(
|
|
294
|
-
title: "#{@item_sets[@item_set_index][:name]}
|
|
272
|
+
title: "#{@item_sets[@item_set_index][:name]} | Sel: #{selection_label} | Offset: #{offset_label}",
|
|
295
273
|
borders: [:all]
|
|
296
274
|
)
|
|
297
275
|
)
|
|
@@ -330,7 +308,11 @@ class WidgetListDemo
|
|
|
330
308
|
@tui.text_span(content: "b", style: @hotkey_style),
|
|
331
309
|
@tui.text_span(content: ": Base (#{base_style_config[:name]}) "),
|
|
332
310
|
@tui.text_span(content: "r", style: @hotkey_style),
|
|
333
|
-
@tui.text_span(content: ": Repeat (#{repeat_config[:name]})
|
|
311
|
+
@tui.text_span(content: ": Repeat (#{repeat_config[:name]})"),
|
|
312
|
+
]),
|
|
313
|
+
@tui.text_line(spans: [
|
|
314
|
+
@tui.text_span(content: "o", style: @hotkey_style),
|
|
315
|
+
@tui.text_span(content: ": Offset Mode (#{offset_mode_config[:name]}) "),
|
|
334
316
|
@tui.text_span(content: "q", style: @hotkey_style),
|
|
335
317
|
@tui.text_span(content: ": Quit"),
|
|
336
318
|
]),
|
|
@@ -373,6 +355,8 @@ class WidgetListDemo
|
|
|
373
355
|
@repeat_index = (@repeat_index + 1) % @repeat_modes.size
|
|
374
356
|
in type: :key, code: "p"
|
|
375
357
|
@scroll_padding_index = (@scroll_padding_index + 1) % @scroll_padding_configs.size
|
|
358
|
+
in type: :key, code: "o"
|
|
359
|
+
@offset_mode_index = (@offset_mode_index + 1) % @offset_modes.size
|
|
376
360
|
else
|
|
377
361
|
nil
|
|
378
362
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Canvas Widget Example
|
|
7
|
+
|
|
8
|
+
[](app.rb)
|
|
9
|
+
|
|
10
|
+
Demonstrates drawing custom graphics and maps using the standard Braille and Block patterns.
|
|
11
|
+
|
|
12
|
+
Standard widgets are great for text, but sometimes you need to draw. The `Canvas` widget gives you a high-resolution coordinate system (x, y) to render shapes, lines, and data visualizations that go beyond the grid.
|
|
13
|
+
|
|
14
|
+
## Features Demonstrated
|
|
15
|
+
|
|
16
|
+
- **High-Resolution Drawing**: Using Braille patterns (`⣿`) to effectively double the vertical and horizontal resolution of the terminal.
|
|
17
|
+
- **Layers**: Drawing multiple shapes (Map, Circles, Lines) in a specific order.
|
|
18
|
+
- **Animation**: Updating coordinates in a loop to create smooth motion.
|
|
19
|
+
- **World Map**: Using the built-in `Map` shape for geographic data.
|
|
20
|
+
|
|
21
|
+
## Hotkeys
|
|
22
|
+
|
|
23
|
+
- **b**: Cycle Background Color (`background_color`)
|
|
24
|
+
- **m**: Cycle Marker Type (`marker`)
|
|
25
|
+
- **l**: Toggle Labels (modifies `shapes`)
|
|
26
|
+
- **q**: Quit
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
ruby examples/widget_map_demo/app.rb
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Learning Outcomes
|
|
35
|
+
|
|
36
|
+
Use this example if you need to...
|
|
37
|
+
- Render geographic data (World, USA, Europe).
|
|
38
|
+
- Overlay custom labels and markers on a map.
|
|
39
|
+
- Animate visual elements on top of a static background.
|
|
@@ -7,15 +7,15 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
|
7
7
|
require "ratatui_ruby"
|
|
8
8
|
|
|
9
9
|
# An example of the Canvas widget showing a world map and animated shapes.
|
|
10
|
-
class
|
|
11
|
-
include RatatuiRuby
|
|
10
|
+
class WidgetMapDemo
|
|
11
|
+
include RatatuiRuby::Widgets
|
|
12
12
|
|
|
13
13
|
COLORS = [:black, :blue, :white, nil].freeze
|
|
14
14
|
MARKERS = [:braille, :half_block, :dot, :block, :bar, :quadrant, :sextant, :octant].freeze
|
|
15
15
|
|
|
16
16
|
# Returns a Canvas view for the map demo with the given circle radius.
|
|
17
17
|
#
|
|
18
|
-
# +tui+:: The RatatuiRuby::
|
|
18
|
+
# +tui+:: The RatatuiRuby::TUI instance.
|
|
19
19
|
# +radius+:: The radius of the animated circle.
|
|
20
20
|
# +marker+:: The marker type.
|
|
21
21
|
# +background_color+:: The background color of the canvas.
|
|
@@ -90,4 +90,4 @@ class AppMapDemo
|
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
WidgetMapDemo.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
# Overlay Widget Demo
|
|
6
|
+
|
|
7
|
+
[](app.rb)
|
|
8
|
+
|
|
9
|
+
This example demonstrates the `Overlay` composition pattern for layering widgets with depth. Modals, notifications, and floating panels all require stacking widgets on top of each other.
|
|
10
|
+
|
|
11
|
+
## Key Concepts
|
|
12
|
+
|
|
13
|
+
- **Layer Composition:** Rendering widgets in order creates visual depth — later renders appear "on top."
|
|
14
|
+
- **Clear Widget:** Using `tui.clear` before rendering a modal erases the background, preventing content bleed-through.
|
|
15
|
+
- **Dynamic Layer Control:** Toggle the number of visible overlay layers at runtime.
|
|
16
|
+
- **Layer Ordering:** Swap which overlay appears in front to demonstrate z-ordering.
|
|
17
|
+
|
|
18
|
+
## Hotkeys
|
|
19
|
+
|
|
20
|
+
- `0`/`1`/`2`: Set number of visible overlay layers
|
|
21
|
+
- `space`: Swap overlay order (which modal is on top)
|
|
22
|
+
- `c`: Toggle Clear widget (on/off)
|
|
23
|
+
- `q`: Quit
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
ruby examples/widget_overlay_demo/app.rb
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Learning Outcomes
|
|
32
|
+
|
|
33
|
+
Use this example if you need to...
|
|
34
|
+
- Build modal dialogs or confirmation popups.
|
|
35
|
+
- Layer notifications over existing content.
|
|
36
|
+
- Understand the Clear widget's role in opaque overlays.
|
|
@@ -0,0 +1,248 @@
|
|
|
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
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
7
|
+
require "ratatui_ruby"
|
|
8
|
+
|
|
9
|
+
HEADLINES = [
|
|
10
|
+
"Scientists Discover New Species of Deep-Sea Octopus Near Hawaii",
|
|
11
|
+
"Global Climate Summit Reaches Historic Agreement on Emissions",
|
|
12
|
+
"Tech Giant Announces Breakthrough in Quantum Computing Research",
|
|
13
|
+
"Local Community Garden Initiative Expands to Ten More Cities",
|
|
14
|
+
"Astronomers Detect Unusual Radio Signals from Distant Galaxy",
|
|
15
|
+
"New Study Links Mediterranean Diet to Improved Heart Health",
|
|
16
|
+
"Electric Vehicle Sales Surge as Battery Technology Improves",
|
|
17
|
+
"Ancient Manuscripts Reveal Previously Unknown Trading Routes",
|
|
18
|
+
"Renewable Energy Now Powers 40% of National Grid",
|
|
19
|
+
"Robotics Team Develops AI System for Disaster Response",
|
|
20
|
+
"Archaeological Dig Uncovers Evidence of Early Human Settlement",
|
|
21
|
+
"Major Airline Commits to Carbon-Neutral Flights by 2035",
|
|
22
|
+
"Breakthrough Treatment Shows Promise for Rare Genetic Disease",
|
|
23
|
+
"City Council Approves Expanded Public Transportation Network",
|
|
24
|
+
"Marine Biologists Track Migration Patterns of Endangered Whales",
|
|
25
|
+
"New App Helps Farmers Optimize Water Usage During Drought",
|
|
26
|
+
"International Space Station Extends Mission Timeline to 2030",
|
|
27
|
+
"Local Schools Implement Innovative STEM Education Program",
|
|
28
|
+
"Wildlife Conservation Efforts Lead to Species Population Recovery",
|
|
29
|
+
"Research Team Creates Biodegradable Alternative to Plastic Packaging",
|
|
30
|
+
"Historic Theater Restoration Project Nears Completion",
|
|
31
|
+
"Cybersecurity Experts Warn of Emerging Online Threats",
|
|
32
|
+
"Community Food Bank Serves Record Number of Families This Year",
|
|
33
|
+
"Innovative Urban Planning Reduces Traffic Congestion by 30%",
|
|
34
|
+
].freeze
|
|
35
|
+
|
|
36
|
+
# Overlay Demo Example
|
|
37
|
+
# Demonstrates the Overlay widget for layering widgets with depth.
|
|
38
|
+
class WidgetOverlayDemo
|
|
39
|
+
def initialize
|
|
40
|
+
@layer_count = 2 # Start with 2 layers visible
|
|
41
|
+
@swapped = false
|
|
42
|
+
@clear = true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def run
|
|
46
|
+
RatatuiRuby.run do |tui|
|
|
47
|
+
@tui = tui
|
|
48
|
+
loop do
|
|
49
|
+
tui.draw do |frame|
|
|
50
|
+
render(frame)
|
|
51
|
+
end
|
|
52
|
+
break if handle_input == :quit
|
|
53
|
+
sleep 0.05
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private def render(frame)
|
|
59
|
+
area = frame.area
|
|
60
|
+
|
|
61
|
+
# Split into main area and control panel
|
|
62
|
+
layout = @tui.layout_split(
|
|
63
|
+
area,
|
|
64
|
+
direction: :vertical,
|
|
65
|
+
constraints: [
|
|
66
|
+
@tui.constraint_fill(1),
|
|
67
|
+
@tui.constraint_length(5),
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
main_area = layout[0]
|
|
72
|
+
control_area = layout[1]
|
|
73
|
+
|
|
74
|
+
# Render background layer - RSS reader
|
|
75
|
+
frame.render_widget(background_layer, main_area)
|
|
76
|
+
|
|
77
|
+
# Render upper layers based on layer_count and swap state
|
|
78
|
+
if @swapped
|
|
79
|
+
render_beta_layer(frame, main_area) if @layer_count >= 2
|
|
80
|
+
render_notification_layer(frame, main_area) if @layer_count >= 1
|
|
81
|
+
else
|
|
82
|
+
render_notification_layer(frame, main_area) if @layer_count >= 1
|
|
83
|
+
render_beta_layer(frame, main_area) if @layer_count >= 2
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Render control panel
|
|
87
|
+
frame.render_widget(control_panel, control_area)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def background_layer
|
|
91
|
+
@background_layer ||= @tui.list(
|
|
92
|
+
items: HEADLINES,
|
|
93
|
+
block: @tui.block(
|
|
94
|
+
title: "RSS Reader",
|
|
95
|
+
borders: [:all]
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def render_notification_layer(frame, area)
|
|
101
|
+
# Position modal: 20% from top, 60% height, 15% from left, 70% width
|
|
102
|
+
|
|
103
|
+
vertical_sections = @tui.layout_split(
|
|
104
|
+
area,
|
|
105
|
+
direction: :vertical,
|
|
106
|
+
constraints: [
|
|
107
|
+
@tui.constraint_fill(2),
|
|
108
|
+
@tui.constraint_fill(5),
|
|
109
|
+
@tui.constraint_fill(3),
|
|
110
|
+
]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
horizontal_sections = @tui.layout_split(
|
|
114
|
+
vertical_sections[1],
|
|
115
|
+
direction: :horizontal,
|
|
116
|
+
constraints: [
|
|
117
|
+
@tui.constraint_fill(1),
|
|
118
|
+
@tui.constraint_fill(5),
|
|
119
|
+
@tui.constraint_fill(1),
|
|
120
|
+
]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
modal_rect = horizontal_sections[1]
|
|
124
|
+
|
|
125
|
+
frame.render_widget(@tui.clear, modal_rect) if @clear
|
|
126
|
+
|
|
127
|
+
# Render the modal content
|
|
128
|
+
frame.render_widget(
|
|
129
|
+
@tui.paragraph(
|
|
130
|
+
text: "Your feeds have been updated",
|
|
131
|
+
wrap: true,
|
|
132
|
+
alignment: :center,
|
|
133
|
+
block: @tui.block(
|
|
134
|
+
title: "Notification",
|
|
135
|
+
borders: [:all],
|
|
136
|
+
border_style: @tui.style(fg: :black),
|
|
137
|
+
style: @tui.style(bg: :red, fg: :black)
|
|
138
|
+
)
|
|
139
|
+
),
|
|
140
|
+
modal_rect
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def render_beta_layer(frame, area)
|
|
145
|
+
# Position modal: 30% from top, 40% height, 25% from left, 50% width
|
|
146
|
+
|
|
147
|
+
vertical_sections = @tui.layout_split(
|
|
148
|
+
area,
|
|
149
|
+
direction: :vertical,
|
|
150
|
+
constraints: [
|
|
151
|
+
@tui.constraint_fill(3),
|
|
152
|
+
@tui.constraint_fill(4),
|
|
153
|
+
@tui.constraint_fill(2),
|
|
154
|
+
]
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
horizontal_sections = @tui.layout_split(
|
|
158
|
+
vertical_sections[1],
|
|
159
|
+
direction: :horizontal,
|
|
160
|
+
constraints: [
|
|
161
|
+
@tui.constraint_fill(2),
|
|
162
|
+
@tui.constraint_fill(3),
|
|
163
|
+
@tui.constraint_fill(2),
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
modal_rect = horizontal_sections[1]
|
|
168
|
+
|
|
169
|
+
frame.render_widget(@tui.clear, modal_rect) if @clear
|
|
170
|
+
|
|
171
|
+
# Render the modal content
|
|
172
|
+
frame.render_widget(
|
|
173
|
+
beta_paragraph,
|
|
174
|
+
modal_rect
|
|
175
|
+
)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def beta_paragraph
|
|
179
|
+
@beta_paragraph ||= @tui.paragraph(
|
|
180
|
+
text: "Thank you for being a beta tester. To give feedback, shout very loudly and we will hear you. Be careful not to scare the llamas.",
|
|
181
|
+
wrap: true,
|
|
182
|
+
alignment: :left,
|
|
183
|
+
block: @tui.block(
|
|
184
|
+
title: "Beta Program",
|
|
185
|
+
borders: [:all],
|
|
186
|
+
border_style: @tui.style(fg: :black),
|
|
187
|
+
style: @tui.style(bg: :blue, fg: :black)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def control_panel
|
|
193
|
+
bold_underline = @tui.style(modifiers: [:bold, :underlined])
|
|
194
|
+
|
|
195
|
+
first_controls = [
|
|
196
|
+
@tui.text_span(content: "0", style: bold_underline),
|
|
197
|
+
@tui.text_span(content: "/"),
|
|
198
|
+
@tui.text_span(content: "1", style: bold_underline),
|
|
199
|
+
@tui.text_span(content: "/"),
|
|
200
|
+
@tui.text_span(content: "2", style: bold_underline),
|
|
201
|
+
@tui.text_span(content: ": Change number of overlays | "),
|
|
202
|
+
@tui.text_span(content: "space", style: bold_underline),
|
|
203
|
+
@tui.text_span(content: ": Swap overlay order"),
|
|
204
|
+
]
|
|
205
|
+
second_controls = [
|
|
206
|
+
@tui.text_span(content: "c", style: bold_underline),
|
|
207
|
+
@tui.text_span(content: ": Toggle clear (currently #{@clear ? 'on' : 'off'})"),
|
|
208
|
+
]
|
|
209
|
+
third_controls = [
|
|
210
|
+
@tui.text_span(content: "q", style: bold_underline),
|
|
211
|
+
@tui.text_span(content: ": Quit"),
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
first = @tui.text_line(spans: first_controls)
|
|
215
|
+
second = @tui.text_line(spans: second_controls)
|
|
216
|
+
third = @tui.text_line(spans: third_controls)
|
|
217
|
+
|
|
218
|
+
@tui.paragraph(
|
|
219
|
+
text: [first, second, third],
|
|
220
|
+
alignment: :center,
|
|
221
|
+
block: @tui.block(
|
|
222
|
+
title: "Controls",
|
|
223
|
+
borders: [:all]
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def handle_input
|
|
229
|
+
case @tui.poll_event
|
|
230
|
+
in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
|
|
231
|
+
:quit
|
|
232
|
+
in { type: :key, code: "0" }
|
|
233
|
+
@layer_count = 0
|
|
234
|
+
in { type: :key, code: "1" }
|
|
235
|
+
@layer_count = 1
|
|
236
|
+
in { type: :key, code: "2" }
|
|
237
|
+
@layer_count = 2
|
|
238
|
+
in { type: :key, code: " " }
|
|
239
|
+
@swapped = !@swapped
|
|
240
|
+
in { type: :key, code: "c" }
|
|
241
|
+
@clear = !@clear
|
|
242
|
+
else
|
|
243
|
+
nil
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
WidgetOverlayDemo.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
3
|
+
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Popup (Clear) Widget Example
|
|
7
|
+
|
|
8
|
+
[](app.rb)
|
|
9
|
+
|
|
10
|
+
Demonstrates how to render opaque overlays on top of content.
|
|
11
|
+
|
|
12
|
+
Terminal renders are additive. If you draw a new widget over an old one, the background colors might mix if not handled correctly. The `Clear` widget resets the area to default (usually transparent/black) to ensure a clean canvas for popups.
|
|
13
|
+
|
|
14
|
+
## Features Demonstrated
|
|
15
|
+
|
|
16
|
+
- **The `Clear` Widget**: Printing spaces over an area to "erase" what was underneath.
|
|
17
|
+
- **Centering**: Using `Layout` constraints to perfectly center a block on screen.
|
|
18
|
+
- **Style Bleed**: showing what happens when you *don't* use `Clear` (background colors leak through).
|
|
19
|
+
|
|
20
|
+
## Hotkeys
|
|
21
|
+
|
|
22
|
+
- **Space**: Toggle Clear Widget (Observe the red background effect when disabled)
|
|
23
|
+
- **q**: Quit
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
ruby examples/widget_popup_demo/app.rb
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Learning Outcomes
|
|
32
|
+
|
|
33
|
+
Use this example if you need to...
|
|
34
|
+
- Create a modal dialog (Confirm, Alert, Form).
|
|
35
|
+
- Implement a dropdown menu that overlays other content.
|
|
36
|
+
- Fix visual artifacts where old text shows through new widgets.
|