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
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
# Custom widget that draws a diagonal line.
|
|
10
|
+
#
|
|
11
|
+
# Demonstrates absolute coordinate rendering respecting the given area bounds.
|
|
12
|
+
# This pattern is essential when custom widgets need to coexist with bordered blocks.
|
|
13
|
+
class DiagonalWidget
|
|
14
|
+
def render(area)
|
|
15
|
+
# Draw a diagonal line within the area's bounds.
|
|
16
|
+
# The area parameter respects parent block borders and padding automatically.
|
|
17
|
+
(0..10).filter_map do |i|
|
|
18
|
+
next if i >= area.width || i >= area.height
|
|
19
|
+
|
|
20
|
+
RatatuiRuby::Draw.string(
|
|
21
|
+
area.x + i,
|
|
22
|
+
area.y + i,
|
|
23
|
+
"\\",
|
|
24
|
+
RatatuiRuby::Style.new(fg: :red, modifiers: [:bold])
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Custom widget that draws a checkerboard pattern.
|
|
31
|
+
#
|
|
32
|
+
# This pattern shows using the area's x, y offset correctly when rendering
|
|
33
|
+
# absolute coordinates. The area parameter may have x, y > 0 when rendered
|
|
34
|
+
# inside a positioned block. Always use area.x and area.y as offsets.
|
|
35
|
+
class CheckerboardWidget
|
|
36
|
+
def initialize(char = "□")
|
|
37
|
+
@char = char
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def render(area)
|
|
41
|
+
result = []
|
|
42
|
+
(0...area.height).each do |row| # rubocop:disable Lint/AmbiguousRange
|
|
43
|
+
(0...area.width).each do |col| # rubocop:disable Lint/AmbiguousRange
|
|
44
|
+
next if (row + col).even?
|
|
45
|
+
|
|
46
|
+
result << RatatuiRuby::Draw.string(
|
|
47
|
+
area.x + col,
|
|
48
|
+
area.y + row,
|
|
49
|
+
@char,
|
|
50
|
+
RatatuiRuby::Style.new(fg: :cyan)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Custom widget that draws a border inside the area.
|
|
59
|
+
#
|
|
60
|
+
# Demonstrates that custom widgets can compose complex shapes using the area's bounds.
|
|
61
|
+
# Here we draw a complete box (corners and edges) that fits within the area,
|
|
62
|
+
# respecting width and height constraints automatically.
|
|
63
|
+
class BorderWidget
|
|
64
|
+
def render(area)
|
|
65
|
+
result = []
|
|
66
|
+
style = RatatuiRuby::Style.new(fg: :green)
|
|
67
|
+
|
|
68
|
+
# Top and bottom
|
|
69
|
+
(0...area.width).each do |x| # rubocop:disable Lint/AmbiguousRange
|
|
70
|
+
result << RatatuiRuby::Draw.string(area.x + x, area.y, "─", style)
|
|
71
|
+
result << RatatuiRuby::Draw.string(area.x + x, area.y + area.height - 1, "─", style)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Left and right
|
|
75
|
+
(0...area.height).each do |y| # rubocop:disable Lint/AmbiguousRange
|
|
76
|
+
result << RatatuiRuby::Draw.string(area.x, area.y + y, "│", style)
|
|
77
|
+
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y + y, "│", style)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Corners
|
|
81
|
+
result << RatatuiRuby::Draw.string(area.x, area.y, "┌", style)
|
|
82
|
+
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y, "┐", style)
|
|
83
|
+
result << RatatuiRuby::Draw.string(area.x, area.y + area.height - 1, "└", style)
|
|
84
|
+
result << RatatuiRuby::Draw.string(area.x + area.width - 1, area.y + area.height - 1, "┘", style)
|
|
85
|
+
|
|
86
|
+
result
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class WidgetRender
|
|
91
|
+
def initialize
|
|
92
|
+
@widget_index = 0
|
|
93
|
+
@widgets = [
|
|
94
|
+
{ name: "Diagonal", widget: DiagonalWidget.new },
|
|
95
|
+
{ name: "Checkerboard", widget: CheckerboardWidget.new("□") },
|
|
96
|
+
{ name: "Border", widget: BorderWidget.new },
|
|
97
|
+
]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def run
|
|
101
|
+
RatatuiRuby.run do |tui|
|
|
102
|
+
@tui = tui
|
|
103
|
+
loop do
|
|
104
|
+
render
|
|
105
|
+
break if handle_input == :quit
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private def render
|
|
111
|
+
@tui.draw do |frame|
|
|
112
|
+
layout = @tui.layout_split(
|
|
113
|
+
frame.area,
|
|
114
|
+
direction: :vertical,
|
|
115
|
+
constraints: [
|
|
116
|
+
@tui.constraint_fill(1),
|
|
117
|
+
@tui.constraint_length(4),
|
|
118
|
+
]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Render a border block to frame widget area
|
|
122
|
+
current_name = @widgets[@widget_index][:name]
|
|
123
|
+
widget_block = @tui.block(
|
|
124
|
+
title: "Custom Widget: #{current_name}",
|
|
125
|
+
borders: [:all]
|
|
126
|
+
)
|
|
127
|
+
frame.render_widget(widget_block, layout[0])
|
|
128
|
+
|
|
129
|
+
# Calculate the inner area, accounting for the block's 1-character border on all sides.
|
|
130
|
+
# This is the key pattern: compute the available space INSIDE the block before
|
|
131
|
+
# passing it to the custom widget's render method.
|
|
132
|
+
# When the custom widget receives this area, all its absolute coordinates will
|
|
133
|
+
# respect the block's boundaries automatically.
|
|
134
|
+
inner_area = @tui.rect(
|
|
135
|
+
x: layout[0].x + 1,
|
|
136
|
+
y: layout[0].y + 1,
|
|
137
|
+
width: [layout[0].width - 2, 0].max,
|
|
138
|
+
height: [layout[0].height - 2, 0].max
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Render the custom widget inside the bordered area.
|
|
142
|
+
# The widget's render method receives the inner_area and draws within it.
|
|
143
|
+
frame.render_widget(@widgets[@widget_index][:widget], inner_area)
|
|
144
|
+
|
|
145
|
+
# Render control panel with current widget info
|
|
146
|
+
control_lines = [
|
|
147
|
+
@tui.text_line(
|
|
148
|
+
spans: [
|
|
149
|
+
@tui.text_span(content: "n", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
150
|
+
@tui.text_span(content: ": Next "),
|
|
151
|
+
@tui.text_span(content: "p", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
152
|
+
@tui.text_span(content: ": Previous "),
|
|
153
|
+
@tui.text_span(content: "q", style: @tui.style(modifiers: [:bold, :underlined])),
|
|
154
|
+
@tui.text_span(content: ": Quit"),
|
|
155
|
+
]
|
|
156
|
+
),
|
|
157
|
+
]
|
|
158
|
+
controls = @tui.paragraph(
|
|
159
|
+
text: control_lines,
|
|
160
|
+
block: @tui.block(
|
|
161
|
+
title: "Controls",
|
|
162
|
+
borders: [:all]
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
frame.render_widget(controls, layout[1])
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private def handle_input
|
|
170
|
+
event = @tui.poll_event
|
|
171
|
+
case event
|
|
172
|
+
in { type: :key, code: "q" }
|
|
173
|
+
:quit
|
|
174
|
+
in { type: :key, code: "n" }
|
|
175
|
+
@widget_index = (@widget_index + 1) % @widgets.length
|
|
176
|
+
in { type: :key, code: "p" }
|
|
177
|
+
@widget_index = (@widget_index - 1) % @widgets.length
|
|
178
|
+
else
|
|
179
|
+
# Ignore other events
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
WidgetRender.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
# Rich Text Example
|
|
10
|
+
# Demonstrates the Span and Line objects for styling individual words
|
|
11
|
+
# within a block of text.
|
|
12
|
+
class WidgetRichText
|
|
13
|
+
def initialize
|
|
14
|
+
@scroll_pos = 0
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
RatatuiRuby.run do |tui|
|
|
19
|
+
@tui = tui
|
|
20
|
+
loop do
|
|
21
|
+
render
|
|
22
|
+
event = handle_input
|
|
23
|
+
break if event == :quit
|
|
24
|
+
sleep 0.05
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private def render
|
|
30
|
+
@tui.draw do |frame|
|
|
31
|
+
layout = @tui.layout_split(
|
|
32
|
+
frame.area,
|
|
33
|
+
direction: :vertical,
|
|
34
|
+
constraints: [
|
|
35
|
+
@tui.constraint_percentage(50),
|
|
36
|
+
@tui.constraint_percentage(50),
|
|
37
|
+
]
|
|
38
|
+
)
|
|
39
|
+
frame.render_widget(simple_text_line_example, layout[0])
|
|
40
|
+
frame.render_widget(complex_example, layout[1])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private def simple_text_line_example
|
|
45
|
+
# Example 1: A line with mixed styles
|
|
46
|
+
@tui.paragraph(
|
|
47
|
+
text: @tui.text_line(
|
|
48
|
+
spans: [
|
|
49
|
+
@tui.text_span(
|
|
50
|
+
content: "Normal text, ",
|
|
51
|
+
style: nil
|
|
52
|
+
),
|
|
53
|
+
@tui.text_span(
|
|
54
|
+
content: "Bold Text",
|
|
55
|
+
style: @tui.style(modifiers: [:bold])
|
|
56
|
+
),
|
|
57
|
+
@tui.text_span(
|
|
58
|
+
content: ", ",
|
|
59
|
+
style: nil
|
|
60
|
+
),
|
|
61
|
+
@tui.text_span(
|
|
62
|
+
content: "Italic Text",
|
|
63
|
+
style: @tui.style(modifiers: [:italic])
|
|
64
|
+
),
|
|
65
|
+
@tui.text_span(
|
|
66
|
+
content: ", ",
|
|
67
|
+
style: nil
|
|
68
|
+
),
|
|
69
|
+
@tui.text_span(
|
|
70
|
+
content: "Red Text",
|
|
71
|
+
style: @tui.style(fg: :red)
|
|
72
|
+
),
|
|
73
|
+
@tui.text_span(
|
|
74
|
+
content: ".",
|
|
75
|
+
style: nil
|
|
76
|
+
),
|
|
77
|
+
]
|
|
78
|
+
),
|
|
79
|
+
block: @tui.block(
|
|
80
|
+
title: "Simple Rich Text",
|
|
81
|
+
borders: [:all]
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private def complex_example
|
|
87
|
+
# Example 2: Multiple lines with different styles
|
|
88
|
+
@tui.paragraph(
|
|
89
|
+
text: [
|
|
90
|
+
@tui.text_line(
|
|
91
|
+
spans: [
|
|
92
|
+
@tui.text_span(content: "✓ ", style: @tui.style(fg: :green, modifiers: [:bold])),
|
|
93
|
+
@tui.text_span(content: "Feature Complete", style: nil),
|
|
94
|
+
@tui.text_span(content: " - All tests passing", style: @tui.style(fg: :gray)),
|
|
95
|
+
]
|
|
96
|
+
),
|
|
97
|
+
@tui.text_line(
|
|
98
|
+
spans: [
|
|
99
|
+
@tui.text_span(content: "⚠ ", style: @tui.style(fg: :yellow, modifiers: [:bold])),
|
|
100
|
+
@tui.text_span(content: "Warning", style: nil),
|
|
101
|
+
@tui.text_span(content: " - Documentation pending", style: @tui.style(fg: :gray)),
|
|
102
|
+
]
|
|
103
|
+
),
|
|
104
|
+
@tui.text_line(
|
|
105
|
+
spans: [
|
|
106
|
+
@tui.text_span(content: "✗ ", style: @tui.style(fg: :red, modifiers: [:bold])),
|
|
107
|
+
@tui.text_span(content: "Not Started", style: nil),
|
|
108
|
+
@tui.text_span(content: " - Performance benchmarks", style: @tui.style(fg: :gray)),
|
|
109
|
+
]
|
|
110
|
+
),
|
|
111
|
+
@tui.text_line(spans: []),
|
|
112
|
+
@tui.text_line(
|
|
113
|
+
spans: [
|
|
114
|
+
@tui.text_span(content: "Press ", style: nil),
|
|
115
|
+
@tui.text_span(content: "Q", style: @tui.style(modifiers: [:bold])),
|
|
116
|
+
@tui.text_span(content: " to quit", style: nil),
|
|
117
|
+
]
|
|
118
|
+
),
|
|
119
|
+
],
|
|
120
|
+
block: @tui.block(
|
|
121
|
+
title: "Status Report",
|
|
122
|
+
borders: [:all]
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private def handle_input
|
|
128
|
+
event = @tui.poll_event
|
|
129
|
+
return :quit if event == "q" || event == :esc || event == :ctrl_c
|
|
130
|
+
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if __FILE__ == $0
|
|
136
|
+
WidgetRichText.new.run
|
|
137
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
8
|
+
require "ratatui_ruby"
|
|
9
|
+
|
|
10
|
+
# Demo: Scrollable Paragraph
|
|
11
|
+
# Shows how to scroll through long text content using arrow keys
|
|
12
|
+
#
|
|
13
|
+
# Helper: Disable experimental warnings since we use line_count/line_width
|
|
14
|
+
RatatuiRuby.experimental_warnings = false
|
|
15
|
+
|
|
16
|
+
class WidgetScrollText
|
|
17
|
+
def run
|
|
18
|
+
RatatuiRuby.run do |tui|
|
|
19
|
+
@tui = tui
|
|
20
|
+
@scroll_x = 0
|
|
21
|
+
@scroll_y = 0
|
|
22
|
+
|
|
23
|
+
@lines = (1..100).map do |i|
|
|
24
|
+
"Line #{i}: " + ("This is a long line of text that can be scrolled horizontally. " * 3) + "End of line #{i}"
|
|
25
|
+
end
|
|
26
|
+
@hotkey_style = @tui.style(modifiers: [:bold, :underlined])
|
|
27
|
+
|
|
28
|
+
loop do
|
|
29
|
+
draw
|
|
30
|
+
break if handle_input == :quit
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render
|
|
36
|
+
# No-op for compatibility if needed, or alias to draw, but draw now uses @tui
|
|
37
|
+
draw
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def handle_input
|
|
41
|
+
case @tui.poll_event
|
|
42
|
+
in { type: :key, code: "q" } | { type: :key, code: "c", modifiers: ["ctrl"] }
|
|
43
|
+
:quit
|
|
44
|
+
in type: :key, code: "up"
|
|
45
|
+
@scroll_y = [@scroll_y - 1, 0].max
|
|
46
|
+
in type: :key, code: "down"
|
|
47
|
+
@scroll_y = [@scroll_y + 1, @lines.length].min
|
|
48
|
+
in type: :key, code: "left"
|
|
49
|
+
@scroll_x = [@scroll_x - 1, 0].max
|
|
50
|
+
in type: :key, code: "right"
|
|
51
|
+
@scroll_x = [@scroll_x + 1, 100].min
|
|
52
|
+
else
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private def draw
|
|
58
|
+
@tui.draw do |frame|
|
|
59
|
+
layout = @tui.layout_split(
|
|
60
|
+
frame.area,
|
|
61
|
+
direction: :vertical,
|
|
62
|
+
constraints: [
|
|
63
|
+
@tui.constraint_fill(1),
|
|
64
|
+
@tui.constraint_length(5),
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
text = @lines.join("\n")
|
|
69
|
+
|
|
70
|
+
# Main content
|
|
71
|
+
main_paragraph = @tui.paragraph(
|
|
72
|
+
text:,
|
|
73
|
+
scroll: [@scroll_y, @scroll_x],
|
|
74
|
+
block: @tui.block(
|
|
75
|
+
title: "Scrollable Text (#{text.lines.count} lines)",
|
|
76
|
+
borders: [:all]
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
frame.render_widget(main_paragraph, layout[0])
|
|
80
|
+
|
|
81
|
+
# Bottom control panel
|
|
82
|
+
control_text = [
|
|
83
|
+
@tui.text_line(spans: [
|
|
84
|
+
@tui.text_span(content: "NAVIGATION (Size: #{main_paragraph.line_count(65535)}x#{main_paragraph.line_width})", style: @tui.style(modifiers: [:bold])),
|
|
85
|
+
]),
|
|
86
|
+
@tui.text_line(spans: [
|
|
87
|
+
@tui.text_span(content: "↑/↓", style: @hotkey_style),
|
|
88
|
+
@tui.text_span(content: ": Vert Scroll (#{@scroll_y}/#{main_paragraph.line_count(65535)}) "),
|
|
89
|
+
@tui.text_span(content: "←/→", style: @hotkey_style),
|
|
90
|
+
@tui.text_span(content: ": Horz Scroll (#{@scroll_x}/#{main_paragraph.line_width}) "),
|
|
91
|
+
@tui.text_span(content: "q", style: @hotkey_style),
|
|
92
|
+
@tui.text_span(content: ": Quit"),
|
|
93
|
+
]),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
control_paragraph = @tui.paragraph(
|
|
97
|
+
text: control_text,
|
|
98
|
+
block: @tui.block(
|
|
99
|
+
title: "Controls",
|
|
100
|
+
borders: [:all]
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
frame.render_widget(control_paragraph, layout[1])
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
WidgetScrollText.new.run if __FILE__ == $PROGRAM_NAME
|
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
# Demonstrates viewport navigation with interactive theme and orientation cycling.
|
|
10
|
+
#
|
|
11
|
+
# Content overflows. Users get lost in long lists without landmarks. They need to know where they are and how much is left.
|
|
12
|
+
#
|
|
13
|
+
# This demo showcases the <tt>Scrollbar</tt> widget. It provides an interactive playground where you can toggle orientations and cycle through different themes (Standard, Rounded, ASCII, Minimal) in real-time.
|
|
14
|
+
#
|
|
15
|
+
# Use it to understand how to provide spatial awareness and navigation cues for overflowing content.
|
|
16
|
+
#
|
|
17
|
+
# === Example
|
|
18
|
+
#
|
|
19
|
+
# Run the demo from the terminal:
|
|
20
|
+
#
|
|
21
|
+
# ruby examples/widget_scrollbar_demo/app.rb
|
|
22
|
+
#
|
|
23
|
+
# rdoc-image:/doc/images/widget_scrollbar_demo.png
|
|
24
|
+
class WidgetScrollbarDemo
|
|
25
|
+
def initialize
|
|
26
|
+
@scroll_position = 0
|
|
27
|
+
@content_length = 50
|
|
28
|
+
@lines = (1..@content_length).map { |i| "Line #{i}" }
|
|
29
|
+
@orientation_index = 0
|
|
30
|
+
@orientations = [
|
|
31
|
+
:vertical,
|
|
32
|
+
:vertical_right,
|
|
33
|
+
:vertical_left,
|
|
34
|
+
:horizontal,
|
|
35
|
+
:horizontal_bottom,
|
|
36
|
+
:horizontal_top,
|
|
37
|
+
]
|
|
38
|
+
@theme_index = 0
|
|
39
|
+
@themes = [
|
|
40
|
+
{
|
|
41
|
+
name: "Standard",
|
|
42
|
+
track_symbol: nil,
|
|
43
|
+
thumb_symbol: "█",
|
|
44
|
+
track_style: nil,
|
|
45
|
+
thumb_style: nil,
|
|
46
|
+
begin_symbol: nil,
|
|
47
|
+
end_symbol: nil,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "Rounded",
|
|
51
|
+
track_symbol: "│",
|
|
52
|
+
thumb_symbol: "┃",
|
|
53
|
+
track_style: { fg: "dark_gray" },
|
|
54
|
+
thumb_style: { fg: "cyan" },
|
|
55
|
+
begin_symbol: "▲",
|
|
56
|
+
end_symbol: "▼",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "ASCII",
|
|
60
|
+
track_symbol: "|",
|
|
61
|
+
thumb_symbol: "#",
|
|
62
|
+
track_style: { fg: "white" },
|
|
63
|
+
thumb_style: { fg: "red" },
|
|
64
|
+
begin_symbol: "^",
|
|
65
|
+
end_symbol: "v",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "Minimal",
|
|
69
|
+
track_symbol: " ",
|
|
70
|
+
thumb_symbol: "▐",
|
|
71
|
+
track_style: nil,
|
|
72
|
+
thumb_style: { fg: "yellow" },
|
|
73
|
+
begin_symbol: nil,
|
|
74
|
+
end_symbol: nil,
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def run
|
|
80
|
+
RatatuiRuby.run do |tui|
|
|
81
|
+
@tui = tui
|
|
82
|
+
loop do
|
|
83
|
+
draw
|
|
84
|
+
event = @tui.poll_event
|
|
85
|
+
break if event == "q" || event == :ctrl_c
|
|
86
|
+
|
|
87
|
+
handle_event(event)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private def handle_event(event)
|
|
93
|
+
if event.mouse?
|
|
94
|
+
case event.kind
|
|
95
|
+
when "scroll_up"
|
|
96
|
+
@scroll_position = [@scroll_position - 1, 0].max
|
|
97
|
+
when "scroll_down"
|
|
98
|
+
@scroll_position = [@scroll_position + 1, @content_length].min
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if event.key? && event.to_s == "s"
|
|
103
|
+
@theme_index = (@theme_index + 1) % @themes.length
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if event.key? && event.to_s == "o"
|
|
107
|
+
@orientation_index = (@orientation_index + 1) % @orientations.length
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private def draw
|
|
112
|
+
@tui.draw do |frame|
|
|
113
|
+
# Calculate visible lines based on scroll position
|
|
114
|
+
# In a real app, you'd want to know the height of the available area.
|
|
115
|
+
# For this demo, we'll just show all lines but offset the text.
|
|
116
|
+
visible_lines = @lines[@scroll_position..-1] || []
|
|
117
|
+
|
|
118
|
+
# Paragraph with content
|
|
119
|
+
theme = @themes[@theme_index]
|
|
120
|
+
orientation = @orientations[@orientation_index]
|
|
121
|
+
|
|
122
|
+
p = @tui.paragraph(
|
|
123
|
+
text: visible_lines.join("\n"),
|
|
124
|
+
block: @tui.block(
|
|
125
|
+
titles: [
|
|
126
|
+
{ content: "Scroll with Mouse Wheel | Theme: #{theme[:name]} | Orientation: #{orientation}" },
|
|
127
|
+
{ content: "Press 's' to cycle theme, 'o' to cycle orientation", position: :bottom, alignment: :center },
|
|
128
|
+
],
|
|
129
|
+
borders: [:all]
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Scrollbar
|
|
134
|
+
s = @tui.scrollbar(
|
|
135
|
+
content_length: @content_length,
|
|
136
|
+
position: @scroll_position,
|
|
137
|
+
orientation:,
|
|
138
|
+
track_symbol: theme[:track_symbol],
|
|
139
|
+
thumb_symbol: theme[:thumb_symbol],
|
|
140
|
+
track_style: theme[:track_style],
|
|
141
|
+
thumb_style: theme[:thumb_style],
|
|
142
|
+
begin_symbol: theme[:begin_symbol],
|
|
143
|
+
end_symbol: theme[:end_symbol]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Render paragraph first, then scrollbar on top
|
|
147
|
+
frame.render_widget(p, frame.area)
|
|
148
|
+
frame.render_widget(s, frame.area)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
WidgetScrollbarDemo.new.run if __FILE__ == $PROGRAM_NAME
|