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.
|
// 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 index = 0;
|
||||||
let mut time: u32 = 0;
|
let mut time: u32 = 0;
|
||||||
let mut label: String;
|
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 std::io::Write;
|
||||||
use termion::{color, cursor};
|
use termion::{color, cursor};
|
||||||
use termion::raw::RawTerminal;
|
use termion::raw::RawTerminal;
|
||||||
use crate::consts::*;
|
use crate::consts::COLOR;
|
||||||
|
use crate::consts::digits::*;
|
||||||
use crate::layout::{Layout, Position};
|
use crate::layout::{Layout, Position};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const COLOR: [&dyn termion::color::Color; 6] = [
|
||||||
pub const USAGE: &str = concat!("USAGE: ", env!("CARGO_PKG_NAME"),
|
&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]]
|
" [-h|-v] [-e|--exec COMMAND] [-p] [-q] [ALARM[/LABEL]]
|
||||||
|
|
||||||
PARAMETERS:
|
PARAMETERS:
|
||||||
|
@ -18,27 +32,18 @@ OPTIONS:
|
||||||
|
|
||||||
SIGNALS: <SIGUSR1> Reset clock.
|
SIGNALS: <SIGUSR1> Reset clock.
|
||||||
<SIGUSR2> Pause or un-pause clock.");
|
<SIGUSR2> Pause or un-pause clock.");
|
||||||
pub const MENUBAR: &str =
|
pub const MENUBAR: &str =
|
||||||
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
|
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
|
||||||
pub const MENUBAR_SHORT: &str =
|
pub const MENUBAR_SHORT: &str =
|
||||||
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
|
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
|
||||||
pub const MENUBAR_INS: &str =
|
pub const MENUBAR_INS: &str =
|
||||||
"Format: HH:MM:SS/LABEL [ENTER] Accept [ESC] Cancel [CTR-C] Quit";
|
"Format: HH:MM:SS/LABEL [ENTER] Accept [ESC] Cancel [CTR-C] Quit";
|
||||||
|
}
|
||||||
|
|
||||||
pub const COLOR: [&dyn termion::color::Color; 6] = [
|
pub mod digits {
|
||||||
&termion::color::Cyan,
|
pub const DIGIT_HEIGHT: u16 = 5;
|
||||||
&termion::color::Magenta,
|
pub const DIGIT_WIDTH: u16 = 5;
|
||||||
&termion::color::Green,
|
pub const DIGITS: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||||
&termion::color::Yellow,
|
|
||||||
&termion::color::Blue,
|
|
||||||
&termion::color::Red,
|
|
||||||
];
|
|
||||||
|
|
||||||
// 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
|
// 0
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
|
@ -110,9 +115,9 @@ pub const DIGITS: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||||
" █",
|
" █",
|
||||||
" █"
|
" █"
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const DIGITS_PLAIN: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
pub const DIGITS_PLAIN: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||||
[
|
[
|
||||||
// 0
|
// 0
|
||||||
"█████",
|
"█████",
|
||||||
|
@ -184,5 +189,6 @@ pub const DIGITS_PLAIN: [[&str; DIGIT_HEIGHT as usize]; 10] = [
|
||||||
" █",
|
" █",
|
||||||
" █"
|
" █"
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
use crate::consts::*;
|
use crate::consts::digits::*;
|
||||||
|
|
||||||
// If screen size falls below these values we skip computation of new
|
// If screen size falls below these values we skip computation of new
|
||||||
// positions.
|
// positions.
|
||||||
|
|
77
src/lib.rs
77
src/lib.rs
|
@ -1,5 +1,6 @@
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
pub mod alarm;
|
pub mod alarm;
|
||||||
|
mod buffer;
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
@ -12,17 +13,17 @@ use std::io::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
use signal_hook::{flag, low_level};
|
use signal_hook::{flag, low_level};
|
||||||
use termion::{clear, color, cursor, style};
|
use termion::{clear, cursor, style};
|
||||||
use termion::raw::{IntoRawMode, RawTerminal};
|
use termion::raw::{IntoRawMode, RawTerminal};
|
||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
//use termion::cursor::DetectCursorPos;
|
//use termion::cursor::DetectCursorPos;
|
||||||
use utils::*;
|
use buffer::Buffer;
|
||||||
use clock::Clock;
|
use clock::Clock;
|
||||||
use layout::Layout;
|
use layout::Layout;
|
||||||
use alarm::{Countdown, exec_command};
|
use alarm::{Countdown, exec_command};
|
||||||
pub use alarm::AlarmRoster;
|
pub use alarm::AlarmRoster;
|
||||||
pub use consts::*;
|
pub use consts::ui::*;
|
||||||
|
|
||||||
const SIGTSTP: usize = signal_hook::consts::SIGTSTP as usize;
|
const SIGTSTP: usize = signal_hook::consts::SIGTSTP as usize;
|
||||||
const SIGWINCH: usize = signal_hook::consts::SIGWINCH 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());
|
layout.set_roster_width(alarm_roster.width());
|
||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
let mut countdown = Countdown::new();
|
let mut countdown = Countdown::new();
|
||||||
let mut buffer = String::new();
|
let mut buffer = Buffer::new();
|
||||||
|
|
||||||
// State variables.
|
// State variables.
|
||||||
// Request redraw of input buffer.
|
|
||||||
let mut update_buffer = false;
|
|
||||||
// Request redraw of menu.
|
// Request redraw of menu.
|
||||||
let mut update_menu = false;
|
let mut update_menu = false;
|
||||||
// Are we in insert mode?
|
// Are we in insert mode?
|
||||||
|
@ -103,10 +102,9 @@ pub fn run(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update input buffer display, if requested.
|
// Update input buffer display, if requested.
|
||||||
if update_buffer {
|
if buffer.altered {
|
||||||
draw_buffer(&mut stdout, &mut layout, &buffer)?;
|
buffer.draw(&mut stdout, &mut layout)?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
update_buffer = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update elapsed time.
|
// Update elapsed time.
|
||||||
|
@ -166,7 +164,7 @@ pub fn run(
|
||||||
alarm_roster.draw(&mut stdout, &mut layout);
|
alarm_roster.draw(&mut stdout, &mut layout);
|
||||||
|
|
||||||
// Redraw buffer.
|
// Redraw buffer.
|
||||||
draw_buffer(&mut stdout, &mut layout, &buffer)?;
|
buffer.draw(&mut stdout, &mut layout)?;
|
||||||
|
|
||||||
// Schedule menu redraw.
|
// Schedule menu redraw.
|
||||||
update_menu = true;
|
update_menu = true;
|
||||||
|
@ -236,9 +234,9 @@ pub fn run(
|
||||||
// Enter.
|
// Enter.
|
||||||
Key::Char('\n') => {
|
Key::Char('\n') => {
|
||||||
if !buffer.is_empty() {
|
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 while processing input buffer.
|
||||||
error_msg(&mut stdout, &layout, e)?;
|
buffer.message(e);
|
||||||
} else {
|
} else {
|
||||||
// Input buffer processed without error.
|
// Input buffer processed without error.
|
||||||
layout.set_roster_width(alarm_roster.width());
|
layout.set_roster_width(alarm_roster.width());
|
||||||
|
@ -251,11 +249,10 @@ pub fn run(
|
||||||
},
|
},
|
||||||
// Escape ^W, and ^U clear input buffer.
|
// Escape ^W, and ^U clear input buffer.
|
||||||
Key::Esc | Key::Ctrl('w') | Key::Ctrl('u') => {
|
Key::Esc | Key::Ctrl('w') | Key::Ctrl('u') => {
|
||||||
buffer.clear();
|
buffer.reset();
|
||||||
insert_mode = false;
|
insert_mode = false;
|
||||||
update_menu = true;
|
update_menu = true;
|
||||||
layout.force_redraw = true;
|
layout.force_redraw = true;
|
||||||
update_buffer = true;
|
|
||||||
},
|
},
|
||||||
// Backspace.
|
// Backspace.
|
||||||
Key::Backspace => {
|
Key::Backspace => {
|
||||||
|
@ -267,12 +264,10 @@ pub fn run(
|
||||||
layout.force_redraw = true;
|
layout.force_redraw = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update_buffer = true;
|
|
||||||
},
|
},
|
||||||
// Forward every char if in insert mode.
|
// Forward every char if in insert mode.
|
||||||
Key::Char(c) if insert_mode => {
|
Key::Char(c) if insert_mode => {
|
||||||
buffer.push(c);
|
buffer.push(c);
|
||||||
update_buffer = true;
|
|
||||||
},
|
},
|
||||||
// Reset clock on 'r'.
|
// Reset clock on 'r'.
|
||||||
Key::Char('r') => {
|
Key::Char('r') => {
|
||||||
|
@ -320,10 +315,8 @@ pub fn run(
|
||||||
insert_mode = true;
|
insert_mode = true;
|
||||||
update_menu = true;
|
update_menu = true;
|
||||||
layout.force_redraw = true;
|
layout.force_redraw = true;
|
||||||
update_buffer = true;
|
|
||||||
} else if !buffer.is_empty() && c == ':' {
|
} else if !buffer.is_empty() && c == ':' {
|
||||||
buffer.push(':');
|
buffer.push(':');
|
||||||
update_buffer = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Any other key.
|
// Any other key.
|
||||||
|
@ -331,7 +324,7 @@ pub fn run(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Main loop delay.
|
// 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();
|
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.
|
// Prepare to suspend execution. Called on SIGTSTP.
|
||||||
fn suspend<W: Write>(mut stdout: &mut RawTerminal<W>)
|
fn suspend<W: Write>(mut stdout: &mut RawTerminal<W>)
|
||||||
-> Result<(), std::io::Error>
|
-> Result<(), std::io::Error>
|
||||||
|
|
Loading…
Reference in a new issue