Skip to content

Commit a3c1ace

Browse files
committed
feat: implement KnownSize trait for Text
refactor popup rendering code, examples,
1 parent a6c08e9 commit a3c1ace

File tree

8 files changed

+145
-168
lines changed

8 files changed

+145
-168
lines changed

Diff for: bacon.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ command = ["cargo", "check", "--workspace"]
1212
need_stdout = false
1313

1414
[jobs.check-all]
15-
command = ["cargo", "check", "--workspace", "--all-targets"]
15+
command = ["cargo", "check", "--workspace", "--all-targets", "--all-features"]
1616
need_stdout = false
1717

1818
[jobs.clippy]
@@ -25,6 +25,7 @@ need_stdout = true
2525

2626
[jobs.test-unit]
2727
command = ["cargo", "test", "--workspace", "--lib"]
28+
need_stdout = true
2829

2930
[jobs.doc]
3031
command = [

Diff for: tui-popup/examples/paragraph.rs

+34-33
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,55 @@ use ratatui::{
88
};
99
use tui_popup::{KnownSizeWrapper, Popup};
1010

11-
mod terminal;
12-
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) -> KnownSizeWrapper<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-
KnownSizeWrapper {
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-
}

Diff for: 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)

Diff for: tui-popup/examples/state.rs

+70-75
Original file line numberDiff line numberDiff line change
@@ -4,95 +4,90 @@ use ratatui::{
44
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind},
55
prelude::{Constraint, Frame, Layout, Rect, Style, Stylize, Text},
66
widgets::{Paragraph, Wrap},
7+
DefaultTerminal,
78
};
89
use tui_popup::{Popup, PopupState};
910

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

23-
#[derive(Default)]
24-
struct App {
25-
popup: PopupState,
26-
should_exit: bool,
19+
fn run(mut terminal: DefaultTerminal) -> Result<()> {
20+
let mut state = PopupState::default();
21+
let mut exit = false;
22+
while !exit {
23+
terminal.draw(|frame| draw(frame, &mut state))?;
24+
handle_events(&mut state, &mut exit)?;
25+
}
26+
Ok(())
2727
}
2828

29-
impl App {
30-
fn render(&mut self, frame: &mut Frame) {
31-
let [background_area, status_area] =
32-
Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(frame.area());
33-
34-
let background = Self::background(background_area);
35-
frame.render_widget(background, background_area);
29+
fn draw(frame: &mut Frame, state: &mut PopupState) {
30+
let vertical = Layout::vertical([Constraint::Min(0), Constraint::Length(1)]);
31+
let [background_area, status_area] = vertical.areas(frame.area());
3632

37-
let popup = Self::popup_widget();
38-
frame.render_stateful_widget_ref(popup, background_area, &mut self.popup);
39-
40-
// must be called after rendering the popup widget as it relies on the popup area being set
41-
let status_bar = self.status_bar();
42-
frame.render_widget(status_bar, status_area);
43-
}
33+
render_background(frame, background_area);
34+
render_popup(frame, background_area, state);
35+
render_status_bar(frame, status_area, state);
36+
}
4437

45-
fn background(area: Rect) -> Paragraph<'static> {
46-
let lorem_ipsum = lipsum(area.area() as usize / 5);
47-
Paragraph::new(lorem_ipsum)
48-
.wrap(Wrap { trim: false })
49-
.dark_gray()
50-
}
38+
fn render_background(frame: &mut Frame, area: Rect) {
39+
let lorem_ipsum = lipsum(area.area() as usize / 5);
40+
let background = Paragraph::new(lorem_ipsum)
41+
.wrap(Wrap { trim: false })
42+
.dark_gray();
43+
frame.render_widget(background, area);
44+
}
5145

52-
fn popup_widget() -> Popup<'static, Text<'static>> {
53-
Popup::new(Text::from_iter([
54-
"q: exit",
55-
"r: reset",
56-
"j: move down",
57-
"k: move up",
58-
"h: move left",
59-
"l: move right",
60-
]))
46+
fn render_popup(frame: &mut Frame, area: Rect, state: &mut PopupState) {
47+
let body = Text::from_iter([
48+
"q: exit",
49+
"r: reset",
50+
"j: move down",
51+
"k: move up",
52+
"h: move left",
53+
"l: move right",
54+
]);
55+
let popup = Popup::new(&body)
6156
.title("Popup")
62-
.style(Style::new().white().on_blue())
63-
}
64-
65-
/// Status bar at the bottom of the screen
66-
///
67-
/// Must be called after rendering the popup widget as it relies on the popup area being set
68-
fn status_bar(&self) -> Paragraph<'static> {
69-
let popup_area = self.popup.area().unwrap_or_default();
70-
let text = format!("Popup area: {popup_area:?}");
71-
Paragraph::new(text).style(Style::new().white().on_black())
72-
}
57+
.style(Style::new().white().on_blue());
58+
frame.render_stateful_widget(popup, area, state);
59+
}
7360

74-
fn handle_events(&mut self) -> Result<()> {
75-
match event::read()? {
76-
Event::Key(event) => self.handle_key_event(event),
77-
Event::Mouse(event) => self.popup.handle_mouse_event(event),
78-
_ => (),
79-
};
80-
Ok(())
81-
}
61+
/// Status bar at the bottom of the screen
62+
///
63+
/// Must be called after rendering the popup widget as it relies on the popup area being set
64+
fn render_status_bar(frame: &mut Frame, area: Rect, state: &mut PopupState) {
65+
let popup_area = state.area().unwrap_or_default();
66+
let text = format!("Popup area: {popup_area:?}");
67+
let paragraph = Paragraph::new(text).style(Style::new().white().on_black());
68+
frame.render_widget(paragraph, area);
69+
}
8270

83-
fn handle_key_event(&mut self, event: KeyEvent) {
84-
if event.kind != KeyEventKind::Press {
85-
return;
86-
}
87-
match event.code {
88-
KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true,
89-
KeyCode::Char('r') => self.popup = PopupState::default(),
90-
// TODO: move handling to PopupState (e.g. move_up, move_down, etc. or move(Move:Up))
91-
KeyCode::Char('j') | KeyCode::Down => self.popup.move_by(0, 1),
92-
KeyCode::Char('k') | KeyCode::Up => self.popup.move_by(0, -1),
93-
KeyCode::Char('h') | KeyCode::Left => self.popup.move_by(-1, 0),
94-
KeyCode::Char('l') | KeyCode::Right => self.popup.move_by(1, 0),
95-
_ => {}
71+
fn handle_events(popup: &mut PopupState, exit: &mut bool) -> Result<()> {
72+
match event::read()? {
73+
Event::Key(event) if event.kind == KeyEventKind::Press => {
74+
handle_key_event(event, popup, exit)
9675
}
76+
Event::Mouse(event) => popup.handle_mouse_event(event),
77+
_ => (),
78+
};
79+
Ok(())
80+
}
81+
82+
fn handle_key_event(event: KeyEvent, popup: &mut PopupState, exit: &mut bool) {
83+
match event.code {
84+
KeyCode::Char('q') | KeyCode::Esc => *exit = true,
85+
KeyCode::Char('r') => *popup = PopupState::default(),
86+
// TODO: move handling to PopupState (e.g. move_up, move_down, etc. or move(Move:Up))
87+
KeyCode::Char('j') | KeyCode::Down => popup.move_by(0, 1),
88+
KeyCode::Char('k') | KeyCode::Up => popup.move_by(0, -1),
89+
KeyCode::Char('h') | KeyCode::Left => popup.move_by(-1, 0),
90+
KeyCode::Char('l') | KeyCode::Right => popup.move_by(1, 0),
91+
_ => {}
9792
}
9893
}

Diff for: tui-popup/examples/terminal/mod.rs

-40
This file was deleted.

Diff for: tui-popup/src/known_size.rs

+10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ pub trait KnownSize {
1414
fn height(&self) -> usize;
1515
}
1616

17+
impl KnownSize for Text<'_> {
18+
fn width(&self) -> usize {
19+
self.width()
20+
}
21+
22+
fn height(&self) -> usize {
23+
self.height()
24+
}
25+
}
26+
1727
impl KnownSize for &Text<'_> {
1828
fn width(&self) -> usize {
1929
Text::width(self)

Diff for: tui-popup/src/known_size_wrapper.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ impl<W: WidgetRef> WidgetRef for KnownSizeWrapper<W> {
2323
}
2424
}
2525

26-
impl<W: WidgetRef> KnownSize for KnownSizeWrapper<W> {
26+
impl<W> KnownSize for KnownSizeWrapper<W> {
27+
fn width(&self) -> usize {
28+
self.width
29+
}
30+
31+
fn height(&self) -> usize {
32+
self.height
33+
}
34+
}
35+
36+
impl<W> KnownSize for &KnownSizeWrapper<W> {
2737
fn width(&self) -> usize {
2838
self.width
2939
}

0 commit comments

Comments
 (0)