ratatui_ruby 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +14 -12
- data/.builds/ruby-3.3.yml +14 -12
- data/.builds/ruby-3.4.yml +14 -12
- data/.builds/ruby-4.0.0.yml +14 -12
- data/AGENTS.md +54 -13
- data/CHANGELOG.md +186 -1
- data/README.md +17 -15
- data/doc/application_architecture.md +116 -0
- data/doc/application_testing.md +12 -7
- data/doc/contributors/better_dx.md +543 -0
- data/doc/contributors/design/ruby_frontend.md +1 -1
- data/doc/contributors/developing_examples.md +203 -0
- data/doc/contributors/documentation_style.md +97 -0
- data/doc/contributors/dwim_dx.md +366 -0
- data/doc/contributors/example_analysis.md +82 -0
- data/doc/custom.css +14 -0
- data/doc/event_handling.md +119 -0
- data/doc/images/all_events.png +0 -0
- data/doc/images/analytics.png +0 -0
- data/doc/images/block_padding.png +0 -0
- data/doc/images/block_titles.png +0 -0
- data/doc/images/box_demo.png +0 -0
- data/doc/images/calendar_demo.png +0 -0
- data/doc/images/cell_demo.png +0 -0
- data/doc/images/chart_demo.png +0 -0
- data/doc/images/custom_widget.png +0 -0
- data/doc/images/flex_layout.png +0 -0
- data/doc/images/gauge_demo.png +0 -0
- data/doc/images/hit_test.png +0 -0
- data/doc/images/line_gauge_demo.png +0 -0
- data/doc/images/list_demo.png +0 -0
- data/doc/images/list_styles.png +0 -0
- data/doc/images/login_form.png +0 -0
- data/doc/images/map_demo.png +0 -0
- data/doc/images/mouse_events.png +0 -0
- data/doc/images/popup_demo.png +0 -0
- data/doc/images/quickstart_dsl.png +0 -0
- data/doc/images/quickstart_lifecycle.png +0 -0
- data/doc/images/ratatui_logo_demo.png +0 -0
- data/doc/images/readme_usage.png +0 -0
- data/doc/images/rich_text.png +0 -0
- data/doc/images/scroll_text.png +0 -0
- data/doc/images/scrollbar_demo.png +0 -0
- data/doc/images/sparkline_demo.png +0 -0
- data/doc/images/table_flex.png +0 -0
- data/doc/images/table_select.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/index.md +1 -0
- data/doc/interactive_design.md +121 -0
- data/doc/quickstart.md +147 -72
- data/examples/all_events/app.rb +169 -0
- data/examples/all_events/app.rbs +7 -0
- data/examples/all_events/test_app.rb +139 -0
- data/examples/analytics/app.rb +258 -0
- data/examples/analytics/app.rbs +7 -0
- data/examples/analytics/test_app.rb +132 -0
- data/examples/block_padding/app.rb +63 -0
- data/examples/block_padding/app.rbs +7 -0
- data/examples/block_padding/test_app.rb +31 -0
- data/examples/block_titles/app.rb +61 -0
- data/examples/block_titles/app.rbs +7 -0
- data/examples/block_titles/test_app.rb +34 -0
- data/examples/box_demo/app.rb +216 -0
- data/examples/box_demo/app.rbs +7 -0
- data/examples/box_demo/test_app.rb +88 -0
- data/examples/calendar_demo/app.rb +101 -0
- data/examples/calendar_demo/app.rbs +7 -0
- data/examples/calendar_demo/test_app.rb +108 -0
- data/examples/cell_demo/app.rb +108 -0
- data/examples/cell_demo/app.rbs +7 -0
- data/examples/cell_demo/test_app.rb +36 -0
- data/examples/chart_demo/app.rb +203 -0
- data/examples/chart_demo/app.rbs +7 -0
- data/examples/chart_demo/test_app.rb +102 -0
- data/examples/custom_widget/app.rb +51 -0
- data/examples/custom_widget/app.rbs +7 -0
- data/examples/custom_widget/test_app.rb +30 -0
- data/examples/flex_layout/app.rb +156 -0
- data/examples/flex_layout/app.rbs +7 -0
- data/examples/flex_layout/test_app.rb +65 -0
- data/examples/gauge_demo/app.rb +182 -0
- data/examples/gauge_demo/app.rbs +7 -0
- data/examples/gauge_demo/test_app.rb +120 -0
- data/examples/hit_test/app.rb +175 -0
- data/examples/hit_test/app.rbs +7 -0
- data/examples/hit_test/test_app.rb +102 -0
- data/examples/line_gauge_demo/app.rb +190 -0
- data/examples/line_gauge_demo/app.rbs +7 -0
- data/examples/line_gauge_demo/test_app.rb +129 -0
- data/examples/list_demo/app.rb +253 -0
- data/examples/list_demo/app.rbs +12 -0
- data/examples/list_demo/test_app.rb +237 -0
- data/examples/list_styles/app.rb +140 -0
- data/examples/list_styles/app.rbs +7 -0
- data/examples/list_styles/test_app.rb +157 -0
- data/examples/{login_form.rb → login_form/app.rb} +12 -16
- data/examples/login_form/app.rbs +7 -0
- data/examples/login_form/test_app.rb +51 -0
- data/examples/map_demo/app.rb +90 -0
- data/examples/map_demo/app.rbs +7 -0
- data/examples/map_demo/test_app.rb +149 -0
- data/examples/{mouse_events.rb → mouse_events/app.rb} +29 -27
- data/examples/mouse_events/app.rbs +7 -0
- data/examples/mouse_events/test_app.rb +53 -0
- data/examples/{popup_demo.rb → popup_demo/app.rb} +15 -17
- data/examples/popup_demo/app.rbs +7 -0
- data/examples/{test_popup_demo.rb → popup_demo/test_app.rb} +18 -26
- data/examples/quickstart_dsl/app.rb +36 -0
- data/examples/quickstart_dsl/app.rbs +7 -0
- data/examples/quickstart_dsl/test_app.rb +29 -0
- data/examples/quickstart_lifecycle/app.rb +39 -0
- data/examples/quickstart_lifecycle/app.rbs +7 -0
- data/examples/quickstart_lifecycle/test_app.rb +29 -0
- data/examples/ratatui_logo_demo/app.rb +79 -0
- data/examples/ratatui_logo_demo/app.rbs +7 -0
- data/examples/ratatui_logo_demo/test_app.rb +51 -0
- data/examples/ratatui_mascot_demo/app.rb +84 -0
- data/examples/ratatui_mascot_demo/app.rbs +7 -0
- data/examples/ratatui_mascot_demo/test_app.rb +47 -0
- data/examples/readme_usage/app.rb +29 -0
- data/examples/readme_usage/app.rbs +7 -0
- data/examples/readme_usage/test_app.rb +29 -0
- data/examples/rich_text/app.rb +141 -0
- data/examples/rich_text/app.rbs +7 -0
- data/examples/rich_text/test_app.rb +166 -0
- data/examples/scroll_text/app.rb +103 -0
- data/examples/scroll_text/app.rbs +7 -0
- data/examples/scroll_text/test_app.rb +110 -0
- data/examples/scrollbar_demo/app.rb +143 -0
- data/examples/scrollbar_demo/app.rbs +7 -0
- data/examples/scrollbar_demo/test_app.rb +77 -0
- data/examples/sparkline_demo/app.rb +240 -0
- data/examples/sparkline_demo/app.rbs +10 -0
- data/examples/sparkline_demo/test_app.rb +107 -0
- data/examples/table_flex/app.rb +65 -0
- data/examples/table_flex/app.rbs +7 -0
- data/examples/table_flex/test_app.rb +36 -0
- data/examples/table_select/app.rb +198 -0
- data/examples/table_select/app.rbs +7 -0
- data/examples/table_select/test_app.rb +180 -0
- data/examples/widget_style_colors/app.rb +104 -0
- data/examples/widget_style_colors/app.rbs +14 -0
- data/examples/widget_style_colors/test_app.rb +48 -0
- data/ext/ratatui_ruby/Cargo.lock +889 -115
- data/ext/ratatui_ruby/Cargo.toml +4 -3
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +7 -0
- data/ext/ratatui_ruby/src/events.rs +218 -229
- data/ext/ratatui_ruby/src/lib.rs +38 -10
- data/ext/ratatui_ruby/src/rendering.rs +90 -10
- data/ext/ratatui_ruby/src/style.rs +281 -98
- data/ext/ratatui_ruby/src/terminal.rs +119 -25
- data/ext/ratatui_ruby/src/text.rs +171 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +97 -24
- data/ext/ratatui_ruby/src/widgets/block.rs +31 -3
- data/ext/ratatui_ruby/src/widgets/calendar.rs +45 -44
- data/ext/ratatui_ruby/src/widgets/canvas.rs +46 -29
- data/ext/ratatui_ruby/src/widgets/chart.rs +69 -27
- data/ext/ratatui_ruby/src/widgets/clear.rs +3 -1
- data/ext/ratatui_ruby/src/widgets/gauge.rs +11 -4
- data/ext/ratatui_ruby/src/widgets/layout.rs +218 -15
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +92 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +91 -11
- data/ext/ratatui_ruby/src/widgets/mod.rs +3 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +3 -2
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +35 -13
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +29 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +44 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +59 -7
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +70 -6
- data/ext/ratatui_ruby/src/widgets/table.rs +173 -64
- data/ext/ratatui_ruby/src/widgets/tabs.rs +105 -5
- data/lib/ratatui_ruby/cell.rb +166 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +49 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +50 -0
- data/lib/ratatui_ruby/event/key.rb +211 -0
- data/lib/ratatui_ruby/event/mouse.rb +124 -0
- data/lib/ratatui_ruby/event/paste.rb +71 -0
- data/lib/ratatui_ruby/event/resize.rb +80 -0
- data/lib/ratatui_ruby/event.rb +79 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +45 -0
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +27 -0
- data/lib/ratatui_ruby/schema/bar_chart.rb +228 -19
- data/lib/ratatui_ruby/schema/block.rb +186 -14
- data/lib/ratatui_ruby/schema/calendar.rb +74 -17
- data/lib/ratatui_ruby/schema/canvas.rb +215 -48
- data/lib/ratatui_ruby/schema/center.rb +49 -11
- data/lib/ratatui_ruby/schema/chart.rb +151 -41
- data/lib/ratatui_ruby/schema/clear.rb +41 -72
- data/lib/ratatui_ruby/schema/constraint.rb +82 -22
- data/lib/ratatui_ruby/schema/cursor.rb +27 -9
- data/lib/ratatui_ruby/schema/draw.rb +53 -0
- data/lib/ratatui_ruby/schema/gauge.rb +59 -15
- data/lib/ratatui_ruby/schema/layout.rb +95 -13
- data/lib/ratatui_ruby/schema/line_gauge.rb +78 -0
- data/lib/ratatui_ruby/schema/list.rb +93 -19
- data/lib/ratatui_ruby/schema/overlay.rb +34 -8
- data/lib/ratatui_ruby/schema/paragraph.rb +87 -30
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +25 -0
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +29 -0
- data/lib/ratatui_ruby/schema/rect.rb +64 -15
- data/lib/ratatui_ruby/schema/scrollbar.rb +132 -24
- data/lib/ratatui_ruby/schema/shape/label.rb +66 -0
- data/lib/ratatui_ruby/schema/sparkline.rb +122 -15
- data/lib/ratatui_ruby/schema/style.rb +49 -21
- data/lib/ratatui_ruby/schema/table.rb +119 -21
- data/lib/ratatui_ruby/schema/tabs.rb +75 -13
- data/lib/ratatui_ruby/schema/text.rb +90 -0
- data/lib/ratatui_ruby/session.rb +146 -0
- data/lib/ratatui_ruby/test_helper.rb +156 -13
- data/lib/ratatui_ruby/version.rb +1 -1
- data/lib/ratatui_ruby.rb +143 -23
- data/sig/ratatui_ruby/event.rbs +69 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +2 -1
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +16 -0
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +13 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +20 -2
- data/sig/ratatui_ruby/schema/block.rbs +5 -4
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -2
- data/sig/ratatui_ruby/schema/canvas.rbs +52 -39
- data/sig/ratatui_ruby/schema/center.rbs +3 -3
- data/sig/ratatui_ruby/schema/chart.rbs +8 -5
- data/sig/ratatui_ruby/schema/constraint.rbs +8 -5
- data/sig/ratatui_ruby/schema/cursor.rbs +1 -1
- data/sig/ratatui_ruby/schema/draw.rbs +23 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +4 -2
- data/sig/ratatui_ruby/schema/layout.rbs +11 -1
- data/sig/ratatui_ruby/schema/line_gauge.rbs +16 -0
- data/sig/ratatui_ruby/schema/list.rbs +5 -1
- data/sig/ratatui_ruby/schema/paragraph.rbs +4 -1
- data/{lib/ratatui_ruby/output.rb → sig/ratatui_ruby/schema/ratatui_logo.rbs} +3 -2
- data/sig/ratatui_ruby/{buffer.rbs → schema/ratatui_mascot.rbs} +4 -3
- data/sig/ratatui_ruby/schema/rect.rbs +2 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +18 -2
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -2
- data/sig/ratatui_ruby/schema/table.rbs +8 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +5 -1
- data/sig/ratatui_ruby/schema/text.rbs +22 -0
- data/tasks/resources/build.yml.erb +13 -11
- data/tasks/terminal_preview/app_screenshot.rb +35 -0
- data/tasks/terminal_preview/crash_report.rb +54 -0
- data/tasks/terminal_preview/example_app.rb +25 -0
- data/tasks/terminal_preview/launcher_script.rb +48 -0
- data/tasks/terminal_preview/preview_collection.rb +60 -0
- data/tasks/terminal_preview/preview_timing.rb +22 -0
- data/tasks/terminal_preview/safety_confirmation.rb +58 -0
- data/tasks/terminal_preview/saved_screenshot.rb +55 -0
- data/tasks/terminal_preview/system_appearance.rb +11 -0
- data/tasks/terminal_preview/terminal_window.rb +138 -0
- data/tasks/terminal_preview/window_id.rb +14 -0
- data/tasks/terminal_preview.rake +28 -0
- data/tasks/test.rake +1 -1
- metadata +174 -53
- data/doc/images/examples-analytics.rb.png +0 -0
- data/doc/images/examples-box_demo.rb.png +0 -0
- data/doc/images/examples-calendar_demo.rb.png +0 -0
- data/doc/images/examples-chart_demo.rb.png +0 -0
- data/doc/images/examples-custom_widget.rb.png +0 -0
- data/doc/images/examples-dashboard.rb.png +0 -0
- data/doc/images/examples-list_styles.rb.png +0 -0
- data/doc/images/examples-login_form.rb.png +0 -0
- data/doc/images/examples-map_demo.rb.png +0 -0
- data/doc/images/examples-mouse_events.rb.png +0 -0
- data/doc/images/examples-popup_demo.rb.gif +0 -0
- data/doc/images/examples-quickstart_lifecycle.rb.png +0 -0
- data/doc/images/examples-scroll_text.rb.png +0 -0
- data/doc/images/examples-scrollbar_demo.rb.png +0 -0
- data/doc/images/examples-stock_ticker.rb.png +0 -0
- data/doc/images/examples-system_monitor.rb.png +0 -0
- data/doc/images/examples-table_select.rb.png +0 -0
- data/examples/analytics.rb +0 -88
- data/examples/box_demo.rb +0 -71
- data/examples/calendar_demo.rb +0 -55
- data/examples/chart_demo.rb +0 -84
- data/examples/custom_widget.rb +0 -43
- data/examples/dashboard.rb +0 -72
- data/examples/list_styles.rb +0 -66
- data/examples/map_demo.rb +0 -58
- data/examples/quickstart_dsl.rb +0 -30
- data/examples/quickstart_lifecycle.rb +0 -40
- data/examples/readme_usage.rb +0 -21
- data/examples/scroll_text.rb +0 -74
- data/examples/scrollbar_demo.rb +0 -75
- data/examples/stock_ticker.rb +0 -93
- data/examples/system_monitor.rb +0 -94
- data/examples/table_select.rb +0 -70
- data/examples/test_analytics.rb +0 -65
- data/examples/test_box_demo.rb +0 -38
- data/examples/test_calendar_demo.rb +0 -66
- data/examples/test_dashboard.rb +0 -38
- data/examples/test_list_styles.rb +0 -61
- data/examples/test_login_form.rb +0 -63
- data/examples/test_map_demo.rb +0 -100
- data/examples/test_scroll_text.rb +0 -130
- data/examples/test_stock_ticker.rb +0 -39
- data/examples/test_system_monitor.rb +0 -40
- data/examples/test_table_select.rb +0 -37
- data/ext/ratatui_ruby/src/buffer.rs +0 -54
- data/lib/ratatui_ruby/dsl.rb +0 -64
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
use crate::style::parse_block;
|
|
2
|
+
use bumpalo::Bump;
|
|
3
|
+
use magnus::{prelude::*, Error, Value};
|
|
4
|
+
use ratatui::{
|
|
5
|
+
layout::Rect,
|
|
6
|
+
widgets::RatatuiMascot,
|
|
7
|
+
Frame,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
pub fn render_ratatui_mascot(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
11
|
+
let block_val: Value = node.funcall("block", ())?;
|
|
12
|
+
|
|
13
|
+
let mut inner_area = area;
|
|
14
|
+
|
|
15
|
+
if !block_val.is_nil() {
|
|
16
|
+
let bump = Bump::new();
|
|
17
|
+
let block = parse_block(block_val, &bump)?;
|
|
18
|
+
inner_area = block.inner(area);
|
|
19
|
+
frame.render_widget(block, area);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let widget = RatatuiMascot::new();
|
|
23
|
+
frame.render_widget(widget, inner_area);
|
|
24
|
+
Ok(())
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#[cfg(test)]
|
|
28
|
+
mod tests {
|
|
29
|
+
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
|
30
|
+
use super::*;
|
|
31
|
+
|
|
32
|
+
#[test]
|
|
33
|
+
fn test_render() {
|
|
34
|
+
let mut buffer = Buffer::empty(Rect::new(0, 0, 50, 20));
|
|
35
|
+
let widget = RatatuiMascot::new();
|
|
36
|
+
widget.render(Rect::new(0, 0, 50, 20), &mut buffer);
|
|
37
|
+
|
|
38
|
+
let content = buffer.content().iter().map(|c| c.symbol()).collect::<String>();
|
|
39
|
+
|
|
40
|
+
// The mascot uses block drawing characters
|
|
41
|
+
assert!(content.contains("█"), "Mascot rendering should contain block characters");
|
|
42
|
+
assert!(!content.trim().is_empty());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
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, Symbol, Value};
|
|
6
7
|
use ratatui::{
|
|
7
8
|
layout::Rect,
|
|
@@ -13,24 +14,75 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
13
14
|
let content_length: usize = node.funcall("content_length", ())?;
|
|
14
15
|
let position: usize = node.funcall("position", ())?;
|
|
15
16
|
let orientation_sym: Symbol = node.funcall("orientation", ())?;
|
|
16
|
-
|
|
17
|
+
|
|
18
|
+
let thumb_symbol_val: Value = node.funcall("thumb_symbol", ())?;
|
|
19
|
+
let thumb_style_val: Value = node.funcall("thumb_style", ())?;
|
|
20
|
+
let track_symbol_val: Value = node.funcall("track_symbol", ())?;
|
|
21
|
+
let track_style_val: Value = node.funcall("track_style", ())?;
|
|
22
|
+
let begin_symbol_val: Value = node.funcall("begin_symbol", ())?;
|
|
23
|
+
let begin_style_val: Value = node.funcall("begin_style", ())?;
|
|
24
|
+
let end_symbol_val: Value = node.funcall("end_symbol", ())?;
|
|
25
|
+
let end_style_val: Value = node.funcall("end_style", ())?;
|
|
26
|
+
let style_val: Value = node.funcall("style", ())?;
|
|
27
|
+
|
|
17
28
|
let block_val: Value = node.funcall("block", ())?;
|
|
18
29
|
|
|
19
30
|
let mut state = ScrollbarState::new(content_length).position(position);
|
|
20
|
-
let mut scrollbar = Scrollbar::default()
|
|
31
|
+
let mut scrollbar = Scrollbar::default();
|
|
21
32
|
|
|
22
33
|
scrollbar = match orientation_sym.to_string().as_str() {
|
|
23
|
-
"
|
|
34
|
+
"vertical_left" => scrollbar.orientation(ScrollbarOrientation::VerticalLeft),
|
|
35
|
+
"horizontal_bottom" | "horizontal" => scrollbar.orientation(ScrollbarOrientation::HorizontalBottom),
|
|
36
|
+
"horizontal_top" => scrollbar.orientation(ScrollbarOrientation::HorizontalTop),
|
|
24
37
|
_ => scrollbar.orientation(ScrollbarOrientation::VerticalRight),
|
|
25
38
|
};
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
// Hoisted strings to extend lifetime
|
|
41
|
+
let thumb_str: String;
|
|
42
|
+
let track_str: String;
|
|
43
|
+
let begin_str: String;
|
|
44
|
+
let end_str: String;
|
|
45
|
+
|
|
46
|
+
if !thumb_symbol_val.is_nil() {
|
|
47
|
+
thumb_str = thumb_symbol_val.funcall("to_s", ())?;
|
|
48
|
+
scrollbar = scrollbar.thumb_symbol(&thumb_str);
|
|
49
|
+
}
|
|
50
|
+
if !thumb_style_val.is_nil() {
|
|
51
|
+
scrollbar = scrollbar.thumb_style(crate::style::parse_style(thumb_style_val)?);
|
|
52
|
+
}
|
|
53
|
+
if !track_symbol_val.is_nil() {
|
|
54
|
+
track_str = track_symbol_val.funcall("to_s", ())?;
|
|
55
|
+
scrollbar = scrollbar.track_symbol(Some(&track_str));
|
|
56
|
+
}
|
|
57
|
+
if !track_style_val.is_nil() {
|
|
58
|
+
scrollbar = scrollbar.track_style(crate::style::parse_style(track_style_val)?);
|
|
59
|
+
}
|
|
60
|
+
if !begin_symbol_val.is_nil() {
|
|
61
|
+
begin_str = begin_symbol_val.funcall("to_s", ())?;
|
|
62
|
+
scrollbar = scrollbar.begin_symbol(Some(&begin_str));
|
|
63
|
+
}
|
|
64
|
+
if !begin_style_val.is_nil() {
|
|
65
|
+
scrollbar = scrollbar.begin_style(crate::style::parse_style(begin_style_val)?);
|
|
66
|
+
}
|
|
67
|
+
if !end_symbol_val.is_nil() {
|
|
68
|
+
end_str = end_symbol_val.funcall("to_s", ())?;
|
|
69
|
+
scrollbar = scrollbar.end_symbol(Some(&end_str));
|
|
70
|
+
}
|
|
71
|
+
if !end_style_val.is_nil() {
|
|
72
|
+
scrollbar = scrollbar.end_style(crate::style::parse_style(end_style_val)?);
|
|
73
|
+
}
|
|
74
|
+
if !style_val.is_nil() {
|
|
75
|
+
scrollbar = scrollbar.style(crate::style::parse_style(style_val)?);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if block_val.is_nil() {
|
|
79
|
+
frame.render_stateful_widget(scrollbar, area, &mut state);
|
|
80
|
+
} else {
|
|
81
|
+
let bump = Bump::new();
|
|
82
|
+
let block = parse_block(block_val, &bump)?;
|
|
29
83
|
let inner_area = block.inner(area);
|
|
30
84
|
frame.render_widget(block, area);
|
|
31
85
|
frame.render_stateful_widget(scrollbar, inner_area, &mut state);
|
|
32
|
-
} else {
|
|
33
|
-
frame.render_stateful_widget(scrollbar, area, &mut state);
|
|
34
86
|
}
|
|
35
87
|
Ok(())
|
|
36
88
|
}
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
-
use crate::style::{parse_block, parse_style};
|
|
5
|
-
use
|
|
6
|
-
use
|
|
4
|
+
use crate::style::{parse_block, parse_style, parse_bar_set};
|
|
5
|
+
use bumpalo::Bump;
|
|
6
|
+
use magnus::{prelude::*, Error, RString, Value};
|
|
7
|
+
use ratatui::{layout::Rect, widgets::Sparkline, widgets::RenderDirection, Frame};
|
|
7
8
|
|
|
8
9
|
pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
10
|
+
let bump = Bump::new();
|
|
11
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
9
12
|
let data_val: magnus::RArray = node.funcall("data", ())?;
|
|
10
13
|
let max_val: Value = node.funcall("max", ())?;
|
|
11
14
|
let style_val: Value = node.funcall("style", ())?;
|
|
12
15
|
let block_val: Value = node.funcall("block", ())?;
|
|
16
|
+
let direction_val: Value = node.funcall("direction", ())?;
|
|
17
|
+
let absent_value_symbol_val: Value = node.funcall("absent_value_symbol", ())?;
|
|
18
|
+
let absent_value_style_val: Value = node.funcall("absent_value_style", ())?;
|
|
19
|
+
let bar_set_val: Value = node.funcall("bar_set", ())?;
|
|
13
20
|
|
|
14
21
|
let mut data_vec = Vec::new();
|
|
15
22
|
for i in 0..data_val.len() {
|
|
16
|
-
let
|
|
17
|
-
|
|
23
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
24
|
+
let val: Value = data_val.entry(index)?;
|
|
25
|
+
if val.is_nil() {
|
|
26
|
+
data_vec.push(None);
|
|
27
|
+
} else {
|
|
28
|
+
let num: u64 = u64::try_convert(val)?;
|
|
29
|
+
data_vec.push(Some(num));
|
|
30
|
+
}
|
|
18
31
|
}
|
|
19
32
|
|
|
20
33
|
let mut sparkline = Sparkline::default().data(&data_vec);
|
|
@@ -29,7 +42,33 @@ pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
if !block_val.is_nil() {
|
|
32
|
-
sparkline = sparkline.block(parse_block(block_val)?);
|
|
45
|
+
sparkline = sparkline.block(parse_block(block_val, &bump)?);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if !direction_val.is_nil() {
|
|
49
|
+
let direction_sym: RString = direction_val.funcall("to_s", ())?;
|
|
50
|
+
let direction_str = direction_sym.to_string()?;
|
|
51
|
+
let direction = match direction_str.as_str() {
|
|
52
|
+
"right_to_left" => RenderDirection::RightToLeft,
|
|
53
|
+
_ => RenderDirection::LeftToRight,
|
|
54
|
+
};
|
|
55
|
+
sparkline = sparkline.direction(direction);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if !absent_value_symbol_val.is_nil() {
|
|
59
|
+
let symbol_str: String = String::try_convert(absent_value_symbol_val)?;
|
|
60
|
+
// Only use the first character if multiple are provided
|
|
61
|
+
if let Some(first_char) = symbol_str.chars().next() {
|
|
62
|
+
sparkline = sparkline.absent_value_symbol(first_char);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if !absent_value_style_val.is_nil() {
|
|
67
|
+
sparkline = sparkline.absent_value_style(parse_style(absent_value_style_val)?);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if !bar_set_val.is_nil() {
|
|
71
|
+
sparkline = sparkline.bar_set(parse_bar_set(bar_set_val, &bump)?);
|
|
33
72
|
}
|
|
34
73
|
|
|
35
74
|
frame.render_widget(sparkline, area);
|
|
@@ -56,4 +95,29 @@ mod tests {
|
|
|
56
95
|
let bars = buf.content().iter().filter(|c| c.symbol() != " ").count();
|
|
57
96
|
assert_eq!(bars, 4);
|
|
58
97
|
}
|
|
98
|
+
|
|
99
|
+
#[test]
|
|
100
|
+
fn test_sparkline_absent_value_symbol() {
|
|
101
|
+
// Data with absent (None) and present values: [Some(5), None, Some(8), None]
|
|
102
|
+
let data = vec![Some(5), None, Some(8), None];
|
|
103
|
+
let sparkline = Sparkline::default()
|
|
104
|
+
.data(&data)
|
|
105
|
+
.absent_value_symbol("-");
|
|
106
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 4, 1));
|
|
107
|
+
sparkline.render(Rect::new(0, 0, 4, 1), &mut buf);
|
|
108
|
+
|
|
109
|
+
// Collect all rendered symbols
|
|
110
|
+
let symbols: Vec<&str> = buf.content().iter().map(|c| c.symbol()).collect();
|
|
111
|
+
|
|
112
|
+
// Check that we have 4 cells rendered
|
|
113
|
+
assert_eq!(symbols.len(), 4, "Should have 4 cells rendered for 4 data points");
|
|
114
|
+
|
|
115
|
+
// Absent values (None) should render as "-"
|
|
116
|
+
assert_eq!(symbols[1], "-", "Second value (None) should render as dash");
|
|
117
|
+
assert_eq!(symbols[3], "-", "Fourth value (None) should render as dash");
|
|
118
|
+
|
|
119
|
+
// Present values should not be dashes
|
|
120
|
+
assert_ne!(symbols[0], "-", "First value (Some(5)) should not be dash");
|
|
121
|
+
assert_ne!(symbols[2], "-", "Third value (Some(8)) should not be dash");
|
|
122
|
+
}
|
|
59
123
|
}
|
|
@@ -2,116 +2,222 @@
|
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
use crate::style::{parse_block, parse_style};
|
|
5
|
+
use bumpalo::Bump;
|
|
5
6
|
use magnus::{prelude::*, Error, Symbol, Value};
|
|
6
7
|
use ratatui::{
|
|
7
|
-
layout::{Constraint, Rect},
|
|
8
|
-
widgets::{Cell, Row, Table, TableState},
|
|
8
|
+
layout::{Constraint, Flex, Rect},
|
|
9
|
+
widgets::{Cell, HighlightSpacing, Row, Table, TableState},
|
|
9
10
|
Frame,
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error> {
|
|
14
|
+
let bump = Bump::new();
|
|
13
15
|
let ruby = magnus::Ruby::get().unwrap();
|
|
14
16
|
let header_val: Value = node.funcall("header", ())?;
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
+
let footer_val: Value = node.funcall("footer", ())?;
|
|
18
|
+
let rows_value: Value = node.funcall("rows", ())?;
|
|
19
|
+
let rows_array = magnus::RArray::from_value(rows_value)
|
|
17
20
|
.ok_or_else(|| Error::new(ruby.exception_type_error(), "expected array for rows"))?;
|
|
18
21
|
let widths_val: Value = node.funcall("widths", ())?;
|
|
19
22
|
let widths_array = magnus::RArray::from_value(widths_val)
|
|
20
23
|
.ok_or_else(|| Error::new(ruby.exception_type_error(), "expected array for widths"))?;
|
|
21
24
|
let highlight_style_val: Value = node.funcall("highlight_style", ())?;
|
|
25
|
+
let column_highlight_style_val: Value = node.funcall("column_highlight_style", ())?;
|
|
26
|
+
let cell_highlight_style_val: Value = node.funcall("cell_highlight_style", ())?;
|
|
22
27
|
let highlight_symbol_val: Value = node.funcall("highlight_symbol", ())?;
|
|
23
28
|
let selected_row_val: Value = node.funcall("selected_row", ())?;
|
|
29
|
+
let selected_column_val: Value = node.funcall("selected_column", ())?;
|
|
24
30
|
let block_val: Value = node.funcall("block", ())?;
|
|
31
|
+
let flex_sym: Symbol = node.funcall("flex", ())?;
|
|
32
|
+
let highlight_spacing_sym: Symbol = node.funcall("highlight_spacing", ())?;
|
|
25
33
|
|
|
26
34
|
let mut rows = Vec::new();
|
|
27
35
|
for i in 0..rows_array.len() {
|
|
28
|
-
let
|
|
29
|
-
let
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let mut cells = Vec::new();
|
|
33
|
-
for j in 0..row_array.len() {
|
|
34
|
-
let cell_val: Value = row_array.entry(j as isize)?;
|
|
35
|
-
let class = cell_val.class();
|
|
36
|
-
let class_name = unsafe { class.name() };
|
|
37
|
-
|
|
38
|
-
if class_name.as_ref() == "RatatuiRuby::Paragraph" {
|
|
39
|
-
let text: String = cell_val.funcall("text", ())?;
|
|
40
|
-
let style_val: Value = cell_val.funcall("style", ())?;
|
|
41
|
-
let cell_style = parse_style(style_val)?;
|
|
42
|
-
cells.push(Cell::from(text).style(cell_style));
|
|
43
|
-
} else if class_name.as_ref() == "RatatuiRuby::Style" {
|
|
44
|
-
cells.push(Cell::from("").style(parse_style(cell_val)?));
|
|
45
|
-
} else {
|
|
46
|
-
let cell_str: String = cell_val.funcall("to_s", ())?;
|
|
47
|
-
cells.push(Cell::from(cell_str));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
rows.push(Row::new(cells));
|
|
36
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
37
|
+
let row_val: Value = rows_array.entry(index)?;
|
|
38
|
+
rows.push(parse_row(row_val)?);
|
|
51
39
|
}
|
|
52
40
|
|
|
53
|
-
let
|
|
54
|
-
for i in 0..widths_array.len() {
|
|
55
|
-
let constraint_obj: Value = widths_array.entry(i as isize)?;
|
|
56
|
-
let type_sym: Symbol = constraint_obj.funcall("type", ())?;
|
|
57
|
-
let value: u16 = constraint_obj.funcall("value", ())?;
|
|
41
|
+
let constraints = parse_constraints(widths_array)?;
|
|
58
42
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
let flex = match flex_sym.to_string().as_str() {
|
|
44
|
+
"start" => Flex::Start,
|
|
45
|
+
"center" => Flex::Center,
|
|
46
|
+
"end" => Flex::End,
|
|
47
|
+
"space_between" => Flex::SpaceBetween,
|
|
48
|
+
"space_around" => Flex::SpaceAround,
|
|
49
|
+
"space_evenly" => Flex::SpaceEvenly,
|
|
50
|
+
_ => Flex::Legacy,
|
|
51
|
+
};
|
|
66
52
|
|
|
67
|
-
let mut table = Table::new(rows, constraints);
|
|
53
|
+
let mut table = Table::new(rows, constraints).flex(flex);
|
|
54
|
+
|
|
55
|
+
let highlight_spacing = match highlight_spacing_sym.to_string().as_str() {
|
|
56
|
+
"always" => HighlightSpacing::Always,
|
|
57
|
+
"never" => HighlightSpacing::Never,
|
|
58
|
+
_ => HighlightSpacing::WhenSelected,
|
|
59
|
+
};
|
|
60
|
+
table = table.highlight_spacing(highlight_spacing);
|
|
68
61
|
|
|
69
62
|
if !header_val.is_nil() {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
let cell_val: Value = header_array.entry(i as isize)?;
|
|
76
|
-
let class = cell_val.class();
|
|
77
|
-
let class_name = unsafe { class.name() };
|
|
78
|
-
|
|
79
|
-
if class_name.as_ref() == "RatatuiRuby::Paragraph" {
|
|
80
|
-
let text: String = cell_val.funcall("text", ())?;
|
|
81
|
-
let style_val: Value = cell_val.funcall("style", ())?;
|
|
82
|
-
let cell_style = parse_style(style_val)?;
|
|
83
|
-
header_cells.push(Cell::from(text).style(cell_style));
|
|
84
|
-
} else {
|
|
85
|
-
let cell_str: String = cell_val.funcall("to_s", ())?;
|
|
86
|
-
header_cells.push(Cell::from(cell_str));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
table = table.header(Row::new(header_cells));
|
|
63
|
+
table = table.header(parse_row(header_val)?);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if !footer_val.is_nil() {
|
|
67
|
+
table = table.footer(parse_row(footer_val)?);
|
|
90
68
|
}
|
|
91
69
|
|
|
92
70
|
if !block_val.is_nil() {
|
|
93
|
-
table = table.block(parse_block(block_val)?);
|
|
71
|
+
table = table.block(parse_block(block_val, &bump)?);
|
|
94
72
|
}
|
|
95
73
|
|
|
96
74
|
if !highlight_style_val.is_nil() {
|
|
97
75
|
table = table.row_highlight_style(parse_style(highlight_style_val)?);
|
|
98
76
|
}
|
|
99
77
|
|
|
78
|
+
if !column_highlight_style_val.is_nil() {
|
|
79
|
+
table = table.column_highlight_style(parse_style(column_highlight_style_val)?);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if !cell_highlight_style_val.is_nil() {
|
|
83
|
+
table = table.cell_highlight_style(parse_style(cell_highlight_style_val)?);
|
|
84
|
+
}
|
|
85
|
+
|
|
100
86
|
if !highlight_symbol_val.is_nil() {
|
|
101
87
|
let symbol: String = highlight_symbol_val.funcall("to_s", ())?;
|
|
102
88
|
table = table.highlight_symbol(symbol);
|
|
103
89
|
}
|
|
104
90
|
|
|
91
|
+
let style_val: Value = node.funcall("style", ())?;
|
|
92
|
+
if !style_val.is_nil() {
|
|
93
|
+
table = table.style(parse_style(style_val)?);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let column_spacing_val: Value = node.funcall("column_spacing", ())?;
|
|
97
|
+
if !column_spacing_val.is_nil() {
|
|
98
|
+
let spacing: u16 = column_spacing_val.funcall("to_int", ())?;
|
|
99
|
+
table = table.column_spacing(spacing);
|
|
100
|
+
}
|
|
101
|
+
|
|
105
102
|
let mut state = TableState::default();
|
|
106
103
|
if !selected_row_val.is_nil() {
|
|
107
104
|
let index: usize = selected_row_val.funcall("to_int", ())?;
|
|
108
105
|
state.select(Some(index));
|
|
109
106
|
}
|
|
107
|
+
if !selected_column_val.is_nil() {
|
|
108
|
+
let index: usize = selected_column_val.funcall("to_int", ())?;
|
|
109
|
+
state.select_column(Some(index));
|
|
110
|
+
}
|
|
110
111
|
|
|
111
112
|
frame.render_stateful_widget(table, area, &mut state);
|
|
112
113
|
Ok(())
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
fn parse_row(row_val: Value) -> Result<Row<'static>, Error> {
|
|
117
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
118
|
+
let row_array = magnus::RArray::from_value(row_val)
|
|
119
|
+
.ok_or_else(|| Error::new(ruby.exception_type_error(), "expected array for row"))?;
|
|
120
|
+
|
|
121
|
+
let mut cells = Vec::new();
|
|
122
|
+
for i in 0..row_array.len() {
|
|
123
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
124
|
+
let cell_val: Value = row_array.entry(index)?;
|
|
125
|
+
cells.push(parse_cell(cell_val)?);
|
|
126
|
+
}
|
|
127
|
+
Ok(Row::new(cells))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn parse_cell(cell_val: Value) -> Result<Cell<'static>, Error> {
|
|
131
|
+
let class = cell_val.class();
|
|
132
|
+
// SAFETY: Immediate conversion to owned string avoids GC-unsafe borrowed reference.
|
|
133
|
+
let class_name = unsafe { class.name() }.into_owned();
|
|
134
|
+
|
|
135
|
+
if class_name == "RatatuiRuby::Paragraph" {
|
|
136
|
+
let text: String = cell_val.funcall("text", ())?;
|
|
137
|
+
let style_val: Value = cell_val.funcall("style", ())?;
|
|
138
|
+
let cell_style = parse_style(style_val)?;
|
|
139
|
+
Ok(Cell::from(text).style(cell_style))
|
|
140
|
+
} else if class_name == "RatatuiRuby::Style" {
|
|
141
|
+
Ok(Cell::from("").style(parse_style(cell_val)?))
|
|
142
|
+
} else if class_name == "RatatuiRuby::Cell" {
|
|
143
|
+
let symbol: String = cell_val.funcall("char", ())?;
|
|
144
|
+
let fg_val: Value = cell_val.funcall("fg", ())?;
|
|
145
|
+
let bg_val: Value = cell_val.funcall("bg", ())?;
|
|
146
|
+
let modifiers_val: Value = cell_val.funcall("modifiers", ())?;
|
|
147
|
+
|
|
148
|
+
let mut style = ratatui::style::Style::default();
|
|
149
|
+
if !fg_val.is_nil() {
|
|
150
|
+
if let Some(color) = crate::style::parse_color_value(fg_val)? {
|
|
151
|
+
style = style.fg(color);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if !bg_val.is_nil() {
|
|
155
|
+
if let Some(color) = crate::style::parse_color_value(bg_val)? {
|
|
156
|
+
style = style.bg(color);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if let Some(mods_array) = magnus::RArray::from_value(modifiers_val) {
|
|
160
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
161
|
+
for i in 0..mods_array.len() {
|
|
162
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
163
|
+
let mod_str: String = mods_array.entry::<String>(index)?;
|
|
164
|
+
if let Some(modifier) = crate::style::parse_modifier_str(&mod_str) {
|
|
165
|
+
style = style.add_modifier(modifier);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
Ok(Cell::from(symbol).style(style))
|
|
170
|
+
} else {
|
|
171
|
+
let cell_str: String = cell_val.funcall("to_s", ())?;
|
|
172
|
+
Ok(Cell::from(cell_str))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn parse_constraints(widths_array: magnus::RArray) -> Result<Vec<Constraint>, Error> {
|
|
177
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
178
|
+
let mut constraints = Vec::new();
|
|
179
|
+
for i in 0..widths_array.len() {
|
|
180
|
+
let index = isize::try_from(i).map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
181
|
+
let constraint_obj: Value = widths_array.entry(index)?;
|
|
182
|
+
let type_sym: Symbol = constraint_obj.funcall("type", ())?;
|
|
183
|
+
let value_obj: Value = constraint_obj.funcall("value", ())?;
|
|
184
|
+
|
|
185
|
+
match type_sym.to_string().as_str() {
|
|
186
|
+
"length" => {
|
|
187
|
+
let val = u16::try_convert(value_obj)?;
|
|
188
|
+
constraints.push(Constraint::Length(val));
|
|
189
|
+
}
|
|
190
|
+
"percentage" => {
|
|
191
|
+
let val = u16::try_convert(value_obj)?;
|
|
192
|
+
constraints.push(Constraint::Percentage(val));
|
|
193
|
+
}
|
|
194
|
+
"min" => {
|
|
195
|
+
let val = u16::try_convert(value_obj)?;
|
|
196
|
+
constraints.push(Constraint::Min(val));
|
|
197
|
+
}
|
|
198
|
+
"max" => {
|
|
199
|
+
let val = u16::try_convert(value_obj)?;
|
|
200
|
+
constraints.push(Constraint::Max(val));
|
|
201
|
+
}
|
|
202
|
+
"fill" => {
|
|
203
|
+
let val = u16::try_convert(value_obj)?;
|
|
204
|
+
constraints.push(Constraint::Fill(val));
|
|
205
|
+
}
|
|
206
|
+
"ratio" => {
|
|
207
|
+
if let Some(arr) = magnus::RArray::from_value(value_obj) {
|
|
208
|
+
if arr.len() == 2 {
|
|
209
|
+
let n = u32::try_convert(arr.entry(0)?)?;
|
|
210
|
+
let d = u32::try_convert(arr.entry(1)?)?;
|
|
211
|
+
constraints.push(Constraint::Ratio(n, d));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
_ => {}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
Ok(constraints)
|
|
219
|
+
}
|
|
220
|
+
|
|
115
221
|
#[cfg(test)]
|
|
116
222
|
mod tests {
|
|
117
223
|
use super::*;
|
|
@@ -122,14 +228,17 @@ mod tests {
|
|
|
122
228
|
fn test_table_rendering() {
|
|
123
229
|
let rows = vec![Row::new(vec!["C1", "C2"])];
|
|
124
230
|
let table = Table::new(rows, [Constraint::Length(3), Constraint::Length(3)])
|
|
125
|
-
.header(Row::new(vec!["H1", "H2"]))
|
|
126
|
-
|
|
127
|
-
|
|
231
|
+
.header(Row::new(vec!["H1", "H2"]))
|
|
232
|
+
.footer(Row::new(vec!["F1", "F2"]));
|
|
233
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 3));
|
|
234
|
+
Widget::render(table, Rect::new(0, 0, 10, 3), &mut buf);
|
|
128
235
|
|
|
129
236
|
let content = buf.content().iter().map(|c| c.symbol()).collect::<String>();
|
|
130
237
|
// Check for presence of header and row content
|
|
131
238
|
assert!(content.contains("H1"));
|
|
132
239
|
assert!(content.contains("H2"));
|
|
240
|
+
assert!(content.contains("F1"));
|
|
241
|
+
assert!(content.contains("F2"));
|
|
133
242
|
assert!(content.contains("C1"));
|
|
134
243
|
assert!(content.contains("C2"));
|
|
135
244
|
}
|