ratatui_ruby 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +14 -12
- data/.builds/ruby-3.3.yml +14 -12
- data/.builds/ruby-3.4.yml +14 -12
- data/.builds/ruby-4.0.0.yml +14 -12
- data/AGENTS.md +54 -13
- data/CHANGELOG.md +186 -1
- data/README.md +17 -15
- data/doc/application_architecture.md +116 -0
- data/doc/application_testing.md +12 -7
- data/doc/contributors/better_dx.md +543 -0
- data/doc/contributors/design/ruby_frontend.md +1 -1
- data/doc/contributors/developing_examples.md +203 -0
- data/doc/contributors/documentation_style.md +97 -0
- data/doc/contributors/dwim_dx.md +366 -0
- data/doc/contributors/example_analysis.md +82 -0
- data/doc/custom.css +14 -0
- data/doc/event_handling.md +119 -0
- data/doc/images/all_events.png +0 -0
- data/doc/images/analytics.png +0 -0
- data/doc/images/block_padding.png +0 -0
- data/doc/images/block_titles.png +0 -0
- data/doc/images/box_demo.png +0 -0
- data/doc/images/calendar_demo.png +0 -0
- data/doc/images/cell_demo.png +0 -0
- data/doc/images/chart_demo.png +0 -0
- data/doc/images/custom_widget.png +0 -0
- data/doc/images/flex_layout.png +0 -0
- data/doc/images/gauge_demo.png +0 -0
- data/doc/images/hit_test.png +0 -0
- data/doc/images/line_gauge_demo.png +0 -0
- data/doc/images/list_demo.png +0 -0
- data/doc/images/list_styles.png +0 -0
- data/doc/images/login_form.png +0 -0
- data/doc/images/map_demo.png +0 -0
- data/doc/images/mouse_events.png +0 -0
- data/doc/images/popup_demo.png +0 -0
- data/doc/images/quickstart_dsl.png +0 -0
- data/doc/images/quickstart_lifecycle.png +0 -0
- data/doc/images/ratatui_logo_demo.png +0 -0
- data/doc/images/readme_usage.png +0 -0
- data/doc/images/rich_text.png +0 -0
- data/doc/images/scroll_text.png +0 -0
- data/doc/images/scrollbar_demo.png +0 -0
- data/doc/images/sparkline_demo.png +0 -0
- data/doc/images/table_flex.png +0 -0
- data/doc/images/table_select.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/index.md +1 -0
- data/doc/interactive_design.md +121 -0
- data/doc/quickstart.md +147 -72
- data/examples/all_events/app.rb +169 -0
- data/examples/all_events/app.rbs +7 -0
- data/examples/all_events/test_app.rb +139 -0
- data/examples/analytics/app.rb +258 -0
- data/examples/analytics/app.rbs +7 -0
- data/examples/analytics/test_app.rb +132 -0
- data/examples/block_padding/app.rb +63 -0
- data/examples/block_padding/app.rbs +7 -0
- data/examples/block_padding/test_app.rb +31 -0
- data/examples/block_titles/app.rb +61 -0
- data/examples/block_titles/app.rbs +7 -0
- data/examples/block_titles/test_app.rb +34 -0
- data/examples/box_demo/app.rb +216 -0
- data/examples/box_demo/app.rbs +7 -0
- data/examples/box_demo/test_app.rb +88 -0
- data/examples/calendar_demo/app.rb +101 -0
- data/examples/calendar_demo/app.rbs +7 -0
- data/examples/calendar_demo/test_app.rb +108 -0
- data/examples/cell_demo/app.rb +108 -0
- data/examples/cell_demo/app.rbs +7 -0
- data/examples/cell_demo/test_app.rb +36 -0
- data/examples/chart_demo/app.rb +203 -0
- data/examples/chart_demo/app.rbs +7 -0
- data/examples/chart_demo/test_app.rb +102 -0
- data/examples/custom_widget/app.rb +51 -0
- data/examples/custom_widget/app.rbs +7 -0
- data/examples/custom_widget/test_app.rb +30 -0
- data/examples/flex_layout/app.rb +156 -0
- data/examples/flex_layout/app.rbs +7 -0
- data/examples/flex_layout/test_app.rb +65 -0
- data/examples/gauge_demo/app.rb +182 -0
- data/examples/gauge_demo/app.rbs +7 -0
- data/examples/gauge_demo/test_app.rb +120 -0
- data/examples/hit_test/app.rb +175 -0
- data/examples/hit_test/app.rbs +7 -0
- data/examples/hit_test/test_app.rb +102 -0
- data/examples/line_gauge_demo/app.rb +190 -0
- data/examples/line_gauge_demo/app.rbs +7 -0
- data/examples/line_gauge_demo/test_app.rb +129 -0
- data/examples/list_demo/app.rb +253 -0
- data/examples/list_demo/app.rbs +12 -0
- data/examples/list_demo/test_app.rb +237 -0
- data/examples/list_styles/app.rb +140 -0
- data/examples/list_styles/app.rbs +7 -0
- data/examples/list_styles/test_app.rb +157 -0
- data/examples/{login_form.rb → login_form/app.rb} +12 -16
- data/examples/login_form/app.rbs +7 -0
- data/examples/login_form/test_app.rb +51 -0
- data/examples/map_demo/app.rb +90 -0
- data/examples/map_demo/app.rbs +7 -0
- data/examples/map_demo/test_app.rb +149 -0
- data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
- data/examples/mouse_events/app.rbs +7 -0
- data/examples/mouse_events/test_app.rb +53 -0
- data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
- data/examples/popup_demo/app.rbs +7 -0
- data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
- data/examples/quickstart_dsl/app.rb +36 -0
- data/examples/quickstart_dsl/app.rbs +7 -0
- data/examples/quickstart_dsl/test_app.rb +29 -0
- data/examples/quickstart_lifecycle/app.rb +39 -0
- data/examples/quickstart_lifecycle/app.rbs +7 -0
- data/examples/quickstart_lifecycle/test_app.rb +29 -0
- data/examples/ratatui_logo_demo/app.rb +79 -0
- data/examples/ratatui_logo_demo/app.rbs +7 -0
- data/examples/ratatui_logo_demo/test_app.rb +51 -0
- data/examples/ratatui_mascot_demo/app.rb +84 -0
- data/examples/ratatui_mascot_demo/app.rbs +7 -0
- data/examples/ratatui_mascot_demo/test_app.rb +47 -0
- data/examples/readme_usage/app.rb +29 -0
- data/examples/readme_usage/app.rbs +7 -0
- data/examples/readme_usage/test_app.rb +29 -0
- data/examples/rich_text/app.rb +141 -0
- data/examples/rich_text/app.rbs +7 -0
- data/examples/rich_text/test_app.rb +166 -0
- data/examples/scroll_text/app.rb +103 -0
- data/examples/scroll_text/app.rbs +7 -0
- data/examples/scroll_text/test_app.rb +110 -0
- data/examples/scrollbar_demo/app.rb +143 -0
- data/examples/scrollbar_demo/app.rbs +7 -0
- data/examples/scrollbar_demo/test_app.rb +77 -0
- data/examples/sparkline_demo/app.rb +240 -0
- data/examples/sparkline_demo/app.rbs +10 -0
- data/examples/sparkline_demo/test_app.rb +107 -0
- data/examples/table_flex/app.rb +65 -0
- data/examples/table_flex/app.rbs +7 -0
- data/examples/table_flex/test_app.rb +36 -0
- data/examples/table_select/app.rb +198 -0
- data/examples/table_select/app.rbs +7 -0
- data/examples/table_select/test_app.rb +180 -0
- data/examples/widget_style_colors/app.rb +104 -0
- data/examples/widget_style_colors/app.rbs +14 -0
- data/examples/widget_style_colors/test_app.rb +48 -0
- data/ext/ratatui_ruby/Cargo.lock +889 -115
- data/ext/ratatui_ruby/Cargo.toml +4 -3
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +7 -0
- data/ext/ratatui_ruby/src/events.rs +218 -229
- data/ext/ratatui_ruby/src/lib.rs +38 -10
- data/ext/ratatui_ruby/src/rendering.rs +90 -10
- data/ext/ratatui_ruby/src/style.rs +281 -98
- data/ext/ratatui_ruby/src/terminal.rs +119 -25
- data/ext/ratatui_ruby/src/text.rs +171 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
- data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
- data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
- data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
- data/ext/ratatui_ruby/src/widgets/chart.rs +69 -27
- data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
- data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
- data/ext/ratatui_ruby/src/widgets/layout.rs +218 -15
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
- data/ext/ratatui_ruby/src/widgets/table.rs +173 -64
- data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
- data/lib/ratatui_ruby/cell.rb +166 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
- data/lib/ratatui_ruby/event/key.rb +211 -0
- data/lib/ratatui_ruby/event/mouse.rb +124 -0
- data/lib/ratatui_ruby/event/paste.rb +71 -0
- data/lib/ratatui_ruby/event/resize.rb +80 -0
- data/lib/ratatui_ruby/event.rb +79 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
- data/lib/ratatui_ruby/schema/block.rb +186 -14
- data/lib/ratatui_ruby/schema/calendar.rb +74 -17
- data/lib/ratatui_ruby/schema/canvas.rb +215 -48
- data/lib/ratatui_ruby/schema/center.rb +49 -11
- data/lib/ratatui_ruby/schema/chart.rb +151 -41
- data/lib/ratatui_ruby/schema/clear.rb +41 -72
- data/lib/ratatui_ruby/schema/constraint.rb +82 -22
- data/lib/ratatui_ruby/schema/cursor.rb +27 -9
- data/lib/ratatui_ruby/schema/draw.rb +53 -0
- data/lib/ratatui_ruby/schema/gauge.rb +59 -15
- data/lib/ratatui_ruby/schema/layout.rb +95 -13
- data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
- data/lib/ratatui_ruby/schema/list.rb +93 -19
- data/lib/ratatui_ruby/schema/overlay.rb +34 -8
- data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
- data/lib/ratatui_ruby/schema/rect.rb +64 -15
- data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
- data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
- data/lib/ratatui_ruby/schema/style.rb +49 -21
- data/lib/ratatui_ruby/schema/table.rb +119 -21
- data/lib/ratatui_ruby/schema/tabs.rb +75 -13
- data/lib/ratatui_ruby/schema/text.rb +90 -0
- data/lib/ratatui_ruby/session.rb +146 -0
- data/lib/ratatui_ruby/test_helper.rb +156 -13
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +143 -23
- data/sig/ratatui_ruby/event.rbs +69 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
- data/sig/ratatui_ruby/schema/block.rbs +5 -4
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
- data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
- data/sig/ratatui_ruby/schema/center.rbs +3 -3
- data/sig/ratatui_ruby/schema/chart.rbs +8 -5
- data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
- data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
- data/sig/ratatui_ruby/schema/draw.rbs +23 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
- data/sig/ratatui_ruby/schema/layout.rbs +11 -1
- data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
- data/sig/ratatui_ruby/schema/list.rbs +5 -1
- data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
- data/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
- data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
- data/sig/ratatui_ruby/schema/rect.rbs +2 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
- data/sig/ratatui_ruby/schema/table.rbs +8 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
- data/sig/ratatui_ruby/schema/text.rbs +22 -0
- data/tasks/resources/build.yml.erb +13 -11
- data/tasks/terminal_preview/app_screenshot.rb +35 -0
- data/tasks/terminal_preview/crash_report.rb +54 -0
- data/tasks/terminal_preview/example_app.rb +25 -0
- data/tasks/terminal_preview/launcher_script.rb +48 -0
- data/tasks/terminal_preview/preview_collection.rb +60 -0
- data/tasks/terminal_preview/preview_timing.rb +22 -0
- data/tasks/terminal_preview/safety_confirmation.rb +58 -0
- data/tasks/terminal_preview/saved_screenshot.rb +55 -0
- data/tasks/terminal_preview/system_appearance.rb +11 -0
- data/tasks/terminal_preview/terminal_window.rb +138 -0
- data/tasks/terminal_preview/window_id.rb +14 -0
- data/tasks/terminal_preview.rake +28 -0
- data/tasks/test.rake +1 -1
- metadata +174 -53
- data/doc/images/examples-analytics.rb.png +0 -0
- data/doc/images/examples-box_demo.rb.png +0 -0
- data/doc/images/examples-calendar_demo.rb.png +0 -0
- data/doc/images/examples-chart_demo.rb.png +0 -0
- data/doc/images/examples-custom_widget.rb.png +0 -0
- data/doc/images/examples-dashboard.rb.png +0 -0
- data/doc/images/examples-list_styles.rb.png +0 -0
- data/doc/images/examples-login_form.rb.png +0 -0
- data/doc/images/examples-map_demo.rb.png +0 -0
- data/doc/images/examples-mouse_events.rb.png +0 -0
- data/doc/images/examples-popup_demo.rb.gif +0 -0
- data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
- data/doc/images/examples-scroll_text.rb.png +0 -0
- data/doc/images/examples-scrollbar_demo.rb.png +0 -0
- data/doc/images/examples-stock_ticker.rb.png +0 -0
- data/doc/images/examples-system_monitor.rb.png +0 -0
- data/doc/images/examples-table_select.rb.png +0 -0
- data/examples/analytics.rb +0 -88
- data/examples/box_demo.rb +0 -71
- data/examples/calendar_demo.rb +0 -55
- data/examples/chart_demo.rb +0 -84
- data/examples/custom_widget.rb +0 -43
- data/examples/dashboard.rb +0 -72
- data/examples/list_styles.rb +0 -66
- data/examples/map_demo.rb +0 -58
- data/examples/quickstart_dsl.rb +0 -30
- data/examples/quickstart_lifecycle.rb +0 -40
- data/examples/readme_usage.rb +0 -21
- data/examples/scroll_text.rb +0 -74
- data/examples/scrollbar_demo.rb +0 -75
- data/examples/stock_ticker.rb +0 -93
- data/examples/system_monitor.rb +0 -94
- data/examples/table_select.rb +0 -70
- data/examples/test_analytics.rb +0 -65
- data/examples/test_box_demo.rb +0 -38
- data/examples/test_calendar_demo.rb +0 -66
- data/examples/test_dashboard.rb +0 -38
- data/examples/test_list_styles.rb +0 -61
- data/examples/test_login_form.rb +0 -63
- data/examples/test_map_demo.rb +0 -100
- data/examples/test_scroll_text.rb +0 -130
- data/examples/test_stock_ticker.rb +0 -39
- data/examples/test_system_monitor.rb +0 -40
- data/examples/test_table_select.rb +0 -37
- data/ext/ratatui_ruby/src/buffer.rs +0 -54
- data/lib/ratatui_ruby/dsl.rb +0 -64
|
@@ -2,38 +2,118 @@
|
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
use crate::style::parse_block;
|
|
5
|
+
use bumpalo::Bump;
|
|
5
6
|
use magnus::{prelude::*, Error, Value};
|
|
6
7
|
use ratatui::{layout::Rect, text::Line, widgets::Tabs, Frame};
|
|
7
8
|
|
|
8
9
|
pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
10
|
+
let bump = Bump::new();
|
|
11
|
+
let tabs = create_tabs(node, &bump)?;
|
|
12
|
+
frame.render_widget(tabs, area);
|
|
13
|
+
Ok(())
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
use crate::text::parse_line;
|
|
17
|
+
|
|
18
|
+
fn create_tabs<'a>(node: Value, bump: &'a Bump) -> Result<Tabs<'a>, Error> {
|
|
9
19
|
let ruby = magnus::Ruby::get().unwrap();
|
|
10
20
|
let titles_val: Value = node.funcall("titles", ())?;
|
|
11
21
|
let selected_index: usize = node.funcall("selected_index", ())?;
|
|
12
22
|
let block_val: Value = node.funcall("block", ())?;
|
|
23
|
+
let divider_val: Value = node.funcall("divider", ())?;
|
|
24
|
+
let highlight_style_val: Value = node.funcall("highlight_style", ())?;
|
|
25
|
+
let padding_left: usize = node.funcall("padding_left", ())?;
|
|
26
|
+
let padding_right: usize = node.funcall("padding_right", ())?;
|
|
13
27
|
|
|
14
28
|
let titles_array = magnus::RArray::from_value(titles_val)
|
|
15
29
|
.ok_or_else(|| Error::new(ruby.exception_type_error(), "expected array for titles"))?;
|
|
16
30
|
|
|
17
31
|
let mut titles = Vec::new();
|
|
18
32
|
for i in 0..titles_array.len() {
|
|
19
|
-
let
|
|
20
|
-
|
|
33
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
34
|
+
let val: Value = titles_array.entry(index)?;
|
|
35
|
+
if let Ok(line) = parse_line(val) {
|
|
36
|
+
titles.push(line);
|
|
37
|
+
} else {
|
|
38
|
+
let s: String = String::try_convert(val)?;
|
|
39
|
+
titles.push(Line::from(s));
|
|
40
|
+
}
|
|
21
41
|
}
|
|
22
42
|
|
|
23
43
|
let mut tabs = Tabs::new(titles).select(selected_index);
|
|
24
44
|
|
|
45
|
+
if !divider_val.is_nil() {
|
|
46
|
+
let divider: String = divider_val.funcall("to_s", ())?;
|
|
47
|
+
tabs = tabs.divider(divider);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if !highlight_style_val.is_nil() {
|
|
51
|
+
let style = crate::style::parse_style(highlight_style_val)?;
|
|
52
|
+
tabs = tabs.highlight_style(style);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let style_val: Value = node.funcall("style", ())?;
|
|
56
|
+
if !style_val.is_nil() {
|
|
57
|
+
tabs = tabs.style(crate::style::parse_style(style_val)?);
|
|
58
|
+
}
|
|
59
|
+
|
|
25
60
|
if !block_val.is_nil() {
|
|
26
|
-
tabs = tabs.block(parse_block(block_val)?);
|
|
61
|
+
tabs = tabs.block(parse_block(block_val, bump)?);
|
|
27
62
|
}
|
|
28
63
|
|
|
29
|
-
|
|
30
|
-
|
|
64
|
+
if padding_left > 0 || padding_right > 0 {
|
|
65
|
+
let left_str = " ".repeat(padding_left);
|
|
66
|
+
let right_str = " ".repeat(padding_right);
|
|
67
|
+
tabs = tabs.padding(left_str, right_str);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Ok(tabs)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
pub fn width(node: Value) -> Result<usize, Error> {
|
|
74
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
75
|
+
let titles_val: Value = node.funcall("titles", ())?;
|
|
76
|
+
let divider_val: Value = node.funcall("divider", ())?;
|
|
77
|
+
let padding_left: usize = node.funcall("padding_left", ())?;
|
|
78
|
+
let padding_right: usize = node.funcall("padding_right", ())?;
|
|
79
|
+
|
|
80
|
+
let titles_array = magnus::RArray::from_value(titles_val)
|
|
81
|
+
.ok_or_else(|| Error::new(ruby.exception_type_error(), "expected array for titles"))?;
|
|
82
|
+
|
|
83
|
+
let mut total_width = padding_left + padding_right;
|
|
84
|
+
|
|
85
|
+
let mut titles_count = 0;
|
|
86
|
+
for i in 0..titles_array.len() {
|
|
87
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
88
|
+
let val: Value = titles_array.entry(index)?;
|
|
89
|
+
let line_width = if let Ok(line) = parse_line(val) {
|
|
90
|
+
line.width()
|
|
91
|
+
} else {
|
|
92
|
+
let s: String = String::try_convert(val)?;
|
|
93
|
+
ratatui::text::Span::raw(s).width()
|
|
94
|
+
};
|
|
95
|
+
total_width += line_width;
|
|
96
|
+
titles_count += 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if titles_count > 1 {
|
|
100
|
+
let divider_width = if !divider_val.is_nil() {
|
|
101
|
+
let d: String = divider_val.funcall("to_s", ())?;
|
|
102
|
+
ratatui::text::Span::raw(d).width()
|
|
103
|
+
} else {
|
|
104
|
+
1 // Default divider is "|"
|
|
105
|
+
};
|
|
106
|
+
total_width += (titles_count - 1) * divider_width;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Ok(total_width)
|
|
31
110
|
}
|
|
32
111
|
|
|
33
112
|
#[cfg(test)]
|
|
34
113
|
mod tests {
|
|
35
114
|
use super::*;
|
|
36
115
|
use ratatui::buffer::Buffer;
|
|
116
|
+
use ratatui::style::{Color, Modifier, Style};
|
|
37
117
|
use ratatui::text::Line;
|
|
38
118
|
use ratatui::widgets::{Tabs, Widget};
|
|
39
119
|
|
|
@@ -49,4 +129,24 @@ mod tests {
|
|
|
49
129
|
assert!(content.contains("Tab2"));
|
|
50
130
|
assert!(content.contains('|'));
|
|
51
131
|
}
|
|
132
|
+
|
|
133
|
+
#[test]
|
|
134
|
+
fn test_tabs_highlight_style() {
|
|
135
|
+
let titles = vec![Line::from("Tab1"), Line::from("Tab2")];
|
|
136
|
+
let highlight_style = Style::default().fg(Color::Red).add_modifier(Modifier::BOLD);
|
|
137
|
+
let tabs = Tabs::new(titles)
|
|
138
|
+
.select(0)
|
|
139
|
+
.highlight_style(highlight_style);
|
|
140
|
+
|
|
141
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 15, 1));
|
|
142
|
+
tabs.render(Rect::new(0, 0, 15, 1), &mut buf);
|
|
143
|
+
|
|
144
|
+
// Check the first cell of the first tab (which is selected)
|
|
145
|
+
// " Tab1 "
|
|
146
|
+
// Index 1 should be 'T' with Red+Bold
|
|
147
|
+
let cell = &buf.content()[1];
|
|
148
|
+
assert_eq!(cell.symbol(), "T");
|
|
149
|
+
assert_eq!(cell.fg, Color::Red);
|
|
150
|
+
assert!(cell.modifier.contains(Modifier::BOLD));
|
|
151
|
+
}
|
|
52
152
|
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
# Represents a single cell in the terminal buffer.
|
|
8
|
+
#
|
|
9
|
+
# A terminal grid is made of cells. Each cell contains a character (symbol) and styling (colors, modifiers).
|
|
10
|
+
# When testing, you often need to verify that a specific cell renders correctly.
|
|
11
|
+
#
|
|
12
|
+
# This object encapsulates that state. It provides predicate methods for modifiers, making assertions readable.
|
|
13
|
+
#
|
|
14
|
+
# Use it to inspect the visual state of your application in tests.
|
|
15
|
+
#
|
|
16
|
+
# === Examples
|
|
17
|
+
#
|
|
18
|
+
# cell = RatatuiRuby.get_cell_at(0, 0)
|
|
19
|
+
# cell.char # => "H"
|
|
20
|
+
# cell.fg # => :red
|
|
21
|
+
# cell.bold? # => true
|
|
22
|
+
#
|
|
23
|
+
class Cell
|
|
24
|
+
# The character displayed in the cell.
|
|
25
|
+
#
|
|
26
|
+
# Named to match Ratatui's Cell::symbol() method.
|
|
27
|
+
attr_reader :symbol
|
|
28
|
+
|
|
29
|
+
# Alias for Rubyists who prefer a shorter name.
|
|
30
|
+
alias char symbol
|
|
31
|
+
|
|
32
|
+
# The foreground color of the cell (e.g., :red, :blue, "#ff0000").
|
|
33
|
+
attr_reader :fg
|
|
34
|
+
|
|
35
|
+
# The background color of the cell (e.g., :black, nil).
|
|
36
|
+
attr_reader :bg
|
|
37
|
+
|
|
38
|
+
# The list of active modifiers (e.g., ["bold", "italic"]).
|
|
39
|
+
attr_reader :modifiers
|
|
40
|
+
|
|
41
|
+
# Returns an empty cell (space character, no styles).
|
|
42
|
+
#
|
|
43
|
+
# === Example
|
|
44
|
+
#
|
|
45
|
+
# Cell.empty # => #<RatatuiRuby::Cell char=" ">
|
|
46
|
+
#
|
|
47
|
+
def self.empty
|
|
48
|
+
new(symbol: " ", fg: nil, bg: nil, modifiers: [])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns a default cell (alias for empty).
|
|
52
|
+
#
|
|
53
|
+
# === Example
|
|
54
|
+
#
|
|
55
|
+
# Cell.default # => #<RatatuiRuby::Cell char=" ">
|
|
56
|
+
#
|
|
57
|
+
def self.default
|
|
58
|
+
empty
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns a cell with a specific character and no styles.
|
|
62
|
+
#
|
|
63
|
+
# [symbol] String (single character).
|
|
64
|
+
#
|
|
65
|
+
# === Example
|
|
66
|
+
#
|
|
67
|
+
# Cell.symbol("X") # => #<RatatuiRuby::Cell symbol="X">
|
|
68
|
+
#
|
|
69
|
+
def self.symbol(symbol)
|
|
70
|
+
new(symbol: symbol, fg: nil, bg: nil, modifiers: [])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Alias for Rubyists who prefer a shorter name.
|
|
74
|
+
def self.char(char)
|
|
75
|
+
symbol(char)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Creates a new Cell.
|
|
79
|
+
#
|
|
80
|
+
# [symbol] String (single character). Aliased as <tt>char:</tt>.
|
|
81
|
+
# [fg] Symbol or String (nullable).
|
|
82
|
+
# [bg] Symbol or String (nullable).
|
|
83
|
+
# [modifiers] Array of Strings.
|
|
84
|
+
def initialize(symbol: nil, char: nil, fg: nil, bg: nil, modifiers: [])
|
|
85
|
+
@symbol = symbol || char || " "
|
|
86
|
+
@fg = fg
|
|
87
|
+
@bg = bg
|
|
88
|
+
@modifiers = modifiers.freeze
|
|
89
|
+
freeze
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns true if the cell has the bold modifier.
|
|
93
|
+
def bold?
|
|
94
|
+
modifiers.include?("bold")
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Returns true if the cell has the dim modifier.
|
|
98
|
+
def dim?
|
|
99
|
+
modifiers.include?("dim")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Returns true if the cell has the italic modifier.
|
|
103
|
+
def italic?
|
|
104
|
+
modifiers.include?("italic")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Returns true if the cell has the underlined modifier.
|
|
108
|
+
def underlined?
|
|
109
|
+
modifiers.include?("underlined")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns true if the cell has the slow_blink modifier.
|
|
113
|
+
def slow_blink?
|
|
114
|
+
modifiers.include?("slow_blink")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns true if the cell has the rapid_blink modifier.
|
|
118
|
+
def rapid_blink?
|
|
119
|
+
modifiers.include?("rapid_blink")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns true if the cell has the reversed modifier.
|
|
123
|
+
def reversed?
|
|
124
|
+
modifiers.include?("reversed")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Returns true if the cell has the hidden modifier.
|
|
128
|
+
def hidden?
|
|
129
|
+
modifiers.include?("hidden")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Returns true if the cell has the crossed_out modifier.
|
|
133
|
+
def crossed_out?
|
|
134
|
+
modifiers.include?("crossed_out")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Checks equality with another Cell.
|
|
138
|
+
def ==(other)
|
|
139
|
+
other.is_a?(Cell) &&
|
|
140
|
+
char == other.char &&
|
|
141
|
+
fg == other.fg &&
|
|
142
|
+
bg == other.bg &&
|
|
143
|
+
modifiers == other.modifiers
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns a string representation of the cell.
|
|
147
|
+
def inspect
|
|
148
|
+
parts = ["symbol=#{symbol.inspect}"]
|
|
149
|
+
parts << "fg=#{fg.inspect}" if fg
|
|
150
|
+
parts << "bg=#{bg.inspect}" if bg
|
|
151
|
+
parts << "modifiers=#{modifiers.inspect}" unless modifiers.empty?
|
|
152
|
+
"#<#{self.class} #{parts.join(" ")}>"
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Returns the cell's character.
|
|
156
|
+
def to_s
|
|
157
|
+
symbol
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Support for pattern matching.
|
|
161
|
+
# Supports both <tt>:symbol</tt> and <tt>:char</tt> keys.
|
|
162
|
+
def deconstruct_keys(keys)
|
|
163
|
+
{ symbol: symbol, char: symbol, fg: fg, bg: bg, modifiers: modifiers }
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# Signals that the application is now active.
|
|
9
|
+
#
|
|
10
|
+
# The user interacts with many windows. Your application needs to know when it has their attention.
|
|
11
|
+
#
|
|
12
|
+
# This event confirms visibility. It fires when the terminal window moves to the foreground.
|
|
13
|
+
#
|
|
14
|
+
# Use it to resume paused activities. Restart animations. Refresh data. The user is watching.
|
|
15
|
+
#
|
|
16
|
+
# Only supported by some terminals (e.g. iTerm2, Kitty, newer xterm).
|
|
17
|
+
#
|
|
18
|
+
# === Example
|
|
19
|
+
#
|
|
20
|
+
# if event.focus_gained?
|
|
21
|
+
# puts "Focus gained"
|
|
22
|
+
# end
|
|
23
|
+
class FocusGained < Event
|
|
24
|
+
# Returns true for FocusGained events.
|
|
25
|
+
#
|
|
26
|
+
# event.focus_gained? # => true
|
|
27
|
+
# event.key? # => false
|
|
28
|
+
def focus_gained?
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Deconstructs the event for pattern matching.
|
|
33
|
+
#
|
|
34
|
+
# case event
|
|
35
|
+
# in type: :focus_gained
|
|
36
|
+
# puts "Application gained focus"
|
|
37
|
+
# end
|
|
38
|
+
def deconstruct_keys(keys)
|
|
39
|
+
{ type: :focus_gained }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Compares this event with another for equality.
|
|
44
|
+
def ==(other)
|
|
45
|
+
other.is_a?(FocusGained)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# Signals that the application is in the background.
|
|
9
|
+
#
|
|
10
|
+
# The user has switched context. Your application is no longer the primary focus.
|
|
11
|
+
#
|
|
12
|
+
# This event warns of inactivity. It fires when the terminal window loses focus.
|
|
13
|
+
#
|
|
14
|
+
# Respond by conserving resources. Pause animations. Stop heavy polling. dim the UI to
|
|
15
|
+
# indicate a background state.
|
|
16
|
+
#
|
|
17
|
+
# Only supported by some terminals (e.g. iTerm2, Kitty, newer xterm).
|
|
18
|
+
#
|
|
19
|
+
# === Example
|
|
20
|
+
#
|
|
21
|
+
# if event.focus_lost?
|
|
22
|
+
# puts "Focus lost"
|
|
23
|
+
# end
|
|
24
|
+
class FocusLost < Event
|
|
25
|
+
# Returns true for FocusLost events.
|
|
26
|
+
#
|
|
27
|
+
# event.focus_lost? # => true
|
|
28
|
+
# event.key? # => false
|
|
29
|
+
def focus_lost?
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Deconstructs the event for pattern matching.
|
|
34
|
+
#
|
|
35
|
+
# case event
|
|
36
|
+
# in type: :focus_lost
|
|
37
|
+
# puts "Application lost focus"
|
|
38
|
+
# end
|
|
39
|
+
def deconstruct_keys(keys)
|
|
40
|
+
{ type: :focus_lost }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Compares this event with another for equality.
|
|
45
|
+
def ==(other)
|
|
46
|
+
other.is_a?(FocusLost)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
module RatatuiRuby
|
|
7
|
+
class Event
|
|
8
|
+
# Captures a keyboard interaction.
|
|
9
|
+
#
|
|
10
|
+
# The keyboard is the primary interface for your terminal application. Raw key codes are often cryptic,
|
|
11
|
+
# and handling modifiers manually is error-prone.
|
|
12
|
+
#
|
|
13
|
+
# This event creates clarity. It encapsulates the interaction, providing a normalized +code+ and
|
|
14
|
+
# a list of active +modifiers+.
|
|
15
|
+
#
|
|
16
|
+
# Compare it directly to strings or symbols for rapid development, or use pattern matching for
|
|
17
|
+
# complex control schemes.
|
|
18
|
+
#
|
|
19
|
+
# === Examples
|
|
20
|
+
#
|
|
21
|
+
# Using predicates:
|
|
22
|
+
# if event.key? && event.ctrl? && event.code == "c"
|
|
23
|
+
# exit
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# Using symbol comparison:
|
|
27
|
+
# if event == :ctrl_c
|
|
28
|
+
# exit
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# Using pattern matching:
|
|
32
|
+
# case event
|
|
33
|
+
# in type: :key, code: "c", modifiers: ["ctrl"]
|
|
34
|
+
# exit
|
|
35
|
+
# end
|
|
36
|
+
class Key < Event
|
|
37
|
+
# The key code (e.g., <tt>"a"</tt>, <tt>"enter"</tt>, <tt>"up"</tt>).
|
|
38
|
+
#
|
|
39
|
+
# puts event.code # => "enter"
|
|
40
|
+
attr_reader :code
|
|
41
|
+
|
|
42
|
+
# List of active modifiers (<tt>"ctrl"</tt>, <tt>"alt"</tt>, <tt>"shift"</tt>).
|
|
43
|
+
#
|
|
44
|
+
# puts event.modifiers # => ["ctrl", "shift"]
|
|
45
|
+
attr_reader :modifiers
|
|
46
|
+
|
|
47
|
+
# Returns true for Key events.
|
|
48
|
+
#
|
|
49
|
+
# event.key? # => true
|
|
50
|
+
# event.mouse? # => false
|
|
51
|
+
# event.resize? # => false
|
|
52
|
+
def key?
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Creates a new Key event.
|
|
57
|
+
#
|
|
58
|
+
# [code]
|
|
59
|
+
# The key code (String).
|
|
60
|
+
# [modifiers]
|
|
61
|
+
# List of modifiers (Array<String>).
|
|
62
|
+
def initialize(code:, modifiers: [])
|
|
63
|
+
@code = code
|
|
64
|
+
@modifiers = modifiers.sort
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Compares the event with another object.
|
|
68
|
+
#
|
|
69
|
+
# - If +other+ is a +Symbol+, compares against #to_sym.
|
|
70
|
+
# - If +other+ is a +String+, compares against #to_s.
|
|
71
|
+
# - Otherwise, performs standard equality check.
|
|
72
|
+
# - Otherwise, compares internal state (code + modifiers).
|
|
73
|
+
def ==(other)
|
|
74
|
+
case other
|
|
75
|
+
when Symbol
|
|
76
|
+
to_sym == other
|
|
77
|
+
when String
|
|
78
|
+
to_s == other
|
|
79
|
+
when Key
|
|
80
|
+
code == other.code && modifiers == other.modifiers
|
|
81
|
+
else
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Converts the event to a Symbol representation.
|
|
87
|
+
#
|
|
88
|
+
# The format is <tt>[modifiers_]code</tt>. Modifiers are sorted alphabetically (alt, ctrl, shift)
|
|
89
|
+
# and joined by underscores.
|
|
90
|
+
#
|
|
91
|
+
# === Supported Keys
|
|
92
|
+
#
|
|
93
|
+
# [Standard]
|
|
94
|
+
# <tt>:enter</tt>, <tt>:backspace</tt>, <tt>:tab</tt>, <tt>:esc</tt>, <tt>:page_up</tt>, <tt>:page_down</tt>, <tt>:home</tt>, <tt>:end</tt>, <tt>:delete</tt>, <tt>:insert</tt>, <tt>:f1</tt>..<tt>:f12</tt>
|
|
95
|
+
# [Navigation]
|
|
96
|
+
# <tt>:up</tt>, <tt>:down</tt>, <tt>:left</tt>, <tt>:right</tt>
|
|
97
|
+
# [Characters]
|
|
98
|
+
# <tt>:a</tt>, <tt>:b</tt>, <tt>:1</tt>, <tt>:space</tt>, etc.
|
|
99
|
+
#
|
|
100
|
+
# === Modifier Examples
|
|
101
|
+
#
|
|
102
|
+
# * <tt>:ctrl_c</tt>
|
|
103
|
+
# * <tt>:alt_enter</tt>
|
|
104
|
+
# * <tt>:shift_left</tt>
|
|
105
|
+
# * <tt>:ctrl_alt_delete</tt>
|
|
106
|
+
def to_sym
|
|
107
|
+
mods = @modifiers.join("_")
|
|
108
|
+
if mods.empty?
|
|
109
|
+
@code.to_sym
|
|
110
|
+
else
|
|
111
|
+
"#{mods}_#{@code}".to_sym
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Converts the event to its String representation.
|
|
116
|
+
#
|
|
117
|
+
# [Printable Characters]
|
|
118
|
+
# Returns the character itself (e.g., <tt>"a"</tt>, <tt>"1"</tt>, <tt>" "</tt>).
|
|
119
|
+
# [Special Keys]
|
|
120
|
+
# Returns an empty string (e.g., <tt>"enter"</tt>, <tt>"up"</tt>, <tt>"f1"</tt> all return <tt>""</tt>).
|
|
121
|
+
# [Modifiers]
|
|
122
|
+
# Returns the character if printable, ignoring modifiers unless they alter the character code itself.
|
|
123
|
+
# Note that <tt>ctrl+c</tt> typically returns <tt>"c"</tt> as the code, so +to_s+ will return <tt>"c"</tt>.
|
|
124
|
+
def to_s
|
|
125
|
+
if text?
|
|
126
|
+
@code
|
|
127
|
+
else
|
|
128
|
+
""
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Returns inspection string.
|
|
133
|
+
def inspect
|
|
134
|
+
"#<#{self.class} code=#{@code.inspect} modifiers=#{@modifiers.inspect}>"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Returns true if CTRL is held.
|
|
138
|
+
def ctrl?
|
|
139
|
+
@modifiers.include?("ctrl")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Returns true if ALT is held.
|
|
143
|
+
def alt?
|
|
144
|
+
@modifiers.include?("alt")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Returns true if SHIFT is held.
|
|
148
|
+
def shift?
|
|
149
|
+
@modifiers.include?("shift")
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Returns true if the key represents a single printable character.
|
|
153
|
+
#
|
|
154
|
+
# RatatuiRuby::Event::Key.new(code: "a").text? # => true
|
|
155
|
+
# RatatuiRuby::Event::Key.new(code: "enter").text? # => false
|
|
156
|
+
# RatatuiRuby::Event::Key.new(code: "space").text? # => false ("space" is not 1 char, " " is)
|
|
157
|
+
def text?
|
|
158
|
+
@code.length == 1
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Returns the key as a printable character (if applicable).
|
|
162
|
+
#
|
|
163
|
+
# [Printable Characters]
|
|
164
|
+
# Returns the character itself (e.g., <tt>"a"</tt>, <tt>"1"</tt>, <tt>" "</tt>).
|
|
165
|
+
# [Special Keys]
|
|
166
|
+
# Returns an empty string (e.g., <tt>"enter"</tt>, <tt>"up"</tt>, <tt>"f1"</tt>).
|
|
167
|
+
#
|
|
168
|
+
# This is equivalent to +to_s+.
|
|
169
|
+
#
|
|
170
|
+
# RatatuiRuby::Event::Key.new(code: "a").char # => "a"
|
|
171
|
+
# RatatuiRuby::Event::Key.new(code: "enter").char # => ""
|
|
172
|
+
def char
|
|
173
|
+
to_s
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Supports dynamic key predicate methods via method_missing.
|
|
177
|
+
#
|
|
178
|
+
# Allows convenient checking for specific keys or key combinations:
|
|
179
|
+
#
|
|
180
|
+
# event.ctrl_c? # => true if Ctrl+C
|
|
181
|
+
# event.enter? # => true if Enter
|
|
182
|
+
# event.shift_up? # => true if Shift+Up
|
|
183
|
+
# event.q? # => true if "q"
|
|
184
|
+
#
|
|
185
|
+
# The method name is converted to a symbol and compared against the event.
|
|
186
|
+
# This works for any key code or modifier+key combination.
|
|
187
|
+
def method_missing(name, *args, &block)
|
|
188
|
+
if name.to_s.end_with?("?")
|
|
189
|
+
key_sym = name.to_s[0...-1].to_sym
|
|
190
|
+
return self == key_sym
|
|
191
|
+
end
|
|
192
|
+
super
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Declares that this class responds to dynamic predicate methods.
|
|
196
|
+
def respond_to_missing?(name, *args)
|
|
197
|
+
name.to_s.end_with?("?") || super
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Deconstructs the event for pattern matching.
|
|
201
|
+
#
|
|
202
|
+
# case event
|
|
203
|
+
# in type: :key, code: "c", modifiers: ["ctrl"]
|
|
204
|
+
# puts "Ctrl+C pressed"
|
|
205
|
+
# end
|
|
206
|
+
def deconstruct_keys(keys)
|
|
207
|
+
{ type: :key, code: @code, modifiers: @modifiers }
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|