mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-25 08:47:18 +02:00
Make crossterm default
This commit is contained in:
parent
2a08fb2f33
commit
a0c6a94854
5 changed files with 141 additions and 82 deletions
47
src/app.rs
47
src/app.rs
|
@ -23,7 +23,15 @@ use tui::{
|
|||
Terminal,
|
||||
};
|
||||
|
||||
use crate::util::Key;
|
||||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
use tui::backend::TermionBackend;
|
||||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
use termion::{
|
||||
event,
|
||||
input::{MouseTerminal, TermRead},
|
||||
raw::{IntoRawMode, RawTerminal},
|
||||
screen::AlternateScreen,
|
||||
};
|
||||
|
||||
pub fn cmp(t1: &Task, t2: &Task) -> Ordering {
|
||||
let urgency1 = match &t1.uda()["urgency"] {
|
||||
|
@ -431,7 +439,8 @@ impl App {
|
|||
task_id
|
||||
)[..],
|
||||
);
|
||||
println!("Opening task ...");
|
||||
// TODO: should we sleep here to show output of the editor?
|
||||
// std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
_ => {
|
||||
println!("Vim failed to start");
|
||||
|
@ -439,40 +448,12 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, event: Key) {
|
||||
match self.mode {
|
||||
AppMode::Report => match event {
|
||||
Key::Ctrl('c') | Key::Char('q') => self.should_quit = true,
|
||||
Key::Char('r') => self.update(),
|
||||
Key::Down | Key::Char('j') => self.next(),
|
||||
Key::Up | Key::Char('k') => self.previous(),
|
||||
Key::Char('d') => self.task_done(),
|
||||
Key::Char('u') => self.task_undo(),
|
||||
Key::Char('e') => self.task_edit(),
|
||||
Key::Char('/') => {
|
||||
self.mode = AppMode::Filter;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::Filter => match event {
|
||||
Key::Char('\n') | Key::Esc => {
|
||||
self.mode = AppMode::Report;
|
||||
}
|
||||
Key::Char(c) => {
|
||||
self.filter.push(c);
|
||||
}
|
||||
Key::Backspace => {
|
||||
self.filter.pop();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::App;
|
||||
use crate::util::setup_terminal;
|
||||
use std::io::stdin;
|
||||
|
||||
use task_hookrs::import::import;
|
||||
|
@ -492,6 +473,10 @@ mod tests {
|
|||
println!("{:?}", t);
|
||||
println!("{:?}", t);
|
||||
println!("{:?}", t);
|
||||
app.next();
|
||||
app.next();
|
||||
app.next();
|
||||
app.task_edit();
|
||||
// if let Ok(tasks) = import(stdin()) {
|
||||
// for task in tasks {
|
||||
// println!("Task: {}, entered {:?} is {} -> {}",
|
||||
|
|
46
src/main.rs
46
src/main.rs
|
@ -17,7 +17,17 @@ use std::time::{Duration, Instant};
|
|||
use tui::backend::Backend;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use app::App;
|
||||
use app::{App, AppMode};
|
||||
use crate::util::Key;
|
||||
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
const APP_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
const APP_NAME: &'static str = env!("CARGO_PKG_NAME");
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Terminal initialization
|
||||
|
@ -37,7 +47,39 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
// Handle input
|
||||
match events.next()? {
|
||||
Event::Input(input) => app.handle_input(input),
|
||||
Event::Input(input) => {
|
||||
match app.mode {
|
||||
AppMode::Report => match input {
|
||||
Key::Ctrl('c') | Key::Char('q') => app.should_quit = true,
|
||||
Key::Char('r') => app.update(),
|
||||
Key::Down | Key::Char('j') => app.next(),
|
||||
Key::Up | Key::Char('k') => app.previous(),
|
||||
Key::Char('d') => app.task_done(),
|
||||
Key::Char('u') => app.task_undo(),
|
||||
Key::Char('e') => {
|
||||
events.pause_event_loop(&mut terminal);
|
||||
app.task_edit();
|
||||
events.resume_event_loop(&mut terminal);
|
||||
},
|
||||
Key::Char('/') => {
|
||||
app.mode = AppMode::Filter;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
AppMode::Filter => match input {
|
||||
Key::Char('\n') | Key::Esc => {
|
||||
app.mode = AppMode::Report;
|
||||
}
|
||||
Key::Char(c) => {
|
||||
app.filter.push(c);
|
||||
}
|
||||
Key::Backspace => {
|
||||
app.filter.pop();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
Event::Tick => app.handle_tick(),
|
||||
}
|
||||
|
||||
|
|
76
src/util.rs
76
src/util.rs
|
@ -12,13 +12,18 @@ use termion::{
|
|||
event,
|
||||
input::{MouseTerminal, TermRead},
|
||||
raw::{IntoRawMode, RawTerminal},
|
||||
screen::AlternateScreen,
|
||||
screen::{AlternateScreen,ToMainScreen, ToAlternateScreen},
|
||||
};
|
||||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
use tui::{backend::TermionBackend, Terminal};
|
||||
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::{sync::mpsc, thread, time::Duration};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Key {
|
||||
|
@ -47,14 +52,6 @@ pub struct EventConfig {
|
|||
pub tick_rate: Duration,
|
||||
}
|
||||
|
||||
impl Default for EventConfig {
|
||||
fn default() -> EventConfig {
|
||||
EventConfig {
|
||||
tick_rate: Duration::from_millis(5),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Event<I> {
|
||||
Input(I),
|
||||
|
@ -80,8 +77,8 @@ pub fn destruct_terminal(mut terminal: Terminal<CrosstermBackend<io::Stdout>>) {
|
|||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
pub fn setup_terminal(
|
||||
) -> Terminal<TermionBackend<AlternateScreen<MouseTerminal<RawTerminal<io::Stdout>>>>> {
|
||||
let stdout = io::stdout().into_raw_mode().unwrap();
|
||||
let stdout = MouseTerminal::from(stdout);
|
||||
let raw_stdout = io::stdout().into_raw_mode().unwrap();
|
||||
let stdout = MouseTerminal::from(raw_stdout);
|
||||
let stdout = AlternateScreen::from(stdout);
|
||||
let backend = TermionBackend::new(stdout);
|
||||
Terminal::new(backend).unwrap()
|
||||
|
@ -94,23 +91,19 @@ pub fn destruct_terminal(
|
|||
}
|
||||
|
||||
pub struct Events {
|
||||
rx: mpsc::Receiver<Event<Key>>,
|
||||
pub rx: mpsc::Receiver<Event<Key>>,
|
||||
pub tx: mpsc::Sender<Event<Key>>,
|
||||
pub pause_stdin: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl Events {
|
||||
/// Constructs an new instance of `Events` with the default config.
|
||||
pub fn new(tick_rate: u64) -> Events {
|
||||
Events::with_config(EventConfig {
|
||||
tick_rate: Duration::from_millis(tick_rate),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "crossterm")]
|
||||
pub fn with_config(config: EventConfig) -> Events {
|
||||
use crossterm::event::{KeyCode::*, KeyModifiers};
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let pause_stdin = Arc::new(Mutex::new(false));
|
||||
let pause_stdin = pause_stdin.clone();
|
||||
let event_tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
|
@ -149,18 +142,23 @@ impl Events {
|
|||
event_tx.send(Event::Tick).unwrap();
|
||||
}
|
||||
});
|
||||
Events { rx }
|
||||
Events { rx, tx, pause_stdin }
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
pub fn with_config(config: EventConfig) -> Events {
|
||||
use termion::event::Key::*;
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let pause_stdin = Arc::new(Mutex::new(false));
|
||||
let input_handle = {
|
||||
let tx = tx.clone();
|
||||
let pause_stdin = pause_stdin.clone();
|
||||
thread::spawn(move || {
|
||||
let stdin = io::stdin();
|
||||
for evt in stdin.keys() {
|
||||
while *pause_stdin.lock().unwrap() {
|
||||
thread::sleep(config.tick_rate);
|
||||
}
|
||||
if let Ok(key) = evt {
|
||||
let key = match key {
|
||||
Backspace => Key::Backspace,
|
||||
|
@ -192,6 +190,7 @@ impl Events {
|
|||
})
|
||||
};
|
||||
let tick_handle = {
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || loop {
|
||||
if tx.send(Event::Tick).is_err() {
|
||||
break;
|
||||
|
@ -199,7 +198,7 @@ impl Events {
|
|||
thread::sleep(config.tick_rate);
|
||||
})
|
||||
};
|
||||
Events { rx }
|
||||
Events { rx, tx, pause_stdin }
|
||||
}
|
||||
|
||||
/// Attempts to read an event.
|
||||
|
@ -207,4 +206,37 @@ impl Events {
|
|||
pub fn next(&self) -> Result<Event<Key>, mpsc::RecvError> {
|
||||
self.rx.recv()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
pub fn pause_event_loop(&self, terminal: & mut Terminal<TermionBackend<AlternateScreen<MouseTerminal<RawTerminal<io::Stdout>>>>>) {
|
||||
*self.pause_stdin.lock().unwrap() = true;
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
write!(terminal.backend_mut(), "{}", ToMainScreen).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "termion", not(feature = "crossterm")))]
|
||||
pub fn resume_event_loop(&self, terminal: & mut Terminal<TermionBackend<AlternateScreen<MouseTerminal<RawTerminal<io::Stdout>>>>>) {
|
||||
write!(terminal.backend_mut(), "{}", ToAlternateScreen).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
*self.pause_stdin.lock().unwrap() = false;
|
||||
terminal.resize(terminal.size().unwrap()).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "crossterm")]
|
||||
pub fn pause_event_loop(&self, terminal: & mut Terminal<CrosstermBackend<io::Stdout>>) {
|
||||
*self.pause_stdin.lock().unwrap() = true;
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
disable_raw_mode().unwrap();
|
||||
execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture).unwrap();
|
||||
terminal.show_cursor().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "crossterm")]
|
||||
pub fn resume_event_loop(&self, terminal: & mut Terminal<CrosstermBackend<io::Stdout>>) {
|
||||
enable_raw_mode().unwrap();
|
||||
execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
*self.pause_stdin.lock().unwrap() = false;
|
||||
terminal.resize(terminal.size().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue