Skip to content

Instantly share code, notes, and snippets.

@sophacles
Last active April 5, 2023 13:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sophacles/bd7722ffdaca99b7531054676c6c092b to your computer and use it in GitHub Desktop.
Save sophacles/bd7722ffdaca99b7531054676c6c092b to your computer and use it in GitHub Desktop.
block splitting
[package]
name = "nested_blocks"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ratatui = "0.20.1"
crossterm = "0.23.2"
use crossterm::{
event::{self, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{
backend::{Backend, CrosstermBackend},
layout::{Constraint, Direction, Layout},
style::{Color, Style},
widgets::{Block, Borders},
Frame, Terminal,
};
fn main() {
// panic handler to always restore terminal
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic| {
let _ = crossterm::terminal::disable_raw_mode();
let _ = crossterm::execute!(std::io::stdout(), LeaveAlternateScreen);
original_hook(panic);
}));
enable_raw_mode().unwrap();
let mut stdout = std::io::stdout();
execute!(stdout, EnterAlternateScreen).unwrap();
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend).unwrap();
loop {
let _ = terminal.draw(|f| draw(f));
if let Event::Key(key) = event::read().unwrap() {
#[allow(clippy::single_match)]
match key.code {
KeyCode::Char(_) => {
break;
}
_ => {}
};
}
}
disable_raw_mode().unwrap();
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
terminal.show_cursor().unwrap();
}
fn draw<B: Backend>(frame: &mut Frame<B>) {
// start with the area of the terminal window (the frame)
let app_area = frame.size();
// split vertically
// +--------------------------------+
// | ignore 20% |
// +--------------------------------+
// | keep this |
// | 60% |
// +--------------------------------+
// | ignore 20% |
// +--------------------------------+
//
// vmid_area is an area, just like the frame size
let vmid_area = Layout::default()
.direction(Direction::Vertical)
.margin(0)
.constraints(
[
Constraint::Percentage(20),
Constraint::Percentage(60),
Constraint::Percentage(20),
]
.as_ref(),
)
.split(app_area)[1];
// split horizontally in the kept area
// The overall splits look like this
// +--------------------------------+
// | ignore 20% |
// +------+----------------+--------+
// | ign | keep this | ign |
// | | 60% | |
// +------+----------------+--------+
// | ignore 20% |
// +--------------------------------+
let vh_mid_area = Layout::default()
.direction(Direction::Horizontal)
.margin(0)
.constraints(
[
Constraint::Percentage(25),
Constraint::Percentage(50),
Constraint::Percentage(25),
]
.as_ref(),
)
.split(vmid_area)[1];
// now create the outer Block.
let container = Block::default()
.borders(Borders::ALL)
.title("outer block")
.style(Style::default().bg(Color::Yellow));
// get the area inside the container block. To calculate this, the area that the block
// will be rendered to is required as an argument.
let inside_container = container.inner(vh_mid_area);
// Split the inside area into 2 chunks, left and right.
let (left_inside, right_inside) = {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.margin(0)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(inside_container);
(chunks[0], chunks[1])
};
// create the left block with a red background
let block_left = Block::default().style(Style::default().bg(Color::Red));
// create the right block with a blue background
let block_right = Block::default().style(Style::default().bg(Color::Blue));
// draw everything.
frame.render_widget(container, vh_mid_area);
frame.render_widget(block_left, left_inside);
frame.render_widget(block_right, right_inside);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment