Move input buffer into it's own module.
This commit is contained in:
parent
d95f2faaef
commit
ade2c9f4da
6 changed files with 299 additions and 236 deletions
|
@ -81,7 +81,7 @@ impl AlarmRoster {
|
|||
}
|
||||
|
||||
// Parse string and add as alarm.
|
||||
pub fn add(&mut self, input: &String) -> Result<(), &str> {
|
||||
pub fn add(&mut self, input: &String) -> Result<(), &'static str> {
|
||||
let mut index = 0;
|
||||
let mut time: u32 = 0;
|
||||
let mut label: String;
|
||||
|
|
109
src/buffer.rs
Normal file
109
src/buffer.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use std::io::Write;
|
||||
use termion::{clear, cursor, color};
|
||||
use termion::raw::RawTerminal;
|
||||
use crate::layout::Layout;
|
||||
use crate::utils;
|
||||
|
||||
|
||||
pub struct Buffer {
|
||||
content: String,
|
||||
// Used for error messages.
|
||||
message: Option<&'static str>,
|
||||
pub altered: bool,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new() -> Buffer {
|
||||
Buffer {
|
||||
content: String::new(),
|
||||
altered: false,
|
||||
message: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self) -> &String {
|
||||
self.altered = false;
|
||||
&self.content
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: char) {
|
||||
self.altered = true;
|
||||
self.message = None;
|
||||
self.content.push(value);
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<char> {
|
||||
self.altered = true;
|
||||
self.message = None;
|
||||
self.content.pop()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.content.is_empty()
|
||||
}
|
||||
|
||||
// Clear input.
|
||||
pub fn clear(&mut self) {
|
||||
self.altered = true;
|
||||
self.content.clear();
|
||||
}
|
||||
|
||||
// Clear input and message.
|
||||
pub fn reset(&mut self) {
|
||||
self.message = None;
|
||||
self.clear();
|
||||
}
|
||||
|
||||
// Draw input buffer.
|
||||
pub fn draw<W: Write>(
|
||||
&self,
|
||||
stdout: &mut RawTerminal<W>,
|
||||
layout: &mut Layout,
|
||||
) -> Result<(), std::io::Error>
|
||||
{
|
||||
if let Some(msg) = self.message {
|
||||
write!(stdout,
|
||||
"{}{}{}{}{}{}",
|
||||
cursor::Hide,
|
||||
cursor::Goto(layout.error.col, layout.error.line),
|
||||
clear::UntilNewline,
|
||||
color::Fg(color::LightRed),
|
||||
&msg,
|
||||
color::Fg(color::Reset))?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !self.content.is_empty() {
|
||||
write!(stdout,
|
||||
"{}{}Add alarm: {}{}",
|
||||
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||
clear::CurrentLine,
|
||||
cursor::Show,
|
||||
&self.content)?;
|
||||
layout.cursor.col =
|
||||
layout.buffer.col
|
||||
+ 11
|
||||
+ utils::unicode_length(&self.content);
|
||||
// TODO: This would be a much better alternative, but panics because
|
||||
// of interference with async_reader.
|
||||
//layout.cursor.col = stdout.cursor_pos()?.0;
|
||||
} else {
|
||||
// Clear buffer display.
|
||||
write!(stdout,
|
||||
"{}{}{}",
|
||||
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||
clear::CurrentLine,
|
||||
cursor::Hide)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Draw error message at input buffer position.
|
||||
pub fn message(
|
||||
&mut self,
|
||||
msg: &'static str,
|
||||
) {
|
||||
self.message = Some(msg);
|
||||
self.altered = true;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,8 @@ use std::time;
|
|||
use std::io::Write;
|
||||
use termion::{color, cursor};
|
||||
use termion::raw::RawTerminal;
|
||||
use crate::consts::*;
|
||||
use crate::consts::COLOR;
|
||||
use crate::consts::digits::*;
|
||||
use crate::layout::{Layout, Position};
|
||||
|
||||
|
||||
|
|
342
src/consts.rs
342
src/consts.rs
|
@ -1,6 +1,20 @@
|
|||
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const USAGE: &str = concat!("USAGE: ", env!("CARGO_PKG_NAME"),
|
||||
|
||||
pub const COLOR: [&dyn termion::color::Color; 6] = [
|
||||
&termion::color::Cyan,
|
||||
&termion::color::Magenta,
|
||||
&termion::color::Green,
|
||||
&termion::color::Yellow,
|
||||
&termion::color::Blue,
|
||||
&termion::color::Red,
|
||||
];
|
||||
|
||||
// Maximum length of labels.
|
||||
pub const LABEL_SIZE_LIMIT: usize = 48;
|
||||
|
||||
pub mod ui {
|
||||
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const USAGE: &str = concat!("USAGE: ", env!("CARGO_PKG_NAME"),
|
||||
" [-h|-v] [-e|--exec COMMAND] [-p] [-q] [ALARM[/LABEL]]
|
||||
|
||||
PARAMETERS:
|
||||
|
@ -18,171 +32,163 @@ OPTIONS:
|
|||
|
||||
SIGNALS: <SIGUSR1> Reset clock.
|
||||
<SIGUSR2> Pause or un-pause clock.");
|
||||
pub const MENUBAR: &str =
|
||||
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
|
||||
pub const MENUBAR_SHORT: &str =
|
||||
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
|
||||
pub const MENUBAR_INS: &str =
|
||||
"Format: HH:MM:SS/LABEL [ENTER] Accept [ESC] Cancel [CTR-C] Quit";
|
||||
pub const MENUBAR: &str =
|
||||
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
|
||||
pub const MENUBAR_SHORT: &str =
|
||||
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
|
||||
pub const MENUBAR_INS: &str =
|
||||
"Format: HH:MM:SS/LABEL [ENTER] Accept [ESC] Cancel [CTR-C] Quit";
|
||||
}
|
||||
|
||||
pub const COLOR: [&dyn termion::color::Color; 6] = [
|
||||
&termion::color::Cyan,
|
||||
&termion::color::Magenta,
|
||||
&termion::color::Green,
|
||||
&termion::color::Yellow,
|
||||
&termion::color::Blue,
|
||||
&termion::color::Red,
|
||||
];
|
||||
pub mod digits {
|
||||
pub const DIGIT_HEIGHT: u16 = 5;
|
||||
pub const DIGIT_WIDTH: u16 = 5;
|
||||
pub const DIGITS: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||
[
|
||||
// 0
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█▄▄▄█",
|
||||
], [
|
||||
// 1
|
||||
" ▀█ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 2
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
"█▀▀▀▀",
|
||||
"█ ",
|
||||
"█▄▄▄▄"
|
||||
], [
|
||||
// 3
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
" ▀▀▀█",
|
||||
" █",
|
||||
"▄▄▄▄█"
|
||||
], [
|
||||
// 4
|
||||
"█ ",
|
||||
"█ █ ",
|
||||
"▀▀▀█▀",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 5
|
||||
"█▀▀▀▀",
|
||||
"█ ",
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
"▄▄▄▄█"
|
||||
], [
|
||||
// 6
|
||||
"█ ",
|
||||
"█ ",
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█▄▄▄█"
|
||||
], [
|
||||
// 7
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
], [
|
||||
// 8
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█▄▄▄█"
|
||||
], [
|
||||
// 9
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
" █"
|
||||
]
|
||||
];
|
||||
|
||||
// Maximum length of labels.
|
||||
pub const LABEL_SIZE_LIMIT: usize = 48;
|
||||
pub const DIGIT_HEIGHT: u16 = 5;
|
||||
pub const DIGIT_WIDTH: u16 = 5;
|
||||
pub const DIGITS: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||
[
|
||||
// 0
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█▄▄▄█",
|
||||
], [
|
||||
// 1
|
||||
" ▀█ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 2
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
"█▀▀▀▀",
|
||||
"█ ",
|
||||
"█▄▄▄▄"
|
||||
], [
|
||||
// 3
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
" ▀▀▀█",
|
||||
" █",
|
||||
"▄▄▄▄█"
|
||||
], [
|
||||
// 4
|
||||
"█ ",
|
||||
"█ █ ",
|
||||
"▀▀▀█▀",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 5
|
||||
"█▀▀▀▀",
|
||||
"█ ",
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
"▄▄▄▄█"
|
||||
], [
|
||||
// 6
|
||||
"█ ",
|
||||
"█ ",
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█▄▄▄█"
|
||||
], [
|
||||
// 7
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
], [
|
||||
// 8
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"█▄▄▄█"
|
||||
], [
|
||||
// 9
|
||||
"█▀▀▀█",
|
||||
"█ █",
|
||||
"▀▀▀▀█",
|
||||
" █",
|
||||
" █"
|
||||
]
|
||||
];
|
||||
|
||||
pub const DIGITS_PLAIN: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||
[
|
||||
// 0
|
||||
"█████",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█████"
|
||||
], [
|
||||
// 1
|
||||
" ██ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 2
|
||||
"█████",
|
||||
" █",
|
||||
"█████",
|
||||
"█ ",
|
||||
"█████"
|
||||
], [
|
||||
// 3
|
||||
"█████",
|
||||
" █",
|
||||
" ████",
|
||||
" █",
|
||||
"█████"
|
||||
], [
|
||||
// 4
|
||||
"█ ",
|
||||
"█ █ ",
|
||||
"█████",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 5
|
||||
"█████",
|
||||
"█ ",
|
||||
"█████",
|
||||
" █",
|
||||
"█████"
|
||||
], [
|
||||
// 6
|
||||
"█ ",
|
||||
"█ ",
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████"
|
||||
], [
|
||||
// 7
|
||||
"█████",
|
||||
" █",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 8
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████"
|
||||
], [
|
||||
// 9
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████",
|
||||
" █",
|
||||
" █"
|
||||
]
|
||||
];
|
||||
pub const DIGITS_PLAIN: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||
[
|
||||
// 0
|
||||
"█████",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█ █",
|
||||
"█████"
|
||||
], [
|
||||
// 1
|
||||
" ██ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 2
|
||||
"█████",
|
||||
" █",
|
||||
"█████",
|
||||
"█ ",
|
||||
"█████"
|
||||
], [
|
||||
// 3
|
||||
"█████",
|
||||
" █",
|
||||
" ████",
|
||||
" █",
|
||||
"█████"
|
||||
], [
|
||||
// 4
|
||||
"█ ",
|
||||
"█ █ ",
|
||||
"█████",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 5
|
||||
"█████",
|
||||
"█ ",
|
||||
"█████",
|
||||
" █",
|
||||
"█████"
|
||||
], [
|
||||
// 6
|
||||
"█ ",
|
||||
"█ ",
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████"
|
||||
], [
|
||||
// 7
|
||||
"█████",
|
||||
" █",
|
||||
" █ ",
|
||||
" █ ",
|
||||
" █ "
|
||||
], [
|
||||
// 8
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████"
|
||||
], [
|
||||
// 9
|
||||
"█████",
|
||||
"█ █",
|
||||
"█████",
|
||||
" █",
|
||||
" █"
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::Config;
|
||||
use crate::consts::*;
|
||||
use crate::consts::digits::*;
|
||||
|
||||
// If screen size falls below these values we skip computation of new
|
||||
// positions.
|
||||
|
|
77
src/lib.rs
77
src/lib.rs
|
@ -1,5 +1,6 @@
|
|||
extern crate termion;
|
||||
pub mod alarm;
|
||||
mod buffer;
|
||||
pub mod clock;
|
||||
pub mod consts;
|
||||
pub mod layout;
|
||||
|
@ -12,17 +13,17 @@ use std::io::Write;
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use signal_hook::{flag, low_level};
|
||||
use termion::{clear, color, cursor, style};
|
||||
use termion::{clear, cursor, style};
|
||||
use termion::raw::{IntoRawMode, RawTerminal};
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
//use termion::cursor::DetectCursorPos;
|
||||
use utils::*;
|
||||
use buffer::Buffer;
|
||||
use clock::Clock;
|
||||
use layout::Layout;
|
||||
use alarm::{Countdown, exec_command};
|
||||
pub use alarm::AlarmRoster;
|
||||
pub use consts::*;
|
||||
pub use consts::ui::*;
|
||||
|
||||
const SIGTSTP: usize = signal_hook::consts::SIGTSTP as usize;
|
||||
const SIGWINCH: usize = signal_hook::consts::SIGWINCH as usize;
|
||||
|
@ -47,11 +48,9 @@ pub fn run(
|
|||
layout.set_roster_width(alarm_roster.width());
|
||||
let mut clock = Clock::new();
|
||||
let mut countdown = Countdown::new();
|
||||
let mut buffer = String::new();
|
||||
let mut buffer = Buffer::new();
|
||||
|
||||
// State variables.
|
||||
// Request redraw of input buffer.
|
||||
let mut update_buffer = false;
|
||||
// Request redraw of menu.
|
||||
let mut update_menu = false;
|
||||
// Are we in insert mode?
|
||||
|
@ -103,10 +102,9 @@ pub fn run(
|
|||
}
|
||||
|
||||
// Update input buffer display, if requested.
|
||||
if update_buffer {
|
||||
draw_buffer(&mut stdout, &mut layout, &buffer)?;
|
||||
if buffer.altered {
|
||||
buffer.draw(&mut stdout, &mut layout)?;
|
||||
stdout.flush()?;
|
||||
update_buffer = false;
|
||||
}
|
||||
|
||||
// Update elapsed time.
|
||||
|
@ -166,7 +164,7 @@ pub fn run(
|
|||
alarm_roster.draw(&mut stdout, &mut layout);
|
||||
|
||||
// Redraw buffer.
|
||||
draw_buffer(&mut stdout, &mut layout, &buffer)?;
|
||||
buffer.draw(&mut stdout, &mut layout)?;
|
||||
|
||||
// Schedule menu redraw.
|
||||
update_menu = true;
|
||||
|
@ -236,9 +234,9 @@ pub fn run(
|
|||
// Enter.
|
||||
Key::Char('\n') => {
|
||||
if !buffer.is_empty() {
|
||||
if let Err(e) = alarm_roster.add(&buffer) {
|
||||
if let Err(e) = alarm_roster.add(buffer.read()) {
|
||||
// Error while processing input buffer.
|
||||
error_msg(&mut stdout, &layout, e)?;
|
||||
buffer.message(e);
|
||||
} else {
|
||||
// Input buffer processed without error.
|
||||
layout.set_roster_width(alarm_roster.width());
|
||||
|
@ -251,11 +249,10 @@ pub fn run(
|
|||
},
|
||||
// Escape ^W, and ^U clear input buffer.
|
||||
Key::Esc | Key::Ctrl('w') | Key::Ctrl('u') => {
|
||||
buffer.clear();
|
||||
buffer.reset();
|
||||
insert_mode = false;
|
||||
update_menu = true;
|
||||
layout.force_redraw = true;
|
||||
update_buffer = true;
|
||||
},
|
||||
// Backspace.
|
||||
Key::Backspace => {
|
||||
|
@ -267,12 +264,10 @@ pub fn run(
|
|||
layout.force_redraw = true;
|
||||
}
|
||||
}
|
||||
update_buffer = true;
|
||||
},
|
||||
// Forward every char if in insert mode.
|
||||
Key::Char(c) if insert_mode => {
|
||||
buffer.push(c);
|
||||
update_buffer = true;
|
||||
},
|
||||
// Reset clock on 'r'.
|
||||
Key::Char('r') => {
|
||||
|
@ -320,10 +315,8 @@ pub fn run(
|
|||
insert_mode = true;
|
||||
update_menu = true;
|
||||
layout.force_redraw = true;
|
||||
update_buffer = true;
|
||||
} else if !buffer.is_empty() && c == ':' {
|
||||
buffer.push(':');
|
||||
update_buffer = true;
|
||||
}
|
||||
},
|
||||
// Any other key.
|
||||
|
@ -331,7 +324,7 @@ pub fn run(
|
|||
}
|
||||
} else {
|
||||
// Main loop delay.
|
||||
thread::sleep(time::Duration::from_millis(200));
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,52 +443,6 @@ pub fn register_signals(
|
|||
flag::register(SIGWINCH as i32, Arc::clone(&recalc_flag)).unwrap();
|
||||
}
|
||||
|
||||
// Draw input buffer.
|
||||
fn draw_buffer<W: Write>(
|
||||
stdout: &mut RawTerminal<W>,
|
||||
layout: &mut Layout,
|
||||
buffer: &String,
|
||||
) -> Result<(), std::io::Error>
|
||||
{
|
||||
if !buffer.is_empty() {
|
||||
write!(stdout,
|
||||
"{}{}Add alarm: {}{}",
|
||||
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||
clear::CurrentLine,
|
||||
cursor::Show,
|
||||
buffer)?;
|
||||
layout.cursor.col = layout.buffer.col + 11 + unicode_length(buffer);
|
||||
// TODO: This would be a much better alternative, but panics because
|
||||
// of interference with async_reader.
|
||||
//layout.cursor.col = stdout.cursor_pos()?.0;
|
||||
} else {
|
||||
// Clear buffer display.
|
||||
write!(stdout,
|
||||
"{}{}{}",
|
||||
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||
clear::CurrentLine,
|
||||
cursor::Hide)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Draw error message at input buffer position.
|
||||
fn error_msg<W: Write>(
|
||||
stdout: &mut RawTerminal<W>,
|
||||
layout: &Layout,
|
||||
msg: &str
|
||||
) -> Result<(), std::io::Error>
|
||||
{
|
||||
write!(stdout,
|
||||
"{}{}{}{}{}",
|
||||
cursor::Goto(layout.error.col, layout.error.line),
|
||||
color::Fg(color::LightRed),
|
||||
msg,
|
||||
color::Fg(color::Reset),
|
||||
cursor::Hide)?;
|
||||
Ok (())
|
||||
}
|
||||
|
||||
// Prepare to suspend execution. Called on SIGTSTP.
|
||||
fn suspend<W: Write>(mut stdout: &mut RawTerminal<W>)
|
||||
-> Result<(), std::io::Error>
|
||||
|
|
Loading…
Reference in a new issue