Initial commit
This commit is contained in:
commit
1c75b09d0d
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,58 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
|
||||
[[package]]
|
||||
name = "multiview"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"termion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall",
|
||||
"redox_termios",
|
||||
]
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "multiview"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
termion = "2.0.1"
|
|
@ -0,0 +1,205 @@
|
|||
use std::env;
|
||||
use std::io::{self, stdin, stdout, Write};
|
||||
|
||||
use termion::event::{Event, Key, MouseEvent};
|
||||
use termion::input::{MouseTerminal, TermRead};
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::IntoAlternateScreen;
|
||||
use termion::terminal_size;
|
||||
use termion::{clear, color, cursor, style};
|
||||
|
||||
/// A tile with a command running inside it.
|
||||
#[derive(Debug)]
|
||||
pub struct Tile {
|
||||
/// The command that should be executed in the tile.
|
||||
pub command: Vec<String>,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
/// Creates a new empty tile.
|
||||
pub fn new(command: &[String]) -> Tile {
|
||||
Tile {
|
||||
command: command
|
||||
.into_iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiple applications running in a single terminal.
|
||||
struct Multiview<W: Write> {
|
||||
/// The stdout on which the multiview will be rendererd.
|
||||
pub stdout: W,
|
||||
|
||||
/// The tiles of the multiview.
|
||||
pub tiles: Vec<Vec<Tile>>,
|
||||
|
||||
/// The coordinates of the selected tiles.
|
||||
pub selected: (u16, u16),
|
||||
}
|
||||
|
||||
impl<W: Write> Multiview<W> {
|
||||
/// Creates a new multiview.
|
||||
pub fn new(stdout: W, tiles: Vec<Vec<Tile>>) -> io::Result<Multiview<W>> {
|
||||
let mut multiview = Multiview {
|
||||
stdout,
|
||||
tiles,
|
||||
selected: (0, 0),
|
||||
};
|
||||
|
||||
write!(
|
||||
multiview.stdout,
|
||||
"{}{}{}┌",
|
||||
clear::All,
|
||||
cursor::Hide,
|
||||
cursor::Goto(1, 1)
|
||||
)?;
|
||||
|
||||
multiview.stdout.flush()?;
|
||||
|
||||
Ok(multiview)
|
||||
}
|
||||
|
||||
/// Helper to easily access a tile.
|
||||
pub fn tile(&self, (i, j): (u16, u16)) -> &Tile {
|
||||
&self.tiles[i as usize][j as usize]
|
||||
}
|
||||
|
||||
/// Sets the selected tile from (x, y) coordinates.
|
||||
pub fn select_tile(&mut self, (x, y): (u16, u16), term_size: (u16, u16)) {
|
||||
let w = term_size.0 / self.tiles[0].len() as u16;
|
||||
let h = term_size.1 / self.tiles.len() as u16;
|
||||
|
||||
self.selected = (y / h, x / w);
|
||||
}
|
||||
|
||||
/// Draws a box from (x1, y1) to (x2, y2).
|
||||
pub fn rect(&mut self, (x1, y1): (u16, u16), (x2, y2): (u16, u16)) -> io::Result<()> {
|
||||
write!(self.stdout, "{}┌", cursor::Goto(x1, y1))?;
|
||||
|
||||
for _ in (x1 + 1)..x2 {
|
||||
write!(self.stdout, "─")?;
|
||||
}
|
||||
|
||||
write!(self.stdout, "┐")?;
|
||||
|
||||
for y in (y1 + 1)..y2 {
|
||||
write!(self.stdout, "{}│", cursor::Goto(x1, y))?;
|
||||
write!(self.stdout, "{}│", cursor::Goto(x2, y))?;
|
||||
}
|
||||
|
||||
write!(self.stdout, "{}└", cursor::Goto(x1, y2))?;
|
||||
|
||||
for _ in (x1 + 1)..x2 {
|
||||
write!(self.stdout, "─")?;
|
||||
}
|
||||
|
||||
write!(self.stdout, "┘")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clears stdout.
|
||||
pub fn clear(&mut self) -> io::Result<()> {
|
||||
write!(self.stdout, "{}", clear::All)
|
||||
}
|
||||
|
||||
/// Renders the (x, y) tile.
|
||||
pub fn render_tile(&mut self, (i, j): (u16, u16), term_size: (u16, u16)) -> io::Result<()> {
|
||||
let w = term_size.0 / self.tiles[0].len() as u16;
|
||||
let h = term_size.1 / self.tiles.len() as u16;
|
||||
|
||||
let x1 = j * w + 1;
|
||||
let y1 = i * h + 1;
|
||||
|
||||
let x2 = (j + 1) * w;
|
||||
let y2 = (i + 1) * h;
|
||||
|
||||
if self.selected == (i, j) {
|
||||
write!(self.stdout, "{}", color::Green.fg_str())?;
|
||||
}
|
||||
self.rect((x1, y1), (x2, y2))?;
|
||||
write!(self.stdout, "{}├", cursor::Goto(x1, y1 + 2))?;
|
||||
|
||||
for _ in (x1 + 1)..x2 {
|
||||
write!(self.stdout, "─")?;
|
||||
}
|
||||
|
||||
write!(self.stdout, "{}┤", cursor::Goto(x2, y1 + 2))?;
|
||||
|
||||
let tile = &self.tile((i, j));
|
||||
|
||||
write!(
|
||||
self.stdout,
|
||||
"{}{} {}Command: {}{}",
|
||||
color::Reset.fg_str(),
|
||||
cursor::Goto(x1 + 1, y1 + 1),
|
||||
style::Bold,
|
||||
tile.command.join(" "),
|
||||
style::Reset,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders all the tiles of the multiview.
|
||||
pub fn render(&mut self, term_size: (u16, u16)) -> io::Result<()> {
|
||||
self.clear()?;
|
||||
|
||||
for i in 0..self.tiles.len() {
|
||||
for j in 0..self.tiles[0].len() {
|
||||
self.render_tile((i as u16, j as u16), term_size)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.stdout.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for Multiview<W> {
|
||||
fn drop(&mut self) {
|
||||
write!(self.stdout, "{}", cursor::Show).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the multiview application.
|
||||
pub fn main() -> io::Result<()> {
|
||||
let args = env::args().skip(1).collect::<Vec<_>>();
|
||||
|
||||
let tiles = args
|
||||
.split(|x| x == "//")
|
||||
.map(|x| x.split(|y| y == "::").map(Tile::new))
|
||||
.map(|x| x.collect::<Vec<_>>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let stdin = stdin();
|
||||
let stdout = stdout().into_raw_mode()?;
|
||||
let stdout = stdout.into_alternate_screen()?;
|
||||
let stdout = MouseTerminal::from(stdout);
|
||||
|
||||
let term_size = terminal_size()?;
|
||||
|
||||
let mut multiview = Multiview::new(stdout, tiles)?;
|
||||
multiview.render(term_size)?;
|
||||
|
||||
for c in stdin.events() {
|
||||
let evt = c?;
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
|
||||
Event::Mouse(me) => match me {
|
||||
MouseEvent::Press(_, x, y) => multiview.select_tile((x, y), term_size),
|
||||
_ => (),
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
multiview.render(term_size)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
fn main() {
|
||||
if let Err(e) = multiview::main() {
|
||||
eprintln!("An error occured: {}", e);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue