ratatui_ruby 1.4.0-x86_64-linux
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 +7 -0
- data/LICENSE +15 -0
- data/LICENSES/AGPL-3.0-or-later.txt +661 -0
- data/LICENSES/CC-BY-SA-4.0.txt +427 -0
- data/LICENSES/CC0-1.0.txt +121 -0
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/LICENSES/MIT.txt +21 -0
- data/REUSE.toml +42 -0
- data/exe/.gitkeep +0 -0
- data/ext/ratatui_ruby/.cargo/config.toml +13 -0
- data/ext/ratatui_ruby/.gitignore +4 -0
- data/ext/ratatui_ruby/Cargo.lock +1737 -0
- data/ext/ratatui_ruby/Cargo.toml +24 -0
- data/ext/ratatui_ruby/clippy.toml +7 -0
- data/ext/ratatui_ruby/extconf.rb +21 -0
- data/ext/ratatui_ruby/src/color.rs +82 -0
- data/ext/ratatui_ruby/src/errors.rs +28 -0
- data/ext/ratatui_ruby/src/events.rs +700 -0
- data/ext/ratatui_ruby/src/frame.rs +241 -0
- data/ext/ratatui_ruby/src/lib.rs +343 -0
- data/ext/ratatui_ruby/src/lib_header.rs +11 -0
- data/ext/ratatui_ruby/src/rendering.rs +158 -0
- data/ext/ratatui_ruby/src/string_width.rs +101 -0
- data/ext/ratatui_ruby/src/style.rs +469 -0
- data/ext/ratatui_ruby/src/terminal/capabilities.rs +46 -0
- data/ext/ratatui_ruby/src/terminal/init.rs +233 -0
- data/ext/ratatui_ruby/src/terminal/mod.rs +42 -0
- data/ext/ratatui_ruby/src/terminal/mutations.rs +158 -0
- data/ext/ratatui_ruby/src/terminal/queries.rs +231 -0
- data/ext/ratatui_ruby/src/terminal/query.rs +400 -0
- data/ext/ratatui_ruby/src/terminal/storage.rs +109 -0
- data/ext/ratatui_ruby/src/terminal/wrapper.rs +16 -0
- data/ext/ratatui_ruby/src/text.rs +225 -0
- data/ext/ratatui_ruby/src/widgets/barchart.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/block.rs +41 -0
- data/ext/ratatui_ruby/src/widgets/calendar.rs +84 -0
- data/ext/ratatui_ruby/src/widgets/canvas.rs +183 -0
- data/ext/ratatui_ruby/src/widgets/center.rs +79 -0
- data/ext/ratatui_ruby/src/widgets/chart.rs +222 -0
- data/ext/ratatui_ruby/src/widgets/clear.rs +39 -0
- data/ext/ratatui_ruby/src/widgets/cursor.rs +32 -0
- data/ext/ratatui_ruby/src/widgets/gauge.rs +65 -0
- data/ext/ratatui_ruby/src/widgets/layout.rs +379 -0
- data/ext/ratatui_ruby/src/widgets/line_gauge.rs +100 -0
- data/ext/ratatui_ruby/src/widgets/list.rs +378 -0
- data/ext/ratatui_ruby/src/widgets/list_state.rs +173 -0
- data/ext/ratatui_ruby/src/widgets/mod.rs +26 -0
- data/ext/ratatui_ruby/src/widgets/overlay.rs +24 -0
- data/ext/ratatui_ruby/src/widgets/paragraph.rs +87 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_logo.rs +40 -0
- data/ext/ratatui_ruby/src/widgets/ratatui_mascot.rs +55 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar.rs +214 -0
- data/ext/ratatui_ruby/src/widgets/scrollbar_state.rs +169 -0
- data/ext/ratatui_ruby/src/widgets/sparkline.rs +127 -0
- data/ext/ratatui_ruby/src/widgets/table.rs +415 -0
- data/ext/ratatui_ruby/src/widgets/table_state.rs +203 -0
- data/ext/ratatui_ruby/src/widgets/tabs.rs +194 -0
- data/lib/ratatui_ruby/backend/window_size.rb +50 -0
- data/lib/ratatui_ruby/backend.rb +59 -0
- data/lib/ratatui_ruby/buffer/cell.rb +212 -0
- data/lib/ratatui_ruby/buffer.rb +149 -0
- data/lib/ratatui_ruby/cell.rb +208 -0
- data/lib/ratatui_ruby/debug.rb +215 -0
- data/lib/ratatui_ruby/draw.rb +63 -0
- data/lib/ratatui_ruby/event/focus_gained.rb +125 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +127 -0
- data/lib/ratatui_ruby/event/key/character.rb +53 -0
- data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
- data/lib/ratatui_ruby/event/key/media.rb +46 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +107 -0
- data/lib/ratatui_ruby/event/key/navigation.rb +72 -0
- data/lib/ratatui_ruby/event/key/system.rb +47 -0
- data/lib/ratatui_ruby/event/key.rb +479 -0
- data/lib/ratatui_ruby/event/mouse.rb +291 -0
- data/lib/ratatui_ruby/event/none.rb +53 -0
- data/lib/ratatui_ruby/event/paste.rb +130 -0
- data/lib/ratatui_ruby/event/resize.rb +221 -0
- data/lib/ratatui_ruby/event/sync.rb +52 -0
- data/lib/ratatui_ruby/event.rb +163 -0
- data/lib/ratatui_ruby/frame.rb +257 -0
- data/lib/ratatui_ruby/labs/a11y.rb +182 -0
- data/lib/ratatui_ruby/labs/frame_a11y_capture.rb +50 -0
- data/lib/ratatui_ruby/labs.rb +47 -0
- data/lib/ratatui_ruby/layout/alignment.rb +91 -0
- data/lib/ratatui_ruby/layout/constraint.rb +337 -0
- data/lib/ratatui_ruby/layout/layout.rb +258 -0
- data/lib/ratatui_ruby/layout/position.rb +81 -0
- data/lib/ratatui_ruby/layout/rect.rb +733 -0
- data/lib/ratatui_ruby/layout/size.rb +62 -0
- data/lib/ratatui_ruby/layout.rb +29 -0
- data/lib/ratatui_ruby/list_state.rb +201 -0
- data/lib/ratatui_ruby/output_guard.rb +171 -0
- data/lib/ratatui_ruby/ratatui_ruby.so +0 -0
- data/lib/ratatui_ruby/scrollbar_state.rb +122 -0
- data/lib/ratatui_ruby/style/color.rb +149 -0
- data/lib/ratatui_ruby/style/style.rb +147 -0
- data/lib/ratatui_ruby/style.rb +19 -0
- data/lib/ratatui_ruby/symbols.rb +435 -0
- data/lib/ratatui_ruby/synthetic_events.rb +106 -0
- data/lib/ratatui_ruby/table_state.rb +251 -0
- data/lib/ratatui_ruby/terminal/capabilities.rb +316 -0
- data/lib/ratatui_ruby/terminal/viewport.rb +80 -0
- data/lib/ratatui_ruby/terminal.rb +66 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb +303 -0
- data/lib/ratatui_ruby/terminal_lifecycle.rb.bak +197 -0
- data/lib/ratatui_ruby/test_helper/event_injection.rb +241 -0
- data/lib/ratatui_ruby/test_helper/global_state.rb +111 -0
- data/lib/ratatui_ruby/test_helper/snapshot.rb +568 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/axis_labels_alignment.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.ansi +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/barchart_styled_label.txt +5 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.ansi +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/chart_rendering.txt +24 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/half_block_marker.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_bottom.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_left.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_right.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.ansi +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/legend_position_top.txt +12 -0
- data/lib/ratatui_ruby/test_helper/snapshots/my_snapshot.txt +1 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_axis_title.txt +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.ansi +10 -0
- data/lib/ratatui_ruby/test_helper/snapshots/styled_dataset_name.txt +10 -0
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +449 -0
- data/lib/ratatui_ruby/test_helper/subprocess_timeout.rb +35 -0
- data/lib/ratatui_ruby/test_helper/terminal.rb +187 -0
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +86 -0
- data/lib/ratatui_ruby/test_helper.rb +115 -0
- data/lib/ratatui_ruby/text/line.rb +245 -0
- data/lib/ratatui_ruby/text/span.rb +158 -0
- data/lib/ratatui_ruby/text.rb +99 -0
- data/lib/ratatui_ruby/tui/buffer_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/canvas_factories.rb +149 -0
- data/lib/ratatui_ruby/tui/core.rb +67 -0
- data/lib/ratatui_ruby/tui/layout_factories.rb +153 -0
- data/lib/ratatui_ruby/tui/state_factories.rb +77 -0
- data/lib/ratatui_ruby/tui/style_factories.rb +22 -0
- data/lib/ratatui_ruby/tui/text_factories.rb +86 -0
- data/lib/ratatui_ruby/tui/widget_factories.rb +272 -0
- data/lib/ratatui_ruby/tui.rb +106 -0
- data/lib/ratatui_ruby/version.rb +12 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +51 -0
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +29 -0
- data/lib/ratatui_ruby/widgets/bar_chart.rb +308 -0
- data/lib/ratatui_ruby/widgets/block.rb +266 -0
- data/lib/ratatui_ruby/widgets/calendar.rb +88 -0
- data/lib/ratatui_ruby/widgets/canvas.rb +297 -0
- data/lib/ratatui_ruby/widgets/cell.rb +59 -0
- data/lib/ratatui_ruby/widgets/center.rb +71 -0
- data/lib/ratatui_ruby/widgets/chart.rb +172 -0
- data/lib/ratatui_ruby/widgets/clear.rb +66 -0
- data/lib/ratatui_ruby/widgets/coerceable_widget.rb +77 -0
- data/lib/ratatui_ruby/widgets/cursor.rb +54 -0
- data/lib/ratatui_ruby/widgets/gauge.rb +146 -0
- data/lib/ratatui_ruby/widgets/line_gauge.rb +158 -0
- data/lib/ratatui_ruby/widgets/list.rb +252 -0
- data/lib/ratatui_ruby/widgets/list_item.rb +55 -0
- data/lib/ratatui_ruby/widgets/overlay.rb +55 -0
- data/lib/ratatui_ruby/widgets/paragraph.rb +113 -0
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +35 -0
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +40 -0
- data/lib/ratatui_ruby/widgets/row.rb +123 -0
- data/lib/ratatui_ruby/widgets/scrollbar.rb +147 -0
- data/lib/ratatui_ruby/widgets/shape/label.rb +80 -0
- data/lib/ratatui_ruby/widgets/sparkline.rb +153 -0
- data/lib/ratatui_ruby/widgets/table.rb +213 -0
- data/lib/ratatui_ruby/widgets/tabs.rb +91 -0
- data/lib/ratatui_ruby/widgets.rb +43 -0
- data/lib/ratatui_ruby.rb +555 -0
- data/sig/examples/app_all_events/app.rbs +11 -0
- data/sig/examples/app_all_events/model/app_model.rbs +23 -0
- data/sig/examples/app_all_events/model/event_entry.rbs +23 -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 +14 -0
- data/sig/examples/app_cli_rich_moments/app.rbs +12 -0
- data/sig/examples/app_color_picker/app.rbs +17 -0
- data/sig/examples/app_external_editor/app.rbs +12 -0
- data/sig/examples/app_login_form/app.rbs +11 -0
- data/sig/examples/app_stateful_interaction/app.rbs +39 -0
- data/sig/examples/verify_quickstart_dsl/app.rbs +17 -0
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +17 -0
- data/sig/examples/verify_readme_usage/app.rbs +17 -0
- data/sig/examples/widget_block_demo/app.rbs +38 -0
- data/sig/examples/widget_box_demo/app.rbs +17 -0
- data/sig/examples/widget_calendar_demo/app.rbs +17 -0
- data/sig/examples/widget_cell_demo/app.rbs +17 -0
- data/sig/examples/widget_chart_demo/app.rbs +17 -0
- data/sig/examples/widget_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_layout_split/app.rbs +16 -0
- data/sig/examples/widget_line_gauge_demo/app.rbs +17 -0
- data/sig/examples/widget_list_demo/app.rbs +17 -0
- data/sig/examples/widget_map_demo/app.rbs +17 -0
- data/sig/examples/widget_popup_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +17 -0
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +17 -0
- data/sig/examples/widget_rect/app.rbs +18 -0
- data/sig/examples/widget_render/app.rbs +16 -0
- data/sig/examples/widget_rich_text/app.rbs +17 -0
- data/sig/examples/widget_scroll_text/app.rbs +17 -0
- data/sig/examples/widget_scrollbar_demo/app.rbs +17 -0
- data/sig/examples/widget_sparkline_demo/app.rbs +16 -0
- data/sig/examples/widget_style_colors/app.rbs +20 -0
- data/sig/examples/widget_table_demo/app.rbs +17 -0
- data/sig/examples/widget_text_width/app.rbs +16 -0
- data/sig/generated/event_key_predicates.rbs +1348 -0
- data/sig/manifest.yaml +5 -0
- data/sig/patches/data.rbs +26 -0
- data/sig/patches/debugger__.rbs +8 -0
- data/sig/ratatui_ruby/backend/window_size.rbs +17 -0
- data/sig/ratatui_ruby/backend.rbs +12 -0
- data/sig/ratatui_ruby/buffer/cell.rbs +46 -0
- data/sig/ratatui_ruby/buffer.rbs +18 -0
- data/sig/ratatui_ruby/cell.rbs +44 -0
- data/sig/ratatui_ruby/clear.rbs +18 -0
- data/sig/ratatui_ruby/constraint.rbs +26 -0
- data/sig/ratatui_ruby/debug.rbs +45 -0
- data/sig/ratatui_ruby/draw.rbs +30 -0
- data/sig/ratatui_ruby/event.rbs +249 -0
- data/sig/ratatui_ruby/frame.rbs +23 -0
- data/sig/ratatui_ruby/interfaces.rbs +25 -0
- data/sig/ratatui_ruby/labs.rbs +90 -0
- data/sig/ratatui_ruby/layout/alignment.rbs +26 -0
- data/sig/ratatui_ruby/layout/constraint.rbs +39 -0
- data/sig/ratatui_ruby/layout/layout.rbs +45 -0
- data/sig/ratatui_ruby/layout/position.rbs +18 -0
- data/sig/ratatui_ruby/layout/rect.rbs +64 -0
- data/sig/ratatui_ruby/layout/size.rbs +18 -0
- data/sig/ratatui_ruby/list_state.rbs +23 -0
- data/sig/ratatui_ruby/output_guard.rbs +23 -0
- data/sig/ratatui_ruby/ratatui_ruby.rbs +113 -0
- data/sig/ratatui_ruby/rect.rbs +17 -0
- data/sig/ratatui_ruby/scrollbar_state.rbs +24 -0
- data/sig/ratatui_ruby/session.rbs +93 -0
- data/sig/ratatui_ruby/style/color.rbs +22 -0
- data/sig/ratatui_ruby/style/style.rbs +29 -0
- data/sig/ratatui_ruby/symbols.rbs +141 -0
- data/sig/ratatui_ruby/synthetic_events.rbs +24 -0
- data/sig/ratatui_ruby/table_state.rbs +27 -0
- data/sig/ratatui_ruby/terminal/capabilities.rbs +38 -0
- data/sig/ratatui_ruby/terminal/viewport.rbs +33 -0
- data/sig/ratatui_ruby/terminal_lifecycle.rbs +39 -0
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +22 -0
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +37 -0
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +77 -0
- data/sig/ratatui_ruby/test_helper/terminal.rbs +20 -0
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +32 -0
- data/sig/ratatui_ruby/test_helper.rbs +18 -0
- data/sig/ratatui_ruby/text/line.rbs +27 -0
- data/sig/ratatui_ruby/text/span.rbs +23 -0
- data/sig/ratatui_ruby/text.rbs +12 -0
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +16 -0
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +38 -0
- data/sig/ratatui_ruby/tui/core.rbs +23 -0
- data/sig/ratatui_ruby/tui/layout_factories.rbs +39 -0
- data/sig/ratatui_ruby/tui/state_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/style_factories.rbs +18 -0
- data/sig/ratatui_ruby/tui/text_factories.rbs +23 -0
- data/sig/ratatui_ruby/tui/widget_factories.rbs +138 -0
- data/sig/ratatui_ruby/tui.rbs +25 -0
- data/sig/ratatui_ruby/version.rbs +12 -0
- data/sig/ratatui_ruby/widgets/bar_chart.rbs +95 -0
- data/sig/ratatui_ruby/widgets/block.rbs +51 -0
- data/sig/ratatui_ruby/widgets/calendar.rbs +45 -0
- data/sig/ratatui_ruby/widgets/canvas.rbs +95 -0
- data/sig/ratatui_ruby/widgets/chart.rbs +91 -0
- data/sig/ratatui_ruby/widgets/coerceable_widget.rbs +26 -0
- data/sig/ratatui_ruby/widgets/gauge.rbs +44 -0
- data/sig/ratatui_ruby/widgets/line_gauge.rbs +48 -0
- data/sig/ratatui_ruby/widgets/list.rbs +63 -0
- data/sig/ratatui_ruby/widgets/misc.rbs +158 -0
- data/sig/ratatui_ruby/widgets/paragraph.rbs +45 -0
- data/sig/ratatui_ruby/widgets/row.rbs +43 -0
- data/sig/ratatui_ruby/widgets/scrollbar.rbs +53 -0
- data/sig/ratatui_ruby/widgets/shape/label.rbs +37 -0
- data/sig/ratatui_ruby/widgets/sparkline.rbs +45 -0
- data/sig/ratatui_ruby/widgets/table.rbs +78 -0
- data/sig/ratatui_ruby/widgets/tabs.rbs +44 -0
- data/sig/ratatui_ruby/widgets.rbs +16 -0
- data/vendor/goodcop/base.yml +1047 -0
- metadata +729 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
use crate::errors::type_error_with_context;
|
|
5
|
+
use crate::rendering::render_node;
|
|
6
|
+
use magnus::{prelude::*, Error, Symbol, Value};
|
|
7
|
+
use ratatui::{
|
|
8
|
+
buffer::Buffer,
|
|
9
|
+
layout::{Constraint, Direction, Flex, Layout, Rect},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
pub fn render(buffer: &mut Buffer, area: Rect, node: Value) -> Result<(), Error> {
|
|
13
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
14
|
+
let direction_sym: Symbol = node.funcall("direction", ())?;
|
|
15
|
+
let children_val: Value = node.funcall("children", ())?;
|
|
16
|
+
let children_array = magnus::RArray::from_value(children_val).ok_or_else(|| {
|
|
17
|
+
type_error_with_context(&ruby, "expected array for children", children_val)
|
|
18
|
+
})?;
|
|
19
|
+
|
|
20
|
+
let constraints_val: Value = node.funcall("constraints", ())?;
|
|
21
|
+
let constraints_array = magnus::RArray::from_value(constraints_val);
|
|
22
|
+
|
|
23
|
+
let flex_sym: Symbol = node.funcall("flex", ())?;
|
|
24
|
+
|
|
25
|
+
let direction = if direction_sym.to_string() == "vertical" {
|
|
26
|
+
Direction::Vertical
|
|
27
|
+
} else {
|
|
28
|
+
Direction::Horizontal
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let flex = match flex_sym.to_string().as_str() {
|
|
32
|
+
"start" => Flex::Start,
|
|
33
|
+
"center" => Flex::Center,
|
|
34
|
+
"end" => Flex::End,
|
|
35
|
+
"space_between" => Flex::SpaceBetween,
|
|
36
|
+
"space_around" => Flex::SpaceAround,
|
|
37
|
+
"space_evenly" => Flex::SpaceEvenly,
|
|
38
|
+
_ => Flex::Legacy,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let len = children_array.len();
|
|
42
|
+
if len > 0 {
|
|
43
|
+
let mut ratatui_constraints = Vec::new();
|
|
44
|
+
|
|
45
|
+
if let Some(arr) = constraints_array {
|
|
46
|
+
for i in 0..arr.len() {
|
|
47
|
+
let index = isize::try_from(i)
|
|
48
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
49
|
+
let constraint_obj: Value = arr.entry(index)?;
|
|
50
|
+
if let Ok(constraint) = parse_constraint(constraint_obj) {
|
|
51
|
+
ratatui_constraints.push(constraint);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If constraints don't match children, adjust or default
|
|
57
|
+
if ratatui_constraints.len() != len {
|
|
58
|
+
ratatui_constraints = (0..len)
|
|
59
|
+
.map(|_| {
|
|
60
|
+
Constraint::Percentage(100 / u16::try_from(len).unwrap_or(u16::MAX).max(1))
|
|
61
|
+
})
|
|
62
|
+
.collect();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let chunks = Layout::default()
|
|
66
|
+
.direction(direction)
|
|
67
|
+
.flex(flex)
|
|
68
|
+
.constraints(ratatui_constraints)
|
|
69
|
+
.split(area);
|
|
70
|
+
|
|
71
|
+
for i in 0..len {
|
|
72
|
+
let index = isize::try_from(i)
|
|
73
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
74
|
+
let child: Value = children_array.entry(index)?;
|
|
75
|
+
if let Err(e) = render_node(buffer, chunks[i], child) {
|
|
76
|
+
eprintln!("Error rendering child {i}: {e:?}");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
Ok(())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub fn parse_constraint(value: Value) -> Result<Constraint, Error> {
|
|
84
|
+
let type_sym: Symbol = value.funcall("type", ())?;
|
|
85
|
+
let value_obj: Value = value.funcall("value", ())?;
|
|
86
|
+
|
|
87
|
+
match type_sym.to_string().as_str() {
|
|
88
|
+
"length" => {
|
|
89
|
+
let val = u16::try_convert(value_obj)?;
|
|
90
|
+
Ok(Constraint::Length(val))
|
|
91
|
+
}
|
|
92
|
+
"percentage" => {
|
|
93
|
+
let val = u16::try_convert(value_obj)?;
|
|
94
|
+
Ok(Constraint::Percentage(val))
|
|
95
|
+
}
|
|
96
|
+
"min" => {
|
|
97
|
+
let val = u16::try_convert(value_obj)?;
|
|
98
|
+
Ok(Constraint::Min(val))
|
|
99
|
+
}
|
|
100
|
+
"max" => {
|
|
101
|
+
let val = u16::try_convert(value_obj)?;
|
|
102
|
+
Ok(Constraint::Max(val))
|
|
103
|
+
}
|
|
104
|
+
"fill" => {
|
|
105
|
+
let val = u16::try_convert(value_obj)?;
|
|
106
|
+
Ok(Constraint::Fill(val))
|
|
107
|
+
}
|
|
108
|
+
"ratio" => {
|
|
109
|
+
if let Some(arr) = magnus::RArray::from_value(value_obj) {
|
|
110
|
+
if arr.len() == 2 {
|
|
111
|
+
let n = u32::try_convert(arr.entry(0)?)?;
|
|
112
|
+
let d = u32::try_convert(arr.entry(1)?)?;
|
|
113
|
+
return Ok(Constraint::Ratio(n, d));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Fallback or error for invalid ratio?
|
|
117
|
+
// For now, let's treat it as Min(0) or similar, or error.
|
|
118
|
+
// But to match previous behavior (which ignored invalid), we just return 0 length or something?
|
|
119
|
+
// Check previous logic: it ignored it (`_ => {}`).
|
|
120
|
+
// But we need to return a Constraint. Use Length(0) as safe fallback if unmatched?
|
|
121
|
+
// Actually, let's error if strictly required, but existing logic pushed nothing if mismatched.
|
|
122
|
+
// If we push nothing, we can't return a Constraint.
|
|
123
|
+
// Let's assume input is valid for now or return a default.
|
|
124
|
+
Ok(Constraint::Length(0))
|
|
125
|
+
}
|
|
126
|
+
_ => Ok(Constraint::Length(0)), // Default fallback
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Splits an area into multiple rectangles based on constraints.
|
|
131
|
+
/// This is a pure calculation helper for hit testing.
|
|
132
|
+
///
|
|
133
|
+
/// # Arguments
|
|
134
|
+
/// * `area` - A Ruby Hash or Rect with :x, :y, :width, :height keys
|
|
135
|
+
/// * `direction` - Symbol :vertical or :horizontal
|
|
136
|
+
/// * `constraints` - Array of Constraint objects
|
|
137
|
+
/// * `flex` - Symbol for flex mode
|
|
138
|
+
///
|
|
139
|
+
/// # Returns
|
|
140
|
+
/// An array of Ruby Hashes representing Rect objects
|
|
141
|
+
pub fn split_layout(
|
|
142
|
+
area: Value,
|
|
143
|
+
direction: Symbol,
|
|
144
|
+
constraints: magnus::RArray,
|
|
145
|
+
flex: Symbol,
|
|
146
|
+
) -> Result<magnus::RArray, Error> {
|
|
147
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
148
|
+
|
|
149
|
+
// Parse area from Hash or Rect-like object
|
|
150
|
+
let x: u16 = area.funcall("x", ())?;
|
|
151
|
+
let y: u16 = area.funcall("y", ())?;
|
|
152
|
+
let width: u16 = area.funcall("width", ())?;
|
|
153
|
+
let height: u16 = area.funcall("height", ())?;
|
|
154
|
+
let rect = Rect::new(x, y, width, height);
|
|
155
|
+
|
|
156
|
+
// Parse direction
|
|
157
|
+
let dir = if direction.to_string() == "vertical" {
|
|
158
|
+
Direction::Vertical
|
|
159
|
+
} else {
|
|
160
|
+
Direction::Horizontal
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Parse flex
|
|
164
|
+
let flex_mode = match flex.to_string().as_str() {
|
|
165
|
+
"start" => Flex::Start,
|
|
166
|
+
"center" => Flex::Center,
|
|
167
|
+
"end" => Flex::End,
|
|
168
|
+
"space_between" => Flex::SpaceBetween,
|
|
169
|
+
"space_around" => Flex::SpaceAround,
|
|
170
|
+
"space_evenly" => Flex::SpaceEvenly,
|
|
171
|
+
_ => Flex::Legacy,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Parse constraints
|
|
175
|
+
let mut ratatui_constraints = Vec::new();
|
|
176
|
+
for i in 0..constraints.len() {
|
|
177
|
+
let index = isize::try_from(i)
|
|
178
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
179
|
+
let constraint_obj: Value = constraints.entry(index)?;
|
|
180
|
+
if let Ok(constraint) = parse_constraint(constraint_obj) {
|
|
181
|
+
ratatui_constraints.push(constraint);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Compute layout
|
|
186
|
+
let chunks = Layout::default()
|
|
187
|
+
.direction(dir)
|
|
188
|
+
.flex(flex_mode)
|
|
189
|
+
.constraints(ratatui_constraints)
|
|
190
|
+
.split(rect);
|
|
191
|
+
|
|
192
|
+
// Convert to Ruby array of Hashes
|
|
193
|
+
let result = ruby.ary_new_capa(chunks.len());
|
|
194
|
+
for chunk in chunks.iter() {
|
|
195
|
+
let hash = ruby.hash_new();
|
|
196
|
+
hash.aset(ruby.sym_new("x"), chunk.x)?;
|
|
197
|
+
hash.aset(ruby.sym_new("y"), chunk.y)?;
|
|
198
|
+
hash.aset(ruby.sym_new("width"), chunk.width)?;
|
|
199
|
+
hash.aset(ruby.sym_new("height"), chunk.height)?;
|
|
200
|
+
result.push(hash)?;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
Ok(result)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// Splits an area into multiple rectangles, returning both segments and spacers.
|
|
207
|
+
/// This is the Ratatui `split_with_spacers` equivalent.
|
|
208
|
+
///
|
|
209
|
+
/// # Arguments
|
|
210
|
+
/// * `area` - A Ruby Hash or Rect with :x, :y, :width, :height keys
|
|
211
|
+
/// * `direction` - Symbol :vertical or :horizontal
|
|
212
|
+
/// * `constraints` - Array of Constraint objects
|
|
213
|
+
/// * `flex` - Symbol for flex mode
|
|
214
|
+
///
|
|
215
|
+
/// # Returns
|
|
216
|
+
/// An array containing two arrays: [segments, spacers], each containing Ruby Hashes representing Rect objects
|
|
217
|
+
pub fn split_with_spacers_layout(
|
|
218
|
+
area: Value,
|
|
219
|
+
direction: Symbol,
|
|
220
|
+
constraints: magnus::RArray,
|
|
221
|
+
flex: Symbol,
|
|
222
|
+
) -> Result<magnus::RArray, Error> {
|
|
223
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
224
|
+
|
|
225
|
+
// Parse area from Hash or Rect-like object
|
|
226
|
+
let x: u16 = area.funcall("x", ())?;
|
|
227
|
+
let y: u16 = area.funcall("y", ())?;
|
|
228
|
+
let width: u16 = area.funcall("width", ())?;
|
|
229
|
+
let height: u16 = area.funcall("height", ())?;
|
|
230
|
+
let rect = Rect::new(x, y, width, height);
|
|
231
|
+
|
|
232
|
+
// Parse direction
|
|
233
|
+
let dir = if direction.to_string() == "vertical" {
|
|
234
|
+
Direction::Vertical
|
|
235
|
+
} else {
|
|
236
|
+
Direction::Horizontal
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Parse flex
|
|
240
|
+
let flex_mode = match flex.to_string().as_str() {
|
|
241
|
+
"start" => Flex::Start,
|
|
242
|
+
"center" => Flex::Center,
|
|
243
|
+
"end" => Flex::End,
|
|
244
|
+
"space_between" => Flex::SpaceBetween,
|
|
245
|
+
"space_around" => Flex::SpaceAround,
|
|
246
|
+
"space_evenly" => Flex::SpaceEvenly,
|
|
247
|
+
_ => Flex::Legacy,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// Parse constraints
|
|
251
|
+
let mut ratatui_constraints = Vec::new();
|
|
252
|
+
for i in 0..constraints.len() {
|
|
253
|
+
let index = isize::try_from(i)
|
|
254
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
255
|
+
let constraint_obj: Value = constraints.entry(index)?;
|
|
256
|
+
if let Ok(constraint) = parse_constraint(constraint_obj) {
|
|
257
|
+
ratatui_constraints.push(constraint);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Compute layout with spacers
|
|
262
|
+
let (segments, spacers) = Layout::default()
|
|
263
|
+
.direction(dir)
|
|
264
|
+
.flex(flex_mode)
|
|
265
|
+
.constraints(ratatui_constraints)
|
|
266
|
+
.split_with_spacers(rect);
|
|
267
|
+
|
|
268
|
+
// Helper to convert Rc<[Rect]> to Ruby array
|
|
269
|
+
let rects_to_ruby_array = |rects: &[Rect]| -> Result<magnus::RArray, Error> {
|
|
270
|
+
let arr = ruby.ary_new_capa(rects.len());
|
|
271
|
+
for chunk in rects {
|
|
272
|
+
let hash = ruby.hash_new();
|
|
273
|
+
hash.aset(ruby.sym_new("x"), chunk.x)?;
|
|
274
|
+
hash.aset(ruby.sym_new("y"), chunk.y)?;
|
|
275
|
+
hash.aset(ruby.sym_new("width"), chunk.width)?;
|
|
276
|
+
hash.aset(ruby.sym_new("height"), chunk.height)?;
|
|
277
|
+
arr.push(hash)?;
|
|
278
|
+
}
|
|
279
|
+
Ok(arr)
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
let segments_arr = rects_to_ruby_array(&segments)?;
|
|
283
|
+
let spacers_arr = rects_to_ruby_array(&spacers)?;
|
|
284
|
+
|
|
285
|
+
// Return [segments, spacers]
|
|
286
|
+
let result = ruby.ary_new_capa(2);
|
|
287
|
+
result.push(segments_arr)?;
|
|
288
|
+
result.push(spacers_arr)?;
|
|
289
|
+
|
|
290
|
+
Ok(result)
|
|
291
|
+
}
|
|
292
|
+
#[cfg(test)]
|
|
293
|
+
mod tests {
|
|
294
|
+
use ratatui::layout::{Constraint, Direction, Flex, Layout, Rect};
|
|
295
|
+
|
|
296
|
+
#[test]
|
|
297
|
+
fn test_layout_logic() {
|
|
298
|
+
let area = Rect::new(0, 0, 100, 100);
|
|
299
|
+
let chunks = Layout::default()
|
|
300
|
+
.direction(Direction::Vertical)
|
|
301
|
+
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
|
302
|
+
.split(area);
|
|
303
|
+
assert_eq!(chunks.len(), 2);
|
|
304
|
+
assert_eq!(chunks[0].height, 50);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#[test]
|
|
308
|
+
fn test_fill_constraint() {
|
|
309
|
+
let area = Rect::new(0, 0, 100, 10);
|
|
310
|
+
let chunks = Layout::default()
|
|
311
|
+
.direction(Direction::Horizontal)
|
|
312
|
+
.constraints([Constraint::Fill(1), Constraint::Fill(3)])
|
|
313
|
+
.split(area);
|
|
314
|
+
assert_eq!(chunks.len(), 2);
|
|
315
|
+
assert_eq!(chunks[0].width, 25);
|
|
316
|
+
assert_eq!(chunks[1].width, 75);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
#[test]
|
|
320
|
+
fn test_flex_space_between() {
|
|
321
|
+
let area = Rect::new(0, 0, 100, 10);
|
|
322
|
+
let chunks = Layout::default()
|
|
323
|
+
.direction(Direction::Horizontal)
|
|
324
|
+
.flex(Flex::SpaceBetween)
|
|
325
|
+
.constraints([
|
|
326
|
+
Constraint::Length(10),
|
|
327
|
+
Constraint::Length(10),
|
|
328
|
+
Constraint::Length(10),
|
|
329
|
+
])
|
|
330
|
+
.split(area);
|
|
331
|
+
assert_eq!(chunks.len(), 3);
|
|
332
|
+
assert_eq!(chunks[0].x, 0);
|
|
333
|
+
assert_eq!(chunks[1].x, 45);
|
|
334
|
+
assert_eq!(chunks[2].x, 90);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#[test]
|
|
338
|
+
fn test_flex_space_evenly() {
|
|
339
|
+
let area = Rect::new(0, 0, 100, 10);
|
|
340
|
+
let chunks = Layout::default()
|
|
341
|
+
.direction(Direction::Horizontal)
|
|
342
|
+
.flex(Flex::SpaceEvenly)
|
|
343
|
+
.constraints([
|
|
344
|
+
Constraint::Length(10),
|
|
345
|
+
Constraint::Length(10),
|
|
346
|
+
Constraint::Length(10),
|
|
347
|
+
])
|
|
348
|
+
.split(area);
|
|
349
|
+
assert_eq!(chunks.len(), 3);
|
|
350
|
+
assert_eq!(chunks[0].x, 18);
|
|
351
|
+
assert_eq!(chunks[1].x, 45);
|
|
352
|
+
assert_eq!(chunks[2].x, 73);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
#[test]
|
|
356
|
+
fn test_flex_center() {
|
|
357
|
+
let area = Rect::new(0, 0, 100, 10);
|
|
358
|
+
let chunks = Layout::default()
|
|
359
|
+
.direction(Direction::Horizontal)
|
|
360
|
+
.flex(Flex::Center)
|
|
361
|
+
.constraints([Constraint::Length(20)])
|
|
362
|
+
.split(area);
|
|
363
|
+
assert_eq!(chunks.len(), 1);
|
|
364
|
+
assert_eq!(chunks[0].x, 40);
|
|
365
|
+
assert_eq!(chunks[0].width, 20);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#[test]
|
|
369
|
+
fn test_max_constraint() {
|
|
370
|
+
let area = Rect::new(0, 0, 100, 10);
|
|
371
|
+
let chunks = Layout::default()
|
|
372
|
+
.direction(Direction::Horizontal)
|
|
373
|
+
.constraints([Constraint::Max(30), Constraint::Fill(1)])
|
|
374
|
+
.split(area);
|
|
375
|
+
assert_eq!(chunks.len(), 2);
|
|
376
|
+
assert_eq!(chunks[0].width, 30);
|
|
377
|
+
assert_eq!(chunks[1].width, 70);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
use crate::style::{parse_block, parse_style};
|
|
5
|
+
use crate::text::parse_span;
|
|
6
|
+
use bumpalo::Bump;
|
|
7
|
+
use magnus::{prelude::*, Error, Value};
|
|
8
|
+
use ratatui::buffer::Buffer;
|
|
9
|
+
use ratatui::{layout::Rect, widgets::LineGauge, widgets::Widget};
|
|
10
|
+
|
|
11
|
+
pub fn render(buffer: &mut Buffer, area: Rect, node: Value) -> Result<(), Error> {
|
|
12
|
+
let bump = Bump::new();
|
|
13
|
+
let ratio: f64 = node.funcall("ratio", ())?;
|
|
14
|
+
let label_val: Value = node.funcall("label", ())?;
|
|
15
|
+
let style_val: Value = node.funcall("style", ())?;
|
|
16
|
+
let filled_style_val: Value = node.funcall("filled_style", ())?;
|
|
17
|
+
let unfilled_style_val: Value = node.funcall("unfilled_style", ())?;
|
|
18
|
+
let block_val: Value = node.funcall("block", ())?;
|
|
19
|
+
let filled_symbol_val: String = node.funcall("filled_symbol", ())?;
|
|
20
|
+
let unfilled_symbol_val: String = node.funcall("unfilled_symbol", ())?;
|
|
21
|
+
|
|
22
|
+
let mut gauge = LineGauge::default()
|
|
23
|
+
.ratio(ratio)
|
|
24
|
+
.filled_symbol(&filled_symbol_val)
|
|
25
|
+
.unfilled_symbol(&unfilled_symbol_val);
|
|
26
|
+
|
|
27
|
+
if !label_val.is_nil() {
|
|
28
|
+
// Try to parse as a Span (rich text)
|
|
29
|
+
if let Ok(span) = parse_span(label_val) {
|
|
30
|
+
gauge = gauge.label(span);
|
|
31
|
+
} else {
|
|
32
|
+
// Fallback to string
|
|
33
|
+
let label_str: String = label_val.funcall("to_s", ())?;
|
|
34
|
+
gauge = gauge.label(label_str);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if !style_val.is_nil() {
|
|
39
|
+
gauge = gauge.style(parse_style(style_val)?);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if !filled_style_val.is_nil() {
|
|
43
|
+
let parsed_style = parse_style(filled_style_val)?;
|
|
44
|
+
gauge = gauge.filled_style(parsed_style);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if !unfilled_style_val.is_nil() {
|
|
48
|
+
let parsed_style = parse_style(unfilled_style_val)?;
|
|
49
|
+
gauge = gauge.unfilled_style(parsed_style);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if !block_val.is_nil() {
|
|
53
|
+
gauge = gauge.block(parse_block(block_val, &bump)?);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
gauge.render(area, buffer);
|
|
57
|
+
Ok(())
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#[cfg(test)]
|
|
61
|
+
mod tests {
|
|
62
|
+
use super::*;
|
|
63
|
+
use ratatui::buffer::Buffer;
|
|
64
|
+
use ratatui::widgets::{LineGauge, Widget};
|
|
65
|
+
|
|
66
|
+
#[test]
|
|
67
|
+
fn test_line_gauge_rendering() {
|
|
68
|
+
let gauge = LineGauge::default().ratio(0.5).label("50%");
|
|
69
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 20, 1));
|
|
70
|
+
gauge.render(Rect::new(0, 0, 20, 1), &mut buf);
|
|
71
|
+
// LineGauge renders filled and unfilled characters
|
|
72
|
+
assert!(buf.content().iter().any(|c| c.symbol() != " "));
|
|
73
|
+
// Should contain label
|
|
74
|
+
assert!(buf.content().iter().any(|c| c.symbol() == "5"));
|
|
75
|
+
assert!(buf.content().iter().any(|c| c.symbol() == "%"));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#[test]
|
|
79
|
+
fn test_line_gauge_zero_ratio() {
|
|
80
|
+
let gauge = LineGauge::default().ratio(0.0);
|
|
81
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 1));
|
|
82
|
+
gauge.render(Rect::new(0, 0, 10, 1), &mut buf);
|
|
83
|
+
// At zero ratio, should mostly show unfilled character
|
|
84
|
+
let unfilled_count = buf
|
|
85
|
+
.content()
|
|
86
|
+
.iter()
|
|
87
|
+
.filter(|c| c.symbol() == "░" || c.symbol() == " ")
|
|
88
|
+
.count();
|
|
89
|
+
assert!(unfilled_count > 0);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#[test]
|
|
93
|
+
fn test_line_gauge_full_ratio() {
|
|
94
|
+
let gauge = LineGauge::default().ratio(1.0).label("100%");
|
|
95
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 20, 1));
|
|
96
|
+
gauge.render(Rect::new(0, 0, 20, 1), &mut buf);
|
|
97
|
+
// At full ratio, should show some non-space characters (filled or partial)
|
|
98
|
+
assert!(buf.content().iter().any(|c| c.symbol() != " "));
|
|
99
|
+
}
|
|
100
|
+
}
|