Skip to content

Commit

Permalink
ncurses port
Browse files Browse the repository at this point in the history
  • Loading branch information
Deezzir committed Feb 5, 2023
1 parent 2302751 commit 01073a0
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 229 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ description = "Interactive TODO application in terminal"
edition = "2021"

[dependencies]
termion = "2.0.1"
ncurses = "5.101.0"
regex = "1.3.9"
chrono = "0.4.23"
6 changes: 3 additions & 3 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ TODO(): Add autoresizing of the TODO app UI(tree?)
TODO(): Finish a Rust TODO app
TODO(): Finish a Game of Life with Rust with GUI(SDL?)
TODO(): Learn Rust
DONE(2023-02-03 05:43:32.718001867 -05:00): Add editing option to TODO app
DONE(2023-02-03 05:43:32.890226461 -05:00): Add inserting option to TODO app
DONE(2023-02-03 05:43:33.055269071 -05:00): Add undo option to TODO app
DONE(2023-02-03 05:43:33.752245741 -05:00): Add date tracking to TODO app
DONE(2023-02-03 05:43:34.097168023 -05:00): Add safechecks for editing mode
DONE(2023-02-04 19:05:33.588875 -05:00): Add safechecks for editing mode
DONE(2023-02-04 19:12:32.253412 -05:00): Add editing option to TODO app
DONE(2023-02-04 19:17:54.422294 -05:00): Add inserting option to TODO app
130 changes: 56 additions & 74 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
extern crate regex;
extern crate termion;
mod mods;

use chrono::Local;
use std::env::args;
use std::io::stderr;
use std::io::stdin;
use std::io::stdout;
use std::io::StderrLock;
use std::io::Write;
use std::process::exit;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use chrono::Local;

use termion::color;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use ncurses::*;

use mods::todo::*;
use mods::ui::*;

const SELECTED_PAIR: (&dyn color::Color, &dyn color::Color) = (&color::Black, &color::Cyan);
const UNSELECTED_PAIR: (&dyn color::Color, &dyn color::Color) = (&color::Black, &color::LightBlack);
const HIGHLIGHT_PAIR: (&dyn color::Color, &dyn color::Color) = (&color::Black, &color::LightGreen);
const UI_PAIR: (&dyn color::Color, &dyn color::Color) = (&color::White, &color::Black);
const SELECTED_PAIR: i16 = 1;
const UNSELECTED_PAIR: i16 = 2;
const HIGHLIGHT_PAIR: i16 = 3;
const UI_PAIR: i16 = 4;

const USAGE: &str = "Usage: todo [-f | --file <file>] [-h | --help]";

Expand Down Expand Up @@ -54,37 +41,32 @@ Author: Iurii Kondrakov <[email protected]>
const FILE_PATH: &str = "TODO";

fn main() {
let stdout = stdout();
let stdout = stdout.lock();
let stderr = stderr();
let stderr = stderr.lock();
let file_path: String = get_args(stderr);
let file_path: String = get_args();

let timeout = Duration::from_millis(100);
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let stdin = stdin();
let stdin = stdin.lock();
let mut stdin = stdin.keys();

while let Some(Ok(key)) = stdin.next() {
tx.send(key).unwrap();
}
});
initscr();
raw();
noecho();
keypad(stdscr(), true);
timeout(16);
curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
use_default_colors();
init_colors();

let mut editing: bool = false;
let mut editing_cursor: usize = 0;
let mut app: TodoApp = TodoApp::new();

let stdout_raw = stdout.into_raw_mode().unwrap();
let mut ui = UI::new(stdout_raw);
let mut ui = UI::new();

app.parse(&file_path);

loop {
erase();
let date = Local::now().format("%Y-%m-%d %H:%M:%S");
let mut w = 0;
let mut h = 0;
getmaxyx(stdscr(), &mut h, &mut w);

ui.begin(Vec2::new(0, 0), LayoutKind::Vert);
ui.begin(Vec2::new(0, 0), LayoutKind::Vert, Vec2::new(w, h));
{
ui.begin_layout(LayoutKind::Horz);
{
Expand Down Expand Up @@ -181,74 +163,74 @@ fn main() {
}
ui.end();

if let Ok(key) = rx.recv_timeout(timeout) {
use Key::*;

refresh();
let key = getch();
if key != ERR {
if !editing {
app.clear_message();
match key {
Up | Char('k') => app.go_up(),
Down | Char('j') => app.go_down(),
Char('g') => app.go_top(),
Char('G') => app.go_bottom(),
Char('K') => app.drag_up(),
Char('J') => app.drag_down(),
Char('\n') => app.move_item(),
Char('d') => app.delete_item(),
Char('i') => {
match key as u8 as char {
'k' => app.go_up(),
'j' => app.go_down(),
'g' => app.go_top(),
'G' => app.go_bottom(),
'K' => app.drag_up(),
'J' => app.drag_down(),
'\n' => app.move_item(),
'd' => app.delete_item(),
'i' => {
editing_cursor = app.insert_item();
editing = editing_cursor == 0;
}
Char('r') => {
'r' => {
editing_cursor = app.edit_item();
editing = editing_cursor > 0;
}
Char('u') => app.undo(),
Char('\t') => app.toggle_panel(),
Char('q') | Key::Ctrl('c') => break,
'u' => app.undo(),
'\t' => app.toggle_panel(),
'q' => break,
_ => {}
}
} else {
match key {
Char('\n') | Esc => {
match key as u8 as char {
'\n' => {
editing = app.finish_edit();
editing_cursor = if editing { editing_cursor } else { 0 };
}
_ => app.edit_item_with(&mut editing_cursor, Some(key)),
_ => app.edit_item_with(&mut editing_cursor, key),
}
}
}
}

endwin();
app.save(&file_path).unwrap();
}

fn get_args(mut stderr: StderrLock) -> String {
fn init_colors() {
start_color();
init_pair(HIGHLIGHT_PAIR, COLOR_BLACK, COLOR_GREEN);
init_pair(SELECTED_PAIR, COLOR_BLACK, COLOR_CYAN);
init_pair(UNSELECTED_PAIR, COLOR_BLACK, COLOR_WHITE);
init_pair(UI_PAIR, COLOR_WHITE, COLOR_BLACK);
}

fn get_args() -> String {
let mut args = args().skip(1);

match args.next() {
Some(arg) => match arg.as_str() {
"-f" | "--file" => args.next().unwrap_or_else(|| {
stderr
.write_all(format!("[ERROR]: No file given for '{arg}'.\n").as_bytes())
.unwrap();
stderr.write_all(USAGE.as_bytes()).unwrap();
stderr.flush().unwrap();
eprintln!("[ERROR]: No file given for '{arg}'.");
eprintln!("{USAGE}");
exit(1);
}),
"-h" | "--help" => {
stderr
.write_all(format!("{HELP}\n{USAGE}\n").as_bytes())
.unwrap();
stderr.flush().unwrap();
eprintln!("{HELP}\n{USAGE}");
exit(0);
}
_ => {
stderr
.write_all(format!("[ERROR]: Unknown argument: '{arg}'.\n").as_bytes())
.unwrap();
stderr.write_all(USAGE.as_bytes()).unwrap();
stderr.flush().unwrap();
eprintln!("[ERROR]: Unknown argument: '{arg}'.");
eprintln!("{USAGE}");
exit(1);
}
},
Expand Down
1 change: 0 additions & 1 deletion src/mods.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
mod term;
pub mod todo;
pub mod ui;
41 changes: 0 additions & 41 deletions src/mods/term.rs

This file was deleted.

66 changes: 29 additions & 37 deletions src/mods/todo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ use std::io::{self, BufRead, Write};

use chrono::{DateTime, Local};

use ncurses::constants;
use regex::Regex;

use termion::event::Key;

// const MAX_STACK_SIZE: usize = 20;

#[derive(PartialEq, Clone, Copy)]
Expand Down Expand Up @@ -210,51 +208,45 @@ impl List {
}
}

fn edit(&mut self, cur: &mut usize, mut key: Option<Key>) {
fn edit(&mut self, cur: &mut usize, key: i32) {
let item = &mut self.list[self.cur];
*cur = min(*cur, item.text.len());

if let Some(key) = key.take() {
use Key::*;
match key {
Left => {
if *cur > 0 {
*cur -= 1;
}
match key {
32..=126 => {
if *cur > item.text.len() {
item.text.push(key as u8 as char);
} else {
item.text.insert(*cur, key as u8 as char);
}
Right => {
if *cur < item.text.len() {
*cur += 1;
}
*cur += 1;
}
constants::KEY_LEFT => {
if *cur > 0 {
*cur -= 1;
}
Backspace => {
if *cur > 0 {
*cur -= 1;
if *cur < item.text.len() {
item.text.remove(*cur);
}
}
}
constants::KEY_RIGHT => {
if *cur < item.text.len() {
*cur += 1;
}
Delete => {
}
constants::KEY_BACKSPACE => {
if *cur > 0 {
*cur -= 1;
if *cur < item.text.len() {
item.text.remove(*cur);
}
}
Home => *cur = 0,
End => *cur = item.text.len(),
Char(c) => {
let c = c as u8;
if c.is_ascii() && (32..127).contains(&c) {
if *cur > item.text.len() {
item.text.push(c as char);
} else {
item.text.insert(*cur, c as char);
}
*cur += 1;
}
}
constants::KEY_DC => {
if *cur < item.text.len() {
item.text.remove(*cur);
}
_ => {}
}
constants::KEY_HOME => *cur = 0,
constants::KEY_END => *cur = item.text.len(),
_ => {}
}
}
}
Expand Down Expand Up @@ -631,7 +623,7 @@ impl TodoApp {
editing_cursor
}

pub fn edit_item_with(&mut self, cur: &mut usize, key: Option<Key>) {
pub fn edit_item_with(&mut self, cur: &mut usize, key: i32) {
assert!(
self.is_in_edit(),
"edit_item_with() called without a matching edit_item() or insert_item()"
Expand Down
Loading

0 comments on commit 01073a0

Please sign in to comment.