Skip to content

Commit c51ac3c

Browse files
authored
chore!: Refactor popup (#49)
These changes were done to relax the dependency on ratatui directly, instead aiming to depend on ratatui-core. To do this requires avoiding the unstable widget-ref feature. This is a preparatory change to support that effort (if it's possible and aligns to the ratatui modularization work going on in 0.30.0 (currently pointing at the alpha.0 release). BREAKING CHANGE: `SizedWidgetRef` is now `KnownSize`. It no longer requires you to implement `WidgetRef` (this requirement is on the popup now instead of in the trait). It also no longer requires you to implement Debug. `SizedWrapper` is now `KnownSizeWrapper` The popup no longer implements `WidgetRef` and `StatefulWidgetRef` directly, instead implements `{Stateful,}Widget for &Popup` --- - **refactor: Move sized widget code to modules** - **feat: impl SizeWidgetRef for String** - **feat: impl StatefulWidget instead of _Ref** - impl StatefulWidget for Popup and &Popup - add tests - add PartialEq for testing Popups - add bacon setup for unit tests - remove --color always from bacon.toml - **feat: impl Widget for Popup** impl Widget for &Popup and Popup Instead of WidgetRef (ratatui now has a blanket impl of WidgetRef for this. Remove the need for Widgets contained in the popup to implement debug. - **chore: test SizedWrapper, add setters and new()** - **feat!: Rename SizedWidgetRef to KnownSize** The trait no longer requires implementors to also implement WidgetRef as that requirement is moved to the Popup generic bounds. As a result, this means that a better name was needed. Sized is probably the right name for this, except there is a well known trait in rust with the same name and this would be confusing. Instead I went with `KnownSize`. BREAKING CHANGE: SizedWidgetRef is now named `KnownSize` and no longer requires implementing WidgetRef. - **feat: Rename SizedWrapper to KnownSizeWrapper.** - **refactor: remove unnecessary generic bounds** - **feat: implement KnownSize trait for Text** refactor popup rendering code, examples,
1 parent ebb8c96 commit c51ac3c

File tree

12 files changed

+419
-279
lines changed

12 files changed

+419
-279
lines changed

.github/workflows/check.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ jobs:
1111
checks: write
1212
uses: joshka/github-workflows/.github/workflows/rust-check.yml@main
1313
with:
14-
msrv: 1.74.0
14+
msrv: 1.80.0

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
[workspace]
22
resolver = "2"
33
members = ["tui-*"]
4+
default-members = ["tui-*"]
45

56
[workspace.package]
67
authors = ["Joshka"]
78
license = "MIT OR Apache-2.0"
89
repository = "https://github.com/joshka/tui-widgets"
910
edition = "2021"
10-
rust-version = "1.74.0"
11+
rust-version = "1.80.0"
1112
categories = ["command-line-interface", "gui"]
1213
keywords = ["cli", "console", "ratatui", "terminal", "tui"]
1314

@@ -23,6 +24,7 @@ futures = "0.3.31"
2324
itertools = "0.13.0"
2425
indoc = "2.0.5"
2526
lipsum = "0.9.1"
27+
pretty_assertions = "1.4.1"
2628
ratatui = { version = "0.30.0-alpha.0", default-features = false }
2729
ratatui-core = { version = "0.1.0-alpha.0" }
2830
ratatui-macros = "0.7.0-alpha.0"

bacon.toml

+11-49
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,23 @@
88
default_job = "check"
99

1010
[jobs.check]
11-
command = ["cargo", "check", "--workspace", "--color", "always"]
11+
command = ["cargo", "check", "--workspace"]
1212
need_stdout = false
1313

1414
[jobs.check-all]
15-
command = [
16-
"cargo",
17-
"check",
18-
"--workspace",
19-
"--all-targets",
20-
"--color",
21-
"always",
22-
]
15+
command = ["cargo", "check", "--workspace", "--all-targets", "--all-features"]
2316
need_stdout = false
2417

2518
[jobs.clippy]
26-
command = [
27-
"cargo",
28-
"clippy",
29-
"--workspace",
30-
"--all-features",
31-
"--all-targets",
32-
"--color",
33-
"always",
34-
]
19+
command = ["cargo", "clippy", "--workspace", "--all-features", "--all-targets"]
3520
need_stdout = false
3621

3722
[jobs.test]
38-
command = [
39-
"cargo",
40-
"test",
41-
"--workspace",
42-
"--color",
43-
"always",
44-
"--",
45-
"--color",
46-
"always", # see https://github.com/Canop/bacon/issues/124
47-
]
23+
command = ["cargo", "test", "--workspace"]
24+
need_stdout = true
25+
26+
[jobs.test-unit]
27+
command = ["cargo", "test", "--workspace", "--lib"]
4828
need_stdout = true
4929

5030
[jobs.doc]
@@ -55,8 +35,6 @@ command = [
5535
"-Zunstable-options",
5636
"-Zrustdoc-scrape-examples",
5737
"--all-features",
58-
"--color",
59-
"always",
6038
"--no-deps",
6139
]
6240
need_stdout = false
@@ -71,8 +49,6 @@ command = [
7149
"-Zunstable-options",
7250
"-Zrustdoc-scrape-examples",
7351
"--all-features",
74-
"--color",
75-
"always",
7652
"--no-deps",
7753
"--open",
7854
]
@@ -84,27 +60,12 @@ on_success = "job:doc" # so that we don't open the browser at each change
8460
# way. Don't forget the `--color always` part or the errors won't be
8561
# properly parsed.
8662
[jobs.run]
87-
command = [
88-
"cargo",
89-
"run",
90-
"--color",
91-
"always",
92-
# put launch parameters for your program behind a `--` separator
93-
]
63+
command = ["cargo", "run"]
9464
need_stdout = true
9565
allow_warnings = true
9666

9767
[jobs.coverage]
98-
command = [
99-
"cargo",
100-
"llvm-cov",
101-
"--workspace",
102-
"--lcov",
103-
"--output-path",
104-
"target/lcov.info",
105-
"--color",
106-
"always",
107-
]
68+
command = ["cargo", "llvm-cov", "--workspace", "--lcov", "--output-path", "target/lcov.info"]
10869

10970
[jobs.format]
11071
command = ["cargo", "+nightly", "fmt", "--", "--check"]
@@ -128,3 +89,4 @@ shift-r = "job:rdme"
12889
f = "job:format"
12990
o = "job:coverage"
13091
v = "job:vhs"
92+
u = "job:test-unit"

tui-popup/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ derive_setters.workspace = true
3131
ratatui = { workspace = true, features = ["unstable-widget-ref"] }
3232

3333
[dev-dependencies]
34+
pretty_assertions.workspace = true
3435
color-eyre.workspace = true
3536
lipsum.workspace = true
3637

tui-popup/examples/paragraph.rs

+35-34
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,57 @@ use ratatui::{
66
widgets::{Paragraph, Wrap},
77
Frame,
88
};
9-
use tui_popup::{Popup, SizedWrapper};
10-
11-
mod terminal;
9+
use tui_popup::{KnownSizeWrapper, Popup};
1210

1311
fn main() -> Result<()> {
14-
let mut terminal = terminal::init()?;
15-
let mut app = App::default();
16-
while !app.should_exit {
17-
terminal.draw(|frame| app.render(frame))?;
18-
app.handle_events()?;
19-
}
20-
terminal::restore()?;
21-
Ok(())
12+
color_eyre::install()?;
13+
let terminal = ratatui::init();
14+
let result = App::default().run(terminal);
15+
ratatui::restore();
16+
result
2217
}
2318

2419
#[derive(Default)]
2520
struct App {
2621
should_exit: bool,
22+
lorem_ipsum: String,
2723
scroll: u16,
2824
}
2925

3026
impl App {
27+
fn run(&mut self, mut terminal: ratatui::DefaultTerminal) -> Result<()> {
28+
self.lorem_ipsum = lipsum(2000);
29+
while !self.should_exit {
30+
terminal.draw(|frame| self.render(frame))?;
31+
self.handle_events()?;
32+
}
33+
Ok(())
34+
}
35+
3136
fn render(&self, frame: &mut Frame) {
3237
let area = frame.area();
33-
let background = background(area);
38+
self.render_background(frame, area);
39+
self.render_popup(frame);
40+
}
3441

35-
let paragraph = paragraph(self.scroll);
36-
let popup = Popup::new(paragraph)
42+
fn render_background(&self, frame: &mut Frame, area: Rect) {
43+
let text = Text::raw(&self.lorem_ipsum);
44+
let paragraph = Paragraph::new(text).wrap(Wrap { trim: false }).dark_gray();
45+
frame.render_widget(paragraph, area);
46+
}
47+
48+
fn render_popup(&self, frame: &mut Frame) {
49+
let lines: Text = (0..10).map(|i| Span::raw(format!("Line {i}"))).collect();
50+
let paragraph = Paragraph::new(lines).scroll((self.scroll, 0));
51+
let wrapper = KnownSizeWrapper {
52+
inner: &paragraph,
53+
width: 21,
54+
height: 5,
55+
};
56+
let popup = Popup::new(wrapper)
3757
.title("scroll: ↑/↓ quit: Esc")
3858
.style(Style::new().white().on_blue());
39-
40-
frame.render_widget(background, area);
41-
frame.render_widget(&popup, area);
59+
frame.render_widget(popup, frame.area());
4260
}
4361

4462
fn handle_events(&mut self) -> Result<()> {
@@ -61,20 +79,3 @@ impl App {
6179
self.scroll = self.scroll.saturating_add(1);
6280
}
6381
}
64-
65-
fn paragraph(scroll: u16) -> SizedWrapper<Paragraph<'static>> {
66-
let lines: Text = (0..10).map(|i| Span::raw(format!("Line {i}"))).collect();
67-
let paragraph = Paragraph::new(lines).scroll((scroll, 0));
68-
SizedWrapper {
69-
inner: paragraph,
70-
width: 21,
71-
height: 5,
72-
}
73-
}
74-
75-
fn background(area: Rect) -> Paragraph<'static> {
76-
let lorem_ipsum = lipsum(area.area() as usize / 5);
77-
Paragraph::new(lorem_ipsum)
78-
.wrap(Wrap { trim: false })
79-
.dark_gray()
80-
}

tui-popup/examples/popup.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ use ratatui::{
88
};
99
use tui_popup::Popup;
1010

11-
mod terminal;
12-
1311
fn main() -> Result<()> {
14-
let mut terminal = terminal::init()?;
12+
color_eyre::install()?;
13+
let mut terminal = ratatui::init();
14+
let result = run(&mut terminal);
15+
ratatui::restore();
16+
result
17+
}
18+
19+
fn run(terminal: &mut ratatui::DefaultTerminal) -> Result<()> {
1520
loop {
16-
terminal.draw(render)?;
17-
if read_any_key()? {
18-
break;
21+
terminal.draw(|frame| {
22+
render(frame);
23+
})?;
24+
if matches!(event::read()?, Event::Key(_)) {
25+
break Ok(());
1926
}
2027
}
21-
terminal::restore()?;
22-
Ok(())
2328
}
2429

2530
fn render(frame: &mut Frame) {
@@ -32,11 +37,6 @@ fn render(frame: &mut Frame) {
3237
frame.render_widget(&popup, area);
3338
}
3439

35-
fn read_any_key() -> Result<bool> {
36-
let event = event::read()?;
37-
Ok(matches!(event, Event::Key(_)))
38-
}
39-
4040
fn background(area: Rect) -> Paragraph<'static> {
4141
let lorem_ipsum = lipsum(area.area() as usize / 5);
4242
Paragraph::new(lorem_ipsum)

0 commit comments

Comments
 (0)