Skip to content

Commit

Permalink
feat: implement KnownSize trait for Text
Browse files Browse the repository at this point in the history
refactor popup rendering code, examples,
  • Loading branch information
joshka committed Nov 28, 2024
1 parent a6c08e9 commit a3c1ace
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 168 deletions.
3 changes: 2 additions & 1 deletion bacon.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ command = ["cargo", "check", "--workspace"]
need_stdout = false

[jobs.check-all]
command = ["cargo", "check", "--workspace", "--all-targets"]
command = ["cargo", "check", "--workspace", "--all-targets", "--all-features"]
need_stdout = false

[jobs.clippy]
Expand All @@ -25,6 +25,7 @@ need_stdout = true

[jobs.test-unit]
command = ["cargo", "test", "--workspace", "--lib"]
need_stdout = true

[jobs.doc]
command = [
Expand Down
67 changes: 34 additions & 33 deletions tui-popup/examples/paragraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,55 @@ use ratatui::{
};
use tui_popup::{KnownSizeWrapper, Popup};

mod terminal;

fn main() -> Result<()> {
let mut terminal = terminal::init()?;
let mut app = App::default();
while !app.should_exit {
terminal.draw(|frame| app.render(frame))?;
app.handle_events()?;
}
terminal::restore()?;
Ok(())
color_eyre::install()?;
let terminal = ratatui::init();
let result = App::default().run(terminal);
ratatui::restore();
result
}

#[derive(Default)]
struct App {
should_exit: bool,
lorem_ipsum: String,
scroll: u16,
}

impl App {
fn run(&mut self, mut terminal: ratatui::DefaultTerminal) -> Result<()> {
self.lorem_ipsum = lipsum(2000);
while !self.should_exit {
terminal.draw(|frame| self.render(frame))?;
self.handle_events()?;
}
Ok(())
}

fn render(&self, frame: &mut Frame) {
let area = frame.area();
let background = background(area);
self.render_background(frame, area);
self.render_popup(frame);
}

let paragraph = paragraph(self.scroll);
let popup = Popup::new(paragraph)
fn render_background(&self, frame: &mut Frame, area: Rect) {
let text = Text::raw(&self.lorem_ipsum);
let paragraph = Paragraph::new(text).wrap(Wrap { trim: false }).dark_gray();
frame.render_widget(paragraph, area);
}

fn render_popup(&self, frame: &mut Frame) {
let lines: Text = (0..10).map(|i| Span::raw(format!("Line {i}"))).collect();
let paragraph = Paragraph::new(lines).scroll((self.scroll, 0));
let wrapper = KnownSizeWrapper {
inner: &paragraph,
width: 21,
height: 5,
};
let popup = Popup::new(wrapper)
.title("scroll: ↑/↓ quit: Esc")
.style(Style::new().white().on_blue());

frame.render_widget(background, area);
frame.render_widget(&popup, area);
frame.render_widget(popup, frame.area());
}

fn handle_events(&mut self) -> Result<()> {
Expand All @@ -61,20 +79,3 @@ impl App {
self.scroll = self.scroll.saturating_add(1);
}
}

fn paragraph(scroll: u16) -> KnownSizeWrapper<Paragraph<'static>> {
let lines: Text = (0..10).map(|i| Span::raw(format!("Line {i}"))).collect();
let paragraph = Paragraph::new(lines).scroll((scroll, 0));
KnownSizeWrapper {
inner: paragraph,
width: 21,
height: 5,
}
}

fn background(area: Rect) -> Paragraph<'static> {
let lorem_ipsum = lipsum(area.area() as usize / 5);
Paragraph::new(lorem_ipsum)
.wrap(Wrap { trim: false })
.dark_gray()
}
26 changes: 13 additions & 13 deletions tui-popup/examples/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@ use ratatui::{
};
use tui_popup::Popup;

mod terminal;

fn main() -> Result<()> {
let mut terminal = terminal::init()?;
color_eyre::install()?;
let mut terminal = ratatui::init();
let result = run(&mut terminal);
ratatui::restore();
result
}

fn run(terminal: &mut ratatui::DefaultTerminal) -> Result<()> {
loop {
terminal.draw(render)?;
if read_any_key()? {
break;
terminal.draw(|frame| {
render(frame);
})?;
if matches!(event::read()?, Event::Key(_)) {
break Ok(());
}
}
terminal::restore()?;
Ok(())
}

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

fn read_any_key() -> Result<bool> {
let event = event::read()?;
Ok(matches!(event, Event::Key(_)))
}

fn background(area: Rect) -> Paragraph<'static> {
let lorem_ipsum = lipsum(area.area() as usize / 5);
Paragraph::new(lorem_ipsum)
Expand Down
145 changes: 70 additions & 75 deletions tui-popup/examples/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,95 +4,90 @@ use ratatui::{
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind},
prelude::{Constraint, Frame, Layout, Rect, Style, Stylize, Text},
widgets::{Paragraph, Wrap},
DefaultTerminal,
};
use tui_popup::{Popup, PopupState};

mod terminal;

fn main() -> Result<()> {
let mut terminal = terminal::init()?;
let mut app = App::default();
while !app.should_exit {
terminal.draw(|frame| app.render(frame))?;
app.handle_events()?;
}
terminal::restore()?;
Ok(())
color_eyre::install()?;
let terminal = ratatui::init();
let result = run(terminal);
ratatui::restore();
result
}

#[derive(Default)]
struct App {
popup: PopupState,
should_exit: bool,
fn run(mut terminal: DefaultTerminal) -> Result<()> {
let mut state = PopupState::default();
let mut exit = false;
while !exit {
terminal.draw(|frame| draw(frame, &mut state))?;
handle_events(&mut state, &mut exit)?;
}
Ok(())
}

impl App {
fn render(&mut self, frame: &mut Frame) {
let [background_area, status_area] =
Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(frame.area());

let background = Self::background(background_area);
frame.render_widget(background, background_area);
fn draw(frame: &mut Frame, state: &mut PopupState) {
let vertical = Layout::vertical([Constraint::Min(0), Constraint::Length(1)]);
let [background_area, status_area] = vertical.areas(frame.area());

let popup = Self::popup_widget();
frame.render_stateful_widget_ref(popup, background_area, &mut self.popup);

// must be called after rendering the popup widget as it relies on the popup area being set
let status_bar = self.status_bar();
frame.render_widget(status_bar, status_area);
}
render_background(frame, background_area);
render_popup(frame, background_area, state);
render_status_bar(frame, status_area, state);
}

fn background(area: Rect) -> Paragraph<'static> {
let lorem_ipsum = lipsum(area.area() as usize / 5);
Paragraph::new(lorem_ipsum)
.wrap(Wrap { trim: false })
.dark_gray()
}
fn render_background(frame: &mut Frame, area: Rect) {
let lorem_ipsum = lipsum(area.area() as usize / 5);
let background = Paragraph::new(lorem_ipsum)
.wrap(Wrap { trim: false })
.dark_gray();
frame.render_widget(background, area);
}

fn popup_widget() -> Popup<'static, Text<'static>> {
Popup::new(Text::from_iter([
"q: exit",
"r: reset",
"j: move down",
"k: move up",
"h: move left",
"l: move right",
]))
fn render_popup(frame: &mut Frame, area: Rect, state: &mut PopupState) {
let body = Text::from_iter([
"q: exit",
"r: reset",
"j: move down",
"k: move up",
"h: move left",
"l: move right",
]);
let popup = Popup::new(&body)
.title("Popup")
.style(Style::new().white().on_blue())
}

/// Status bar at the bottom of the screen
///
/// Must be called after rendering the popup widget as it relies on the popup area being set
fn status_bar(&self) -> Paragraph<'static> {
let popup_area = self.popup.area().unwrap_or_default();
let text = format!("Popup area: {popup_area:?}");
Paragraph::new(text).style(Style::new().white().on_black())
}
.style(Style::new().white().on_blue());
frame.render_stateful_widget(popup, area, state);
}

fn handle_events(&mut self) -> Result<()> {
match event::read()? {
Event::Key(event) => self.handle_key_event(event),
Event::Mouse(event) => self.popup.handle_mouse_event(event),
_ => (),
};
Ok(())
}
/// Status bar at the bottom of the screen
///
/// Must be called after rendering the popup widget as it relies on the popup area being set
fn render_status_bar(frame: &mut Frame, area: Rect, state: &mut PopupState) {
let popup_area = state.area().unwrap_or_default();
let text = format!("Popup area: {popup_area:?}");
let paragraph = Paragraph::new(text).style(Style::new().white().on_black());
frame.render_widget(paragraph, area);
}

fn handle_key_event(&mut self, event: KeyEvent) {
if event.kind != KeyEventKind::Press {
return;
}
match event.code {
KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true,
KeyCode::Char('r') => self.popup = PopupState::default(),
// TODO: move handling to PopupState (e.g. move_up, move_down, etc. or move(Move:Up))
KeyCode::Char('j') | KeyCode::Down => self.popup.move_by(0, 1),
KeyCode::Char('k') | KeyCode::Up => self.popup.move_by(0, -1),
KeyCode::Char('h') | KeyCode::Left => self.popup.move_by(-1, 0),
KeyCode::Char('l') | KeyCode::Right => self.popup.move_by(1, 0),
_ => {}
fn handle_events(popup: &mut PopupState, exit: &mut bool) -> Result<()> {
match event::read()? {
Event::Key(event) if event.kind == KeyEventKind::Press => {
handle_key_event(event, popup, exit)
}
Event::Mouse(event) => popup.handle_mouse_event(event),
_ => (),
};
Ok(())
}

fn handle_key_event(event: KeyEvent, popup: &mut PopupState, exit: &mut bool) {
match event.code {
KeyCode::Char('q') | KeyCode::Esc => *exit = true,
KeyCode::Char('r') => *popup = PopupState::default(),
// TODO: move handling to PopupState (e.g. move_up, move_down, etc. or move(Move:Up))
KeyCode::Char('j') | KeyCode::Down => popup.move_by(0, 1),
KeyCode::Char('k') | KeyCode::Up => popup.move_by(0, -1),
KeyCode::Char('h') | KeyCode::Left => popup.move_by(-1, 0),
KeyCode::Char('l') | KeyCode::Right => popup.move_by(1, 0),
_ => {}
}
}
40 changes: 0 additions & 40 deletions tui-popup/examples/terminal/mod.rs

This file was deleted.

10 changes: 10 additions & 0 deletions tui-popup/src/known_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ pub trait KnownSize {
fn height(&self) -> usize;
}

impl KnownSize for Text<'_> {
fn width(&self) -> usize {
self.width()
}

fn height(&self) -> usize {
self.height()
}
}

impl KnownSize for &Text<'_> {
fn width(&self) -> usize {
Text::width(self)
Expand Down
12 changes: 11 additions & 1 deletion tui-popup/src/known_size_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ impl<W: WidgetRef> WidgetRef for KnownSizeWrapper<W> {
}
}

impl<W: WidgetRef> KnownSize for KnownSizeWrapper<W> {
impl<W> KnownSize for KnownSizeWrapper<W> {
fn width(&self) -> usize {
self.width
}

fn height(&self) -> usize {
self.height
}
}

impl<W> KnownSize for &KnownSizeWrapper<W> {
fn width(&self) -> usize {
self.width
}
Expand Down
Loading

0 comments on commit a3c1ace

Please sign in to comment.