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,183 @@
|
|
|
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_color, parse_style};
|
|
5
|
+
use crate::text::parse_text;
|
|
6
|
+
use bumpalo::Bump;
|
|
7
|
+
use magnus::{prelude::*, Error, RArray, Symbol, Value};
|
|
8
|
+
use ratatui::buffer::Buffer;
|
|
9
|
+
use ratatui::{
|
|
10
|
+
symbols::Marker,
|
|
11
|
+
widgets::{
|
|
12
|
+
canvas::{Canvas, Circle, Line, Map, MapResolution, Rectangle},
|
|
13
|
+
Widget,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
#[allow(clippy::too_many_lines)]
|
|
18
|
+
pub fn render(buffer: &mut Buffer, area: ratatui::layout::Rect, node: Value) -> Result<(), Error> {
|
|
19
|
+
let bump = Bump::new();
|
|
20
|
+
let shapes_val: RArray = node.funcall("shapes", ())?;
|
|
21
|
+
let x_bounds_val: RArray = node.funcall("x_bounds", ())?;
|
|
22
|
+
let y_bounds_val: RArray = node.funcall("y_bounds", ())?;
|
|
23
|
+
let marker_sym: Symbol = node.funcall("marker", ())?;
|
|
24
|
+
let block_val: Value = node.funcall("block", ())?;
|
|
25
|
+
|
|
26
|
+
let x_bounds: [f64; 2] = [x_bounds_val.entry::<f64>(0)?, x_bounds_val.entry::<f64>(1)?];
|
|
27
|
+
let y_bounds: [f64; 2] = [y_bounds_val.entry::<f64>(0)?, y_bounds_val.entry::<f64>(1)?];
|
|
28
|
+
|
|
29
|
+
let marker = match marker_sym.to_string().as_str() {
|
|
30
|
+
"dot" => Marker::Dot,
|
|
31
|
+
"block" => Marker::Block,
|
|
32
|
+
"bar" => Marker::Bar,
|
|
33
|
+
"half_block" => Marker::HalfBlock,
|
|
34
|
+
"quadrant" => Marker::Quadrant,
|
|
35
|
+
"sextant" => Marker::Sextant,
|
|
36
|
+
"octant" => Marker::Octant,
|
|
37
|
+
_ => Marker::Braille,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
let mut canvas = Canvas::default()
|
|
41
|
+
.x_bounds(x_bounds)
|
|
42
|
+
.y_bounds(y_bounds)
|
|
43
|
+
.marker(marker);
|
|
44
|
+
|
|
45
|
+
if !block_val.is_nil() {
|
|
46
|
+
canvas = canvas.block(parse_block(block_val, &bump)?);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let background_color_val: Value = node.funcall("background_color", ())?;
|
|
50
|
+
if !background_color_val.is_nil() {
|
|
51
|
+
let background_color =
|
|
52
|
+
parse_color(&background_color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
|
|
53
|
+
canvas = canvas.background_color(background_color);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let canvas = canvas.paint(|ctx| {
|
|
57
|
+
for shape_val in shapes_val {
|
|
58
|
+
let class = shape_val.class();
|
|
59
|
+
// SAFETY: Immediate conversion to owned string avoids GC-unsafe borrowed reference.
|
|
60
|
+
let class_name = unsafe { class.name() }.into_owned();
|
|
61
|
+
|
|
62
|
+
match class_name.as_str() {
|
|
63
|
+
"RatatuiRuby::Widgets::Shape::Line" => {
|
|
64
|
+
let x1: f64 = shape_val.funcall("x1", ()).unwrap_or(0.0);
|
|
65
|
+
let y1: f64 = shape_val.funcall("y1", ()).unwrap_or(0.0);
|
|
66
|
+
let x2: f64 = shape_val.funcall("x2", ()).unwrap_or(0.0);
|
|
67
|
+
let y2: f64 = shape_val.funcall("y2", ()).unwrap_or(0.0);
|
|
68
|
+
let color_val: Value = shape_val.funcall("color", ()).unwrap();
|
|
69
|
+
let color =
|
|
70
|
+
parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
|
|
71
|
+
ctx.draw(&Line {
|
|
72
|
+
x1,
|
|
73
|
+
y1,
|
|
74
|
+
x2,
|
|
75
|
+
y2,
|
|
76
|
+
color,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
"RatatuiRuby::Widgets::Shape::Rectangle" => {
|
|
80
|
+
let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
|
|
81
|
+
let y: f64 = shape_val.funcall("y", ()).unwrap_or(0.0);
|
|
82
|
+
let width: f64 = shape_val.funcall("width", ()).unwrap_or(0.0);
|
|
83
|
+
let height: f64 = shape_val.funcall("height", ()).unwrap_or(0.0);
|
|
84
|
+
let color_val: Value = shape_val.funcall("color", ()).unwrap();
|
|
85
|
+
let color =
|
|
86
|
+
parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
|
|
87
|
+
ctx.draw(&Rectangle {
|
|
88
|
+
x,
|
|
89
|
+
y,
|
|
90
|
+
width,
|
|
91
|
+
height,
|
|
92
|
+
color,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
"RatatuiRuby::Widgets::Shape::Circle" => {
|
|
96
|
+
let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
|
|
97
|
+
let y: f64 = shape_val.funcall("y", ()).unwrap_or(0.0);
|
|
98
|
+
let radius: f64 = shape_val.funcall("radius", ()).unwrap_or(0.0);
|
|
99
|
+
let color_val: Value = shape_val.funcall("color", ()).unwrap();
|
|
100
|
+
let color =
|
|
101
|
+
parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
|
|
102
|
+
ctx.draw(&Circle {
|
|
103
|
+
x,
|
|
104
|
+
y,
|
|
105
|
+
radius,
|
|
106
|
+
color,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
"RatatuiRuby::Widgets::Shape::Map" => {
|
|
110
|
+
let color_val: Value = shape_val.funcall("color", ()).unwrap();
|
|
111
|
+
let color =
|
|
112
|
+
parse_color(&color_val.to_string()).unwrap_or(ratatui::style::Color::Reset);
|
|
113
|
+
let resolution_sym: Symbol = shape_val.funcall("resolution", ()).unwrap();
|
|
114
|
+
let resolution = match resolution_sym.to_string().as_str() {
|
|
115
|
+
"high" => MapResolution::High,
|
|
116
|
+
_ => MapResolution::Low,
|
|
117
|
+
};
|
|
118
|
+
ctx.draw(&Map { color, resolution });
|
|
119
|
+
}
|
|
120
|
+
"RatatuiRuby::Widgets::Shape::Label" => {
|
|
121
|
+
let x: f64 = shape_val.funcall("x", ()).unwrap_or(0.0);
|
|
122
|
+
let y: f64 = shape_val.funcall("y", ()).unwrap_or(0.0);
|
|
123
|
+
let text_val: Value = shape_val.funcall("text", ()).unwrap();
|
|
124
|
+
let style_val: Value = shape_val.funcall("style", ()).unwrap();
|
|
125
|
+
|
|
126
|
+
// Parse text - take first line if multiple
|
|
127
|
+
if let Ok(lines) = parse_text(text_val) {
|
|
128
|
+
let mut line = lines.into_iter().next().unwrap_or_default();
|
|
129
|
+
|
|
130
|
+
// Apply style if provided
|
|
131
|
+
if !style_val.is_nil() {
|
|
132
|
+
if let Ok(style) = parse_style(style_val) {
|
|
133
|
+
line = line.style(style);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ctx.print(x, y, line);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
_ => {}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
Widget::render(canvas, area, buffer);
|
|
146
|
+
Ok(())
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#[cfg(test)]
|
|
150
|
+
mod tests {
|
|
151
|
+
use super::*;
|
|
152
|
+
use ratatui::buffer::Buffer;
|
|
153
|
+
use ratatui::layout::Rect;
|
|
154
|
+
use ratatui::widgets::Widget;
|
|
155
|
+
|
|
156
|
+
#[test]
|
|
157
|
+
fn test_canvas_rendering() {
|
|
158
|
+
let canvas = Canvas::default()
|
|
159
|
+
.x_bounds([0.0, 10.0])
|
|
160
|
+
.y_bounds([0.0, 10.0])
|
|
161
|
+
.marker(Marker::Braille)
|
|
162
|
+
.paint(|ctx| {
|
|
163
|
+
ctx.draw(&Line {
|
|
164
|
+
x1: 0.0,
|
|
165
|
+
y1: 0.0,
|
|
166
|
+
x2: 10.0,
|
|
167
|
+
y2: 10.0,
|
|
168
|
+
color: ratatui::style::Color::Red,
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 5));
|
|
172
|
+
canvas.render(Rect::new(0, 0, 5, 5), &mut buf);
|
|
173
|
+
|
|
174
|
+
let mut found_braille = false;
|
|
175
|
+
for cell in buf.content() {
|
|
176
|
+
if !cell.symbol().trim().is_empty() {
|
|
177
|
+
found_braille = true;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
assert!(found_braille, "Canvas should render Braille characters");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
use crate::rendering::render_node;
|
|
5
|
+
use magnus::{prelude::*, Error, Value};
|
|
6
|
+
use ratatui::{
|
|
7
|
+
buffer::Buffer,
|
|
8
|
+
layout::{Constraint, Direction, Layout, Rect},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
pub fn render(buffer: &mut Buffer, area: Rect, node: Value) -> Result<(), Error> {
|
|
12
|
+
let child: Value = node.funcall("child", ())?;
|
|
13
|
+
let width_percent: u16 = node.funcall("width_percent", ())?;
|
|
14
|
+
let height_percent: u16 = node.funcall("height_percent", ())?;
|
|
15
|
+
|
|
16
|
+
let popup_layout = Layout::default()
|
|
17
|
+
.direction(Direction::Vertical)
|
|
18
|
+
.constraints([
|
|
19
|
+
Constraint::Percentage((100 - height_percent) / 2),
|
|
20
|
+
Constraint::Percentage(height_percent),
|
|
21
|
+
Constraint::Percentage((100 - height_percent) / 2),
|
|
22
|
+
])
|
|
23
|
+
.split(area);
|
|
24
|
+
|
|
25
|
+
let vertical_center_area = popup_layout[1];
|
|
26
|
+
|
|
27
|
+
let popup_layout_horizontal = Layout::default()
|
|
28
|
+
.direction(Direction::Horizontal)
|
|
29
|
+
.constraints([
|
|
30
|
+
Constraint::Percentage((100 - width_percent) / 2),
|
|
31
|
+
Constraint::Percentage(width_percent),
|
|
32
|
+
Constraint::Percentage((100 - width_percent) / 2),
|
|
33
|
+
])
|
|
34
|
+
.split(vertical_center_area);
|
|
35
|
+
|
|
36
|
+
let center_area = popup_layout_horizontal[1];
|
|
37
|
+
|
|
38
|
+
render_node(buffer, center_area, child)?;
|
|
39
|
+
Ok(())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[cfg(test)]
|
|
43
|
+
mod tests {
|
|
44
|
+
|
|
45
|
+
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
|
46
|
+
|
|
47
|
+
#[test]
|
|
48
|
+
fn test_center_logic() {
|
|
49
|
+
let area = Rect::new(0, 0, 100, 100);
|
|
50
|
+
let width_percent = 50;
|
|
51
|
+
let height_percent = 50;
|
|
52
|
+
|
|
53
|
+
let popup_layout = Layout::default()
|
|
54
|
+
.direction(Direction::Vertical)
|
|
55
|
+
.constraints([
|
|
56
|
+
Constraint::Percentage((100 - height_percent) / 2),
|
|
57
|
+
Constraint::Percentage(height_percent),
|
|
58
|
+
Constraint::Percentage((100 - height_percent) / 2),
|
|
59
|
+
])
|
|
60
|
+
.split(area);
|
|
61
|
+
let vertical_center = popup_layout[1];
|
|
62
|
+
|
|
63
|
+
// Vertical check
|
|
64
|
+
assert_eq!(vertical_center.height, 50);
|
|
65
|
+
|
|
66
|
+
let popup_layout_horizontal = Layout::default()
|
|
67
|
+
.direction(Direction::Horizontal)
|
|
68
|
+
.constraints([
|
|
69
|
+
Constraint::Percentage((100 - width_percent) / 2),
|
|
70
|
+
Constraint::Percentage(width_percent),
|
|
71
|
+
Constraint::Percentage((100 - width_percent) / 2),
|
|
72
|
+
])
|
|
73
|
+
.split(vertical_center);
|
|
74
|
+
let center = popup_layout_horizontal[1];
|
|
75
|
+
|
|
76
|
+
// Horizontal check
|
|
77
|
+
assert_eq!(center.width, 50);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
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::style::{parse_block, parse_style};
|
|
6
|
+
use crate::text::parse_line;
|
|
7
|
+
use bumpalo::Bump;
|
|
8
|
+
use magnus::{prelude::*, Error, Symbol, Value};
|
|
9
|
+
use ratatui::buffer::Buffer;
|
|
10
|
+
use ratatui::{
|
|
11
|
+
layout::Rect,
|
|
12
|
+
symbols,
|
|
13
|
+
widgets::{Axis, Chart, Dataset, GraphType, LegendPosition, Widget},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
#[allow(clippy::too_many_lines)]
|
|
17
|
+
pub fn render(buffer: &mut Buffer, area: Rect, node: Value) -> Result<(), Error> {
|
|
18
|
+
let bump = Bump::new();
|
|
19
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
20
|
+
|
|
21
|
+
let datasets_val: magnus::RArray = node.funcall("datasets", ())?;
|
|
22
|
+
let x_axis_val: Value = node.funcall("x_axis", ())?;
|
|
23
|
+
let y_axis_val: Value = node.funcall("y_axis", ())?;
|
|
24
|
+
let block_val: Value = node.funcall("block", ())?;
|
|
25
|
+
let style_val: Value = node.funcall("style", ())?;
|
|
26
|
+
let legend_position_val: Value = node.funcall("legend_position", ())?;
|
|
27
|
+
let hidden_legend_constraints_val: Value = node.funcall("hidden_legend_constraints", ())?;
|
|
28
|
+
|
|
29
|
+
let mut datasets = Vec::new();
|
|
30
|
+
// We need to keep the data alive until the chart is rendered
|
|
31
|
+
let mut data_storage: Vec<Vec<(f64, f64)>> = Vec::new();
|
|
32
|
+
|
|
33
|
+
for i in 0..datasets_val.len() {
|
|
34
|
+
let index = isize::try_from(i)
|
|
35
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
36
|
+
let ds_val: Value = datasets_val.entry(index)?;
|
|
37
|
+
let data_array: magnus::RArray = ds_val.funcall("data", ())?;
|
|
38
|
+
|
|
39
|
+
let mut points = Vec::new();
|
|
40
|
+
for j in 0..data_array.len() {
|
|
41
|
+
let index = isize::try_from(j)
|
|
42
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
43
|
+
let point_array_val: Value = data_array.entry(index)?;
|
|
44
|
+
let point_array = magnus::RArray::from_value(point_array_val).ok_or_else(|| {
|
|
45
|
+
type_error_with_context(&ruby, "expected array for point", point_array_val)
|
|
46
|
+
})?;
|
|
47
|
+
let x: f64 = point_array.entry(0)?;
|
|
48
|
+
let y: f64 = point_array.entry(1)?;
|
|
49
|
+
points.push((x, y));
|
|
50
|
+
}
|
|
51
|
+
data_storage.push(points);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (i, points) in data_storage.iter().enumerate() {
|
|
55
|
+
let index = isize::try_from(i)
|
|
56
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
57
|
+
let ds_val: Value = datasets_val.entry(index)?;
|
|
58
|
+
let name_val: Value = ds_val.funcall("name", ())?;
|
|
59
|
+
let name = if let Ok(line) = parse_line(name_val) {
|
|
60
|
+
line
|
|
61
|
+
} else {
|
|
62
|
+
let name_str: String = name_val.funcall("to_s", ())?;
|
|
63
|
+
ratatui::text::Line::from(name_str)
|
|
64
|
+
};
|
|
65
|
+
let marker_sym: Symbol = ds_val.funcall("marker", ())?;
|
|
66
|
+
let graph_type_sym: Symbol = ds_val.funcall("graph_type", ())?;
|
|
67
|
+
|
|
68
|
+
let marker = match marker_sym.to_string().as_str() {
|
|
69
|
+
"braille" => symbols::Marker::Braille,
|
|
70
|
+
"block" => symbols::Marker::Block,
|
|
71
|
+
"bar" => symbols::Marker::Bar,
|
|
72
|
+
"half_block" => symbols::Marker::HalfBlock,
|
|
73
|
+
_ => symbols::Marker::Dot,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let graph_type = match graph_type_sym.to_string().as_str() {
|
|
77
|
+
"scatter" => GraphType::Scatter,
|
|
78
|
+
_ => GraphType::Line,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
let mut ds_style = ratatui::style::Style::default();
|
|
82
|
+
let style_val: Value = ds_val.funcall("style", ())?;
|
|
83
|
+
if !style_val.is_nil() {
|
|
84
|
+
ds_style = parse_style(style_val)?;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let ds = Dataset::default()
|
|
88
|
+
.name(name)
|
|
89
|
+
.marker(marker)
|
|
90
|
+
.graph_type(graph_type)
|
|
91
|
+
.style(ds_style)
|
|
92
|
+
.data(points);
|
|
93
|
+
datasets.push(ds);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let x_axis = parse_axis(x_axis_val)?;
|
|
97
|
+
let y_axis = parse_axis(y_axis_val)?;
|
|
98
|
+
|
|
99
|
+
let mut chart = Chart::new(datasets).x_axis(x_axis).y_axis(y_axis);
|
|
100
|
+
|
|
101
|
+
if !block_val.is_nil() {
|
|
102
|
+
chart = chart.block(parse_block(block_val, &bump)?);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if !style_val.is_nil() {
|
|
106
|
+
chart = chart.style(parse_style(style_val)?);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if !legend_position_val.is_nil() {
|
|
110
|
+
let pos_sym: Symbol = legend_position_val.funcall("to_sym", ())?;
|
|
111
|
+
let pos = match pos_sym.to_string().as_str() {
|
|
112
|
+
"top_left" => LegendPosition::TopLeft,
|
|
113
|
+
"top" => LegendPosition::Top,
|
|
114
|
+
"bottom_left" => LegendPosition::BottomLeft,
|
|
115
|
+
"bottom" => LegendPosition::Bottom,
|
|
116
|
+
"bottom_right" => LegendPosition::BottomRight,
|
|
117
|
+
"left" => LegendPosition::Left,
|
|
118
|
+
"right" => LegendPosition::Right,
|
|
119
|
+
_ => LegendPosition::TopRight,
|
|
120
|
+
};
|
|
121
|
+
chart = chart.legend_position(Some(pos));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if !hidden_legend_constraints_val.is_nil() {
|
|
125
|
+
let constraints_array: magnus::RArray =
|
|
126
|
+
hidden_legend_constraints_val.funcall("to_a", ())?;
|
|
127
|
+
if constraints_array.len() == 2 {
|
|
128
|
+
let width_val: Value = constraints_array.entry(0)?;
|
|
129
|
+
let height_val: Value = constraints_array.entry(1)?;
|
|
130
|
+
let width_constraint = super::layout::parse_constraint(width_val)?;
|
|
131
|
+
let height_constraint = super::layout::parse_constraint(height_val)?;
|
|
132
|
+
chart = chart.hidden_legend_constraints((width_constraint, height_constraint));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
Widget::render(chart, area, buffer);
|
|
137
|
+
Ok(())
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fn parse_axis(axis_val: Value) -> Result<Axis<'static>, Error> {
|
|
141
|
+
let ruby = magnus::Ruby::get().unwrap();
|
|
142
|
+
let title_val: Value = axis_val.funcall("title", ())?;
|
|
143
|
+
let title = if title_val.is_nil() {
|
|
144
|
+
ratatui::text::Line::from("")
|
|
145
|
+
} else if let Ok(line) = parse_line(title_val) {
|
|
146
|
+
line
|
|
147
|
+
} else {
|
|
148
|
+
let title_str: String = title_val.funcall("to_s", ())?;
|
|
149
|
+
ratatui::text::Line::from(title_str)
|
|
150
|
+
};
|
|
151
|
+
let bounds_val: magnus::RArray = axis_val.funcall("bounds", ())?;
|
|
152
|
+
let labels_val: magnus::RArray = axis_val.funcall("labels", ())?;
|
|
153
|
+
let style_val: Value = axis_val.funcall("style", ())?;
|
|
154
|
+
let labels_alignment_val: Value = axis_val.funcall("labels_alignment", ())?;
|
|
155
|
+
|
|
156
|
+
let bounds: [f64; 2] = [bounds_val.entry(0)?, bounds_val.entry(1)?];
|
|
157
|
+
|
|
158
|
+
let mut labels = Vec::new();
|
|
159
|
+
for i in 0..labels_val.len() {
|
|
160
|
+
let index = isize::try_from(i)
|
|
161
|
+
.map_err(|e| Error::new(ruby.exception_range_error(), e.to_string()))?;
|
|
162
|
+
let entry_val: Value = labels_val.entry(index)?;
|
|
163
|
+
let label = if let Ok(line) = parse_line(entry_val) {
|
|
164
|
+
line
|
|
165
|
+
} else {
|
|
166
|
+
let label_str: String = entry_val.funcall("to_s", ())?;
|
|
167
|
+
ratatui::text::Line::from(label_str)
|
|
168
|
+
};
|
|
169
|
+
labels.push(label);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let mut axis = Axis::default().title(title).bounds(bounds).labels(labels);
|
|
173
|
+
|
|
174
|
+
if !style_val.is_nil() {
|
|
175
|
+
axis = axis.style(parse_style(style_val)?);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if !labels_alignment_val.is_nil() {
|
|
179
|
+
let alignment_sym: Symbol = labels_alignment_val.funcall("to_sym", ())?;
|
|
180
|
+
let alignment = match alignment_sym.to_string().as_str() {
|
|
181
|
+
"left" => ratatui::layout::HorizontalAlignment::Left,
|
|
182
|
+
"right" => ratatui::layout::HorizontalAlignment::Right,
|
|
183
|
+
_ => ratatui::layout::HorizontalAlignment::Center,
|
|
184
|
+
};
|
|
185
|
+
axis = axis.labels_alignment(alignment);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
Ok(axis)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[cfg(test)]
|
|
192
|
+
mod tests {
|
|
193
|
+
use super::*;
|
|
194
|
+
use ratatui::buffer::Buffer;
|
|
195
|
+
use ratatui::widgets::Widget;
|
|
196
|
+
|
|
197
|
+
#[test]
|
|
198
|
+
fn test_chart_rendering() {
|
|
199
|
+
let data = vec![(0.0, 0.0), (1.0, 1.0)];
|
|
200
|
+
let datasets = vec![Dataset::default().name("TestDS").data(&data)];
|
|
201
|
+
let chart = Chart::new(datasets)
|
|
202
|
+
.x_axis(
|
|
203
|
+
Axis::default()
|
|
204
|
+
.bounds([0.0, 1.0])
|
|
205
|
+
.labels(vec!["XMIN".into(), "XMAX".into()] as Vec<ratatui::text::Line>),
|
|
206
|
+
)
|
|
207
|
+
.y_axis(
|
|
208
|
+
Axis::default()
|
|
209
|
+
.bounds([0.0, 1.0])
|
|
210
|
+
.labels(vec!["YMIN".into(), "YMAX".into()] as Vec<ratatui::text::Line>),
|
|
211
|
+
);
|
|
212
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 40, 20));
|
|
213
|
+
chart.render(Rect::new(0, 0, 40, 20), &mut buf);
|
|
214
|
+
assert!(buf.content().iter().any(|c| c.symbol() != " "));
|
|
215
|
+
let content = buf.content().iter().map(|c| c.symbol()).collect::<String>();
|
|
216
|
+
assert!(content.contains("XMIN"));
|
|
217
|
+
assert!(content.contains("XMAX"));
|
|
218
|
+
assert!(content.contains("YMIN"));
|
|
219
|
+
assert!(content.contains("YMAX"));
|
|
220
|
+
assert!(content.contains("TestDS"));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
use bumpalo::Bump;
|
|
5
|
+
use magnus::{prelude::*, Error, Value};
|
|
6
|
+
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
|
7
|
+
|
|
8
|
+
pub fn render(buffer: &mut Buffer, area: Rect, node: Value) -> Result<(), Error> {
|
|
9
|
+
ratatui::widgets::Clear.render(area, buffer);
|
|
10
|
+
|
|
11
|
+
// If a block is provided, render it on top of the cleared area
|
|
12
|
+
if let Ok(block_val) = node.funcall::<_, _, Value>("block", ()) {
|
|
13
|
+
if !block_val.is_nil() {
|
|
14
|
+
let bump = Bump::new();
|
|
15
|
+
let block = crate::style::parse_block(block_val, &bump)?;
|
|
16
|
+
block.render(area, buffer);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Ok(())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[cfg(test)]
|
|
24
|
+
mod tests {
|
|
25
|
+
use ratatui::{backend::TestBackend, layout::Rect, widgets::Widget, Terminal};
|
|
26
|
+
|
|
27
|
+
#[test]
|
|
28
|
+
fn test_clear_renders_without_error() {
|
|
29
|
+
let backend = TestBackend::new(10, 5);
|
|
30
|
+
let mut terminal = Terminal::new(backend).unwrap();
|
|
31
|
+
|
|
32
|
+
terminal
|
|
33
|
+
.draw(|frame| {
|
|
34
|
+
let area = Rect::new(0, 0, 10, 5);
|
|
35
|
+
ratatui::widgets::Clear.render(area, frame.buffer_mut());
|
|
36
|
+
})
|
|
37
|
+
.unwrap();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
use magnus::{Error, Value};
|
|
5
|
+
use ratatui::{buffer::Buffer, layout::Rect};
|
|
6
|
+
|
|
7
|
+
/// Cursor widget requires Frame for `set_cursor_position` - cannot be rendered to buffer alone
|
|
8
|
+
/// This is a no-op when rendering to buffer directly (e.g., in `insert_before`)
|
|
9
|
+
/// For Frame-based rendering, use `frame.set_cursor_position()` directly
|
|
10
|
+
#[allow(dead_code, clippy::unnecessary_wraps)]
|
|
11
|
+
pub fn render(_buffer: &mut Buffer, _area: Rect, _node: Value) -> Result<(), Error> {
|
|
12
|
+
// Cursor positioning requires Frame.set_cursor_position(), not Buffer
|
|
13
|
+
// This is intentionally a no-op for buffer-only rendering
|
|
14
|
+
// The Frame wrapper in frame.rs should handle Cursor widgets specially
|
|
15
|
+
Ok(())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[cfg(test)]
|
|
19
|
+
mod tests {
|
|
20
|
+
use ratatui::layout::Rect;
|
|
21
|
+
|
|
22
|
+
#[test]
|
|
23
|
+
fn test_cursor_math() {
|
|
24
|
+
let area = Rect::new(10, 10, 50, 50);
|
|
25
|
+
let x = 5;
|
|
26
|
+
let y = 5;
|
|
27
|
+
let abs_x = area.x + x;
|
|
28
|
+
let abs_y = area.y + y;
|
|
29
|
+
assert_eq!(abs_x, 15);
|
|
30
|
+
assert_eq!(abs_y, 15);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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::Gauge, 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 gauge_style_val: Value = node.funcall("gauge_style", ())?;
|
|
17
|
+
let block_val: Value = node.funcall("block", ())?;
|
|
18
|
+
let use_unicode: bool = node.funcall("use_unicode", ())?;
|
|
19
|
+
let mut gauge = Gauge::default().ratio(ratio).use_unicode(use_unicode);
|
|
20
|
+
|
|
21
|
+
if !label_val.is_nil() {
|
|
22
|
+
// Try to parse as a Span (rich text)
|
|
23
|
+
if let Ok(span) = parse_span(label_val) {
|
|
24
|
+
gauge = gauge.label(span);
|
|
25
|
+
} else {
|
|
26
|
+
// Fallback to string
|
|
27
|
+
let label_str: String = label_val.funcall("to_s", ())?;
|
|
28
|
+
gauge = gauge.label(label_str);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if !style_val.is_nil() {
|
|
33
|
+
gauge = gauge.style(parse_style(style_val)?);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if !gauge_style_val.is_nil() {
|
|
37
|
+
gauge = gauge.gauge_style(parse_style(gauge_style_val)?);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if !block_val.is_nil() {
|
|
41
|
+
gauge = gauge.block(parse_block(block_val, &bump)?);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
gauge.render(area, buffer);
|
|
45
|
+
Ok(())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#[cfg(test)]
|
|
49
|
+
mod tests {
|
|
50
|
+
use super::*;
|
|
51
|
+
use ratatui::buffer::Buffer;
|
|
52
|
+
use ratatui::widgets::{Gauge, Widget};
|
|
53
|
+
|
|
54
|
+
#[test]
|
|
55
|
+
fn test_gauge_rendering() {
|
|
56
|
+
let gauge = Gauge::default().ratio(0.5).label("50%");
|
|
57
|
+
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 1));
|
|
58
|
+
gauge.render(Rect::new(0, 0, 10, 1), &mut buf);
|
|
59
|
+
// Gauge renders block characters
|
|
60
|
+
assert!(buf.content().iter().any(|c| c.symbol() != " "));
|
|
61
|
+
// Should contain label
|
|
62
|
+
assert!(buf.content().iter().any(|c| c.symbol() == "5"));
|
|
63
|
+
assert!(buf.content().iter().any(|c| c.symbol() == "%"));
|
|
64
|
+
}
|
|
65
|
+
}
|