Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1350c2f

Browse files
mattaByron
authored andcommitted
Replace tui_react::Terminal with tui::Terminal
This is an experiment in converting a program that used the Terminal provided by tui-react into a program that uses the Terminal provided by ratatui. My hypothesis was that the two were not significantly different from eachother, and this seems to be true in this case. I found that `Widget` trait used by ratatui's `Terminal` is sufficiently powerful to pass arbitrary mutable state to rendering code. A relatively simple `FunctionWidget` trampoline is used to achieve this. The tui_react dependency is retained. The code still calls some tui_react utility functions and uses its List widget instead of the ratatui List widget. These utilities do not depend on the tui_react Terminal and could, in theory, be split out of tui-react into a separate crate.
1 parent e3aff9d commit 1350c2f

8 files changed

Lines changed: 83 additions & 44 deletions

File tree

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md", "!**/t
1111

1212
[features]
1313
default = ["tui-crossplatform", "trash-move"]
14-
tui-crossplatform = ["crosstermion/tui-react-crossterm", "tui", "tui-react", "open", "unicode-segmentation", "unicode-width"]
15-
14+
tui-crossplatform = ["crosstermion/tui-crossterm", "tui", "tui-react", "open", "unicode-segmentation", "unicode-width"]
1615
trash-move = ["trash"]
1716

1817
[dependencies]

src/interactive/app/eventloop.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use dua::{
1414
WalkResult,
1515
};
1616
use std::path::PathBuf;
17-
use tui::backend::Backend;
18-
use tui_react::Terminal;
17+
use tui::{backend::Backend, buffer::Buffer, layout::Rect, widgets::Widget, Terminal};
1918

2019
use super::state::{AppState, Cursor};
2120
use super::tree_view::TreeView;
@@ -569,6 +568,55 @@ enum Refresh {
569568
Selected,
570569
}
571570

571+
/// A [`Widget`] that renders by calling a function.
572+
///
573+
/// The `FunctionWidget` struct holds a function that renders into a portion of
574+
/// a [`Buffer`] designated by a [`Rect`].
575+
///
576+
/// This widget can be used to create custom UI elements that are defined by a
577+
/// rendering function. and allows for rendering functions that do not implement
578+
/// the [`Widget`] trait.
579+
struct FunctionWidget<F>
580+
where
581+
F: FnOnce(Rect, &mut Buffer),
582+
{
583+
render: F,
584+
}
585+
586+
impl<F> FunctionWidget<F>
587+
where
588+
F: FnOnce(Rect, &mut Buffer),
589+
{
590+
/// Creates a new [`FunctionWidget`] with the given rendering function.
591+
///
592+
/// The rendering function must have the signature `FnOnce(Rect, &mut
593+
/// Buffer)`, where:
594+
/// - [`Rect`] represents the available space for rendering.
595+
/// - [`Buffer`] is the buffer to write the rendered content to.
596+
///
597+
/// The `FunctionWidget` can then be used to render the provided function in
598+
/// a user interface.
599+
fn new(function: F) -> FunctionWidget<F>
600+
where
601+
F: FnOnce(Rect, &mut Buffer),
602+
{
603+
FunctionWidget { render: function }
604+
}
605+
}
606+
607+
/// Implements the [`Widget`] trait for [`FunctionWidget`].
608+
///
609+
/// The implementation simply calls the provided render function with the given
610+
/// `Rect` and `Buffer`.
611+
impl<F> Widget for FunctionWidget<F>
612+
where
613+
F: FnOnce(Rect, &mut Buffer),
614+
{
615+
fn render(self, area: Rect, buf: &mut Buffer) {
616+
(self.render)(area, buf);
617+
}
618+
}
619+
572620
pub fn draw_window<B>(
573621
window: &mut MainWindow,
574622
props: MainWindowProps<'_>,
@@ -578,10 +626,14 @@ pub fn draw_window<B>(
578626
where
579627
B: Backend,
580628
{
581-
let area = terminal.pre_render()?;
582-
window.render(props, area, terminal, cursor);
583-
terminal.post_render()?;
584-
629+
terminal.draw(|frame| {
630+
frame.render_widget(
631+
FunctionWidget::new(|area, buf| {
632+
window.render(props, area, buf, cursor);
633+
}),
634+
frame.size(),
635+
);
636+
})?;
585637
Ok(())
586638
}
587639

src/interactive/app/handlers.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use crate::interactive::{
66
use crosstermion::input::Key;
77
use dua::traverse::TreeIndex;
88
use std::{fs, io, path::PathBuf};
9-
use tui::backend::Backend;
10-
use tui_react::Terminal;
9+
use tui::{backend::Backend, Terminal};
1110

1211
use super::state::{AppState, FocussedPane::*};
1312

src/interactive/app/terminal.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ use dua::{
88
traverse::{Traversal, TraversalStats},
99
ByteFormat, WalkOptions, WalkResult,
1010
};
11-
use tui::prelude::Backend;
12-
use tui_react::Terminal;
11+
use tui::{backend::Backend, Terminal};
1312

1413
use crate::interactive::widgets::MainWindow;
1514

src/interactive/app/tests/utils.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ use std::{
1616
io::ErrorKind,
1717
path::{Path, PathBuf},
1818
};
19-
use tui::backend::TestBackend;
20-
use tui_react::Terminal;
19+
use tui::{backend::TestBackend, Terminal};
2120

2221
use crate::interactive::{app::tests::FIXTURE_PATH, terminal::TerminalApp};
2322

src/interactive/widgets/glob.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ use crosstermion::input::Key;
55
use dua::traverse::{Tree, TreeIndex};
66
use petgraph::Direction;
77
use std::borrow::Borrow;
8-
use tui::backend::Backend;
9-
use tui::prelude::Buffer;
108
use tui::{
9+
buffer::Buffer,
1110
layout::Rect,
1211
style::Style,
1312
text::{Line, Span, Text},
1413
widgets::{Block, Borders, Paragraph, Widget},
1514
};
16-
use tui_react::util::{block_width, rect};
17-
use tui_react::{draw_text_nowrap_fn, Terminal};
15+
use tui_react::{
16+
draw_text_nowrap_fn,
17+
util::{block_width, rect},
18+
};
1819
use unicode_segmentation::UnicodeSegmentation;
1920
use unicode_width::UnicodeWidthStr;
2021

@@ -100,15 +101,13 @@ impl GlobPane {
100101
new_cursor_pos.clamp(0, self.input.graphemes(true).count())
101102
}
102103

103-
pub fn render<B>(
104+
pub fn render(
104105
&mut self,
105106
props: impl Borrow<GlobPaneProps>,
106107
area: Rect,
107-
terminal: &mut Terminal<B>,
108+
buffer: &mut Buffer,
108109
cursor: &mut Cursor,
109-
) where
110-
B: Backend,
111-
{
110+
) {
112111
let GlobPaneProps {
113112
border_style,
114113
has_focus,
@@ -120,18 +119,15 @@ impl GlobPane {
120119
.border_style(*border_style)
121120
.borders(Borders::ALL);
122121
let inner_block_area = block.inner(area);
123-
block.render(area, terminal.current_buffer_mut());
122+
block.render(area, buffer);
124123

125124
let spans = vec![Span::from(&self.input)];
126125
Paragraph::new(Text::from(Line::from(spans)))
127126
.style(Style::default())
128-
.render(
129-
margin_left_right(inner_block_area, 1),
130-
terminal.current_buffer_mut(),
131-
);
127+
.render(margin_left_right(inner_block_area, 1), buffer);
132128

133129
if *has_focus {
134-
draw_top_right_help(area, title, terminal.current_buffer_mut());
130+
draw_top_right_help(area, title, buffer);
135131

136132
cursor.show = true;
137133
cursor.x = inner_block_area.x

src/interactive/widgets/main.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ use crate::interactive::{
77
DisplayOptions,
88
};
99
use std::borrow::Borrow;
10-
use tui::backend::Backend;
10+
use tui::buffer::Buffer;
1111
use tui::{
1212
layout::{Constraint, Direction, Layout, Rect},
1313
style::Modifier,
1414
style::{Color, Style},
1515
};
16-
use tui_react::Terminal;
1716
use Constraint::*;
1817
use FocussedPane::*;
1918

@@ -36,15 +35,13 @@ pub struct MainWindow {
3635
}
3736

3837
impl MainWindow {
39-
pub fn render<'a, B>(
38+
pub fn render<'a>(
4039
&mut self,
4140
props: impl Borrow<MainWindowProps<'a>>,
4241
area: Rect,
43-
terminal: &mut Terminal<B>,
42+
buffer: &mut Buffer,
4443
cursor: &mut Cursor,
45-
) where
46-
B: Backend,
47-
{
44+
) {
4845
let MainWindowProps {
4946
current_path,
5047
entries_traversed,
@@ -59,7 +56,7 @@ impl MainWindow {
5956
let (header_area, content_area, footer_area) = main_window_layout(area);
6057

6158
let header_bg_color = header_background_color(self.is_anything_marked(), state.focussed);
62-
Header.render(header_bg_color, header_area, terminal.current_buffer_mut());
59+
Header.render(header_bg_color, header_area, buffer);
6360

6461
let (entries_area, help_pane, mark_pane) = {
6562
let (left_pane, right_pane) = content_layout(content_area);
@@ -90,15 +87,15 @@ impl MainWindow {
9087
border_style: mark_style,
9188
format: display.byte_format,
9289
};
93-
pane.render(props, mark_area, terminal.current_buffer_mut());
90+
pane.render(props, mark_area, buffer);
9491
}
9592

9693
if let Some((help_area, pane)) = help_pane {
9794
let props = HelpPaneProps {
9895
border_style: help_style,
9996
has_focus: matches!(state.focussed, Help),
10097
};
101-
pane.render(props, help_area, terminal.current_buffer_mut());
98+
pane.render(props, help_area, buffer);
10299
}
103100

104101
let marked = self.mark_pane.as_ref().map(|p| p.marked());
@@ -113,15 +110,14 @@ impl MainWindow {
113110
sort_mode: state.sorting,
114111
show_columns: &state.show_columns,
115112
};
116-
self.entries_pane
117-
.render(props, entries_area, terminal.current_buffer_mut());
113+
self.entries_pane.render(props, entries_area, buffer);
118114

119115
if let Some((glob_area, pane)) = glob_pane {
120116
let props = GlobPaneProps {
121117
border_style: glob_style,
122118
has_focus: matches!(state.focussed, Glob),
123119
};
124-
pane.render(props, glob_area, terminal, cursor);
120+
pane.render(props, glob_area, buffer, cursor);
125121
}
126122

127123
Footer.render(
@@ -135,7 +131,7 @@ impl MainWindow {
135131
sort_mode: state.sorting,
136132
},
137133
footer_area,
138-
terminal.current_buffer_mut(),
134+
buffer,
139135
);
140136
}
141137

0 commit comments

Comments
 (0)