diff --git a/Cargo.toml b/Cargo.toml index 338397d..a8c0636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,11 @@ name = "layout" path = "examples/layout.rs" required-features = ["termion"] +[[example]] +name = "popup" +path = "examples/popup.rs" +required-features = ["termion"] + [[example]] name = "block" path = "examples/block.rs" diff --git a/examples/popup.rs b/examples/popup.rs new file mode 100644 index 0000000..3c12979 --- /dev/null +++ b/examples/popup.rs @@ -0,0 +1,111 @@ +#[allow(dead_code)] +mod util; + +use crate::util::event::{Event, Events}; +use std::{error::Error, io}; +use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; +use tui::layout::Rect; +use tui::widgets::Clear; +use tui::{ + backend::TermionBackend, + layout::{Alignment, Constraint, Direction, Layout}, + style::{Color, Modifier, Style}, + widgets::{Block, Borders, Paragraph, Text}, + Terminal, +}; + +/// helper function to create a centered rect using up +/// certain percentage of the available rect `r` +fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints( + [ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ] + .as_ref(), + ) + .split(r); + + Layout::default() + .direction(Direction::Horizontal) + .constraints( + [ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ] + .as_ref(), + ) + .split(popup_layout[1])[1] +} + +fn main() -> Result<(), Box> { + // Terminal initialization + let stdout = io::stdout().into_raw_mode()?; + let stdout = MouseTerminal::from(stdout); + let stdout = AlternateScreen::from(stdout); + let backend = TermionBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + terminal.hide_cursor()?; + + let events = Events::new(); + + loop { + terminal.draw(|mut f| { + let size = f.size(); + + let chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .split(size); + + let s = "Veeeeeeeeeeeeeeeery loooooooooooooooooong striiiiiiiiiiiiiiiiiiiiiiiiiing. "; + let mut long_line = s.repeat(usize::from(size.width)*usize::from(size.height)/300); + long_line.push('\n'); + + let text = [ + Text::raw("This is a line \n"), + Text::styled("This is a line \n", Style::default().fg(Color::Red)), + Text::styled("This is a line\n", Style::default().bg(Color::Blue)), + Text::styled( + "This is a longer line\n", + Style::default().modifier(Modifier::CROSSED_OUT), + ), + Text::styled(&long_line, Style::default().bg(Color::Green)), + Text::styled( + "This is a line\n", + Style::default().fg(Color::Green).modifier(Modifier::ITALIC), + ), + ]; + + let paragraph = Paragraph::new(text.iter()) + .block(Block::default().title("Left Block").borders(Borders::ALL)) + .alignment(Alignment::Left).wrap(true); + f.render_widget(paragraph, chunks[0]); + + let paragraph = Paragraph::new(text.iter()) + .block(Block::default().title("Right Block").borders(Borders::ALL)) + .alignment(Alignment::Left).wrap(true); + f.render_widget(paragraph, chunks[1]); + + let block = Block::default().title("Popup").borders(Borders::ALL); + let area = centered_rect(60, 20, size); + f.render_widget(Clear, area); //this clears out the background + f.render_widget(block, area); + })?; + + match events.next()? { + Event::Input(input) => { + if let Key::Char('q') = input { + break; + } + } + _ => {} + } + } + + Ok(()) +} diff --git a/src/widgets/clear.rs b/src/widgets/clear.rs new file mode 100644 index 0000000..82a7a90 --- /dev/null +++ b/src/widgets/clear.rs @@ -0,0 +1,35 @@ +use crate::buffer::Buffer; +use crate::layout::Rect; +use crate::widgets::Widget; + +/// A widget to to clear/reset a certain area to allow overdrawing (e.g. for popups) +/// +/// # Examples +/// +/// ``` +/// # use tui::widgets::{Clear, Block, Borders}; +/// # use tui::layout::Rect; +/// # use tui::Frame; +/// # use tui::backend::Backend; +/// fn draw_on_clear(f: &mut Frame, area: Rect) { +/// let block = Block::default().title("Block").borders(Borders::ALL); +/// f.render_widget(Clear, area); // <- this will clear/reset the area first +/// f.render_widget(block, area); // now render the block widget +/// } +/// ``` +/// +/// # Popup Example +/// +/// For a more complete example how to utilize `Clear` to realize popups see +/// the example `examples/popup.rs` +pub struct Clear; + +impl Widget for Clear { + fn render(self, area: Rect, buf: &mut Buffer) { + for x in area.left()..area.right() { + for y in area.top()..area.bottom() { + buf.get_mut(x, y).reset(); + } + } + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index aa161c3..98b6fcc 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -5,6 +5,7 @@ mod barchart; mod block; pub mod canvas; mod chart; +mod clear; mod gauge; mod list; mod paragraph; @@ -16,6 +17,7 @@ mod tabs; pub use self::barchart::BarChart; pub use self::block::{Block, BorderType}; pub use self::chart::{Axis, Chart, Dataset, GraphType, Marker}; +pub use self::clear::Clear; pub use self::gauge::Gauge; pub use self::list::{List, ListState}; pub use self::paragraph::Paragraph;