mirror of
https://github.com/SL-RU/mmtui.git
synced 2025-03-04 00:14:45 +01:00
Improve info field output
This commit is contained in:
parent
74de1049b5
commit
71760bd6ed
4 changed files with 175 additions and 109 deletions
|
@ -2,6 +2,10 @@
|
|||
name = "mmt"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Lutsai Aleksandr <s.lyra@ya.ru>"]
|
||||
description = "Terminal User Interface disk mount manager for TUI file managers"
|
||||
repository = "https://github.com/SL-RU/mmt"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
crossterm = { version = "0.28.1", features = ["event-stream"]}
|
||||
|
@ -15,9 +19,12 @@ udisks2 = "0.2.0"
|
|||
pedantic = { level = "warn", priority = -1 }
|
||||
must_use_candidate = { level = "allow" }
|
||||
expect_used = { level = "allow" }
|
||||
unwrap_used = { level = "warn" }
|
||||
unwrap_used = { level = "allow" }
|
||||
panic = { level = "allow" }
|
||||
doc_markdown = { level = "allow" }
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
[package.metadata.aur]
|
||||
depends = ["udisks2"]
|
124
src/main.rs
124
src/main.rs
|
@ -1,32 +1,23 @@
|
|||
mod drives;
|
||||
mod mountpoints;
|
||||
|
||||
use std::{io::stderr, sync::Arc, time::Duration};
|
||||
mod tui;
|
||||
|
||||
use crossterm::{
|
||||
event::{Event, EventStream, KeyCode, KeyEventKind},
|
||||
event::{Event, EventStream, KeyEventKind},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use ratatui::{
|
||||
layout::{Alignment, Constraint, Layout},
|
||||
prelude::CrosstermBackend,
|
||||
style::Color,
|
||||
text::{Line, Text},
|
||||
widgets::{
|
||||
Block, BorderType, Padding, Paragraph, Row, StatefulWidget, Table, TableState, Widget, Wrap,
|
||||
},
|
||||
Frame, Terminal,
|
||||
};
|
||||
use ratatui::{prelude::CrosstermBackend, Terminal};
|
||||
use std::{io::stderr, sync::Arc, time::Duration};
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_stream::StreamExt;
|
||||
use tui::{InputResult, Tui};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> udisks2::Result<()> {
|
||||
enable_raw_mode().unwrap();
|
||||
execute!(stderr(), EnterAlternateScreen).unwrap();
|
||||
let mut terminal = Terminal::new(CrosstermBackend::new(stderr())).unwrap();
|
||||
let mut ts = TableState::new();
|
||||
|
||||
let period = Duration::from_secs_f32(1.0 / 10.0);
|
||||
let mut interval = tokio::time::interval(period);
|
||||
|
@ -42,107 +33,34 @@ async fn main() -> udisks2::Result<()> {
|
|||
}
|
||||
});
|
||||
|
||||
let mut output = String::new();
|
||||
let mut last_status = String::new();
|
||||
|
||||
let mut output_change_path = String::new();
|
||||
let mut tui = Tui::default();
|
||||
loop {
|
||||
let drv = state.lock().await.clone();
|
||||
let mut selected: Option<drives::Block> = None;
|
||||
tui.drv = state.lock().await.to_vec();
|
||||
tui.selected = None;
|
||||
terminal
|
||||
.draw(|f| draw(f, &mut ts, &drv, &mut selected, &last_status))
|
||||
.draw(|f| tui.draw(f))
|
||||
.expect("failed to draw frame");
|
||||
|
||||
tokio::select! {
|
||||
_ = interval.tick() => { },
|
||||
Some(Ok(event)) = events.next() => {
|
||||
if let Event::Key(key) = event {
|
||||
let key = tokio::select! {
|
||||
_ = interval.tick() => continue,
|
||||
Some(Ok(Event::Key(key))) = events.next() => key,
|
||||
};
|
||||
|
||||
if key.kind == KeyEventKind::Press {
|
||||
match key.code {
|
||||
KeyCode::Up | KeyCode::Char('k') => ts.select_previous(),
|
||||
KeyCode::Down | KeyCode::Char('j') => ts.select_next(),
|
||||
KeyCode::Char('m') => if let Some(b) = &selected {
|
||||
last_status = format!("{:?}", drives::mount(b).await);
|
||||
}
|
||||
KeyCode::Char('u') => if let Some(b) = &selected {
|
||||
last_status = format!("{:?}", drives::unmount(b).await);
|
||||
}
|
||||
KeyCode::Esc | KeyCode::Char('q') => break,
|
||||
KeyCode::Enter => {
|
||||
output = selected.unwrap().mount.unwrap();
|
||||
match tui.input(key).await {
|
||||
InputResult::None => continue,
|
||||
InputResult::Quit => break,
|
||||
InputResult::QuitChangeDirectory(p) => {
|
||||
output_change_path = p;
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
disable_raw_mode().unwrap();
|
||||
execute!(stderr(), LeaveAlternateScreen).unwrap();
|
||||
println!("{output}");
|
||||
println!("{output_change_path}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(
|
||||
frame: &mut Frame,
|
||||
state: &mut TableState,
|
||||
drv: &[drives::Drive],
|
||||
selected: &mut Option<drives::Block>,
|
||||
last_status: &str,
|
||||
) {
|
||||
let text = Text::raw("Hello World!");
|
||||
frame.render_widget(text, frame.area());
|
||||
|
||||
let layout = Layout::default()
|
||||
.direction(ratatui::layout::Direction::Vertical)
|
||||
.constraints(vec![Constraint::Fill(1), Constraint::Length(5)])
|
||||
.split(frame.area());
|
||||
|
||||
let block = Block::bordered()
|
||||
.title(Line::styled("Mount", Color::White))
|
||||
.title_alignment(Alignment::Center)
|
||||
.padding(Padding::symmetric(1, 1))
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Color::Yellow);
|
||||
block.clone().render(layout[0], frame.buffer_mut());
|
||||
|
||||
let rows: Vec<drives::Block> = drv.iter().flat_map(|d| d.blocks.clone()).collect();
|
||||
|
||||
state
|
||||
.selected()
|
||||
.and_then(|n| rows.get(n).cloned())
|
||||
.clone_into(selected);
|
||||
|
||||
let rows = rows.iter().map(|i| {
|
||||
Row::new(vec![
|
||||
i.dev.clone(),
|
||||
i.label.clone(),
|
||||
i.mount.clone().unwrap_or_default(),
|
||||
if i.mounted {
|
||||
"M".to_owned()
|
||||
} else {
|
||||
"O".to_owned()
|
||||
},
|
||||
])
|
||||
});
|
||||
let widths = [
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Length(3),
|
||||
];
|
||||
let table = Table::new(rows, widths)
|
||||
.row_highlight_style(Color::Green)
|
||||
.highlight_symbol(">");
|
||||
|
||||
StatefulWidget::render(table, block.inner(frame.area()), frame.buffer_mut(), state);
|
||||
frame.render_widget(
|
||||
Paragraph::new(format!(
|
||||
"j - UP, k - DOWN, l - Goto mountpoint, m - Mount, u - Unmount, e - Eject\n{selected:?} {last_status:?}"
|
||||
))
|
||||
.wrap(Wrap { trim: true }),
|
||||
layout[1],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ impl MountPoint {
|
|||
Some(MountPoint {
|
||||
dev: parts
|
||||
.next()
|
||||
.and_then(|d| if !d.starts_with('#') { Some(d) } else { None })?
|
||||
.and_then(|d| if d.starts_with('#') { None } else { Some(d) })?
|
||||
.into(),
|
||||
path: Some(parts.next()?.to_string()),
|
||||
fs: parts.next()?.into(),
|
||||
|
|
141
src/tui.rs
Normal file
141
src/tui.rs
Normal file
|
@ -0,0 +1,141 @@
|
|||
use crate::drives;
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use ratatui::{
|
||||
layout::{Alignment, Constraint, Layout},
|
||||
style::Color,
|
||||
text::Line,
|
||||
widgets::{
|
||||
Block, BorderType, Padding, Paragraph, Row, StatefulWidget, Table, TableState, Widget, Wrap,
|
||||
},
|
||||
Frame,
|
||||
};
|
||||
|
||||
pub enum InputResult {
|
||||
None,
|
||||
Quit,
|
||||
QuitChangeDirectory(String),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Tui {
|
||||
ts: TableState,
|
||||
pub drv: Vec<drives::Drive>,
|
||||
pub selected: Option<drives::Block>,
|
||||
pub last_status: String,
|
||||
}
|
||||
|
||||
impl Tui {
|
||||
pub async fn input(&mut self, key: KeyEvent) -> InputResult {
|
||||
match key.code {
|
||||
KeyCode::Up | KeyCode::Char('k') => {
|
||||
self.ts.select_previous();
|
||||
InputResult::None
|
||||
}
|
||||
KeyCode::Down | KeyCode::Char('j') => {
|
||||
self.ts.select_next();
|
||||
InputResult::None
|
||||
}
|
||||
KeyCode::Char('m') => {
|
||||
if let Some(b) = &self.selected {
|
||||
self.last_status = format!("{:?}", drives::mount(b).await);
|
||||
}
|
||||
InputResult::None
|
||||
}
|
||||
KeyCode::Char('u') => {
|
||||
if let Some(b) = &self.selected {
|
||||
self.last_status = format!("{:?}", drives::unmount(b).await);
|
||||
}
|
||||
InputResult::None
|
||||
}
|
||||
KeyCode::Esc | KeyCode::Char('q') => InputResult::Quit,
|
||||
KeyCode::Enter => {
|
||||
let output = self.selected.clone().unwrap().mount.unwrap();
|
||||
InputResult::QuitChangeDirectory(output)
|
||||
}
|
||||
_ => InputResult::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, frame: &mut Frame) {
|
||||
let layout = Layout::default()
|
||||
.direction(ratatui::layout::Direction::Vertical)
|
||||
.constraints(vec![Constraint::Fill(1), Constraint::Length(5)])
|
||||
.split(frame.area());
|
||||
|
||||
let block = Block::bordered()
|
||||
.title(Line::styled("Mount", Color::White))
|
||||
.title_alignment(Alignment::Center)
|
||||
.padding(Padding::symmetric(1, 1))
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(Color::Yellow);
|
||||
block.clone().render(layout[0], frame.buffer_mut());
|
||||
|
||||
let rows: Vec<drives::Block> = self.drv.iter().flat_map(|d| d.blocks.clone()).collect();
|
||||
|
||||
if self.ts.selected().is_none() && !self.drv.is_empty() {
|
||||
self.ts.select(Some(0));
|
||||
}
|
||||
|
||||
self.ts
|
||||
.selected()
|
||||
.and_then(|n| rows.get(n).cloned())
|
||||
.clone_into(&mut self.selected);
|
||||
|
||||
let rows = rows.iter().map(|i| {
|
||||
Row::new(vec![
|
||||
i.dev.clone(),
|
||||
i.label.clone(),
|
||||
i.mount.clone().unwrap_or_default(),
|
||||
if i.mounted {
|
||||
"M".to_owned()
|
||||
} else {
|
||||
"O".to_owned()
|
||||
},
|
||||
])
|
||||
});
|
||||
let widths = [
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Ratio(1, 3),
|
||||
Constraint::Length(3),
|
||||
];
|
||||
let table = Table::new(rows, widths)
|
||||
.row_highlight_style(Color::Green)
|
||||
.highlight_symbol(">");
|
||||
|
||||
StatefulWidget::render(
|
||||
table,
|
||||
block.inner(frame.area()),
|
||||
frame.buffer_mut(),
|
||||
&mut self.ts,
|
||||
);
|
||||
|
||||
let descr = match &self.selected {
|
||||
Some(s) => {
|
||||
let mnt_point = match &s.mount {
|
||||
None => "",
|
||||
Some(m) => m,
|
||||
};
|
||||
let mounted = if s.mounted {
|
||||
format!("mounted to {mnt_point:?}")
|
||||
} else {
|
||||
format!("not mounted {mnt_point:?}")
|
||||
};
|
||||
|
||||
format!(
|
||||
"dev: {:?} label: {:?} type: {:?} {mounted} ",
|
||||
s.dev, s.label, s.fstype
|
||||
)
|
||||
}
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
let info = format!(
|
||||
"j - UP, k - DOWN, l - Goto mountpoint, m - Mount, u - Unmount, e - Eject\n{descr} {:?}",
|
||||
self.last_status
|
||||
);
|
||||
|
||||
let info = Paragraph::new(info).wrap(Wrap { trim: true });
|
||||
frame.render_widget(info, layout[1]);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue