Add more documentation.
parent
e95cc4c1e9
commit
ab099b524a
|
@ -0,0 +1,2 @@
|
|||
break rust_panic
|
||||
break core::option::expect_failed::h4927e1fef06c4878
|
|
@ -18,6 +18,12 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! This crate contains the frontend stuff of the application. The application entry way on `src/bin.rs` creates an event loop and passes input to the `ui` module.
|
||||
|
||||
The mail handling stuff is done in the `melib` crate which includes all backend needs. The split is done to theoretically be able to create different frontends with the same innards.
|
||||
*/
|
||||
|
||||
extern crate melib;
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
/*!
|
||||
Define a (x, y) point in the terminal display as a holder of a character, foreground/background
|
||||
colors and attributes.
|
||||
*/
|
||||
use std::ops::{Index, IndexMut, Deref, DerefMut};
|
||||
use super::position::*;
|
||||
use termion::color::AnsiValue;
|
||||
|
||||
|
||||
/// Types and implementations taken from rustty for convenience.
|
||||
|
||||
pub trait CellAccessor: HasSize {
|
||||
fn cellvec(&self) -> &Vec<Cell>;
|
||||
fn cellvec_mut(&mut self) -> &mut Vec<Cell>;
|
||||
|
@ -409,10 +416,10 @@ impl Color {
|
|||
pub fn as_termion(&self) -> AnsiValue {
|
||||
match *self {
|
||||
b @ Color::Black | b @ Color::Red | b @ Color::Green | b @ Color::Yellow | b @ Color::Blue | b @ Color::Magenta | b @ Color::Cyan | b @ Color::White | b @ Color::Default =>
|
||||
{
|
||||
{
|
||||
AnsiValue(b.as_byte())
|
||||
},
|
||||
Color::Byte(b) => {
|
||||
Color::Byte(b) => {
|
||||
AnsiValue(b as u8)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/*! Entities that handle Mail specific functions.
|
||||
*/
|
||||
use ui::components::*;
|
||||
use ui::cells::*;
|
||||
|
||||
|
@ -90,10 +92,10 @@ impl MailListing {
|
|||
match iter.peek() {
|
||||
Some(&(_, x))
|
||||
if mailbox.get_thread(*x).get_indentation() == indentation =>
|
||||
{
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
}
|
||||
{
|
||||
indentations.pop();
|
||||
indentations.push(true);
|
||||
}
|
||||
_ => {
|
||||
indentations.pop();
|
||||
indentations.push(false);
|
||||
|
@ -115,8 +117,8 @@ impl MailListing {
|
|||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
}
|
||||
let x = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, idx == self.cursor_pos.2, &indentations),
|
||||
};
|
||||
let x = write_string_to_grid(&MailListing::make_thread_entry(envelope, idx, indentation, container, &indentations),
|
||||
&mut content,
|
||||
fg_color,
|
||||
bg_color,
|
||||
|
@ -129,23 +131,23 @@ impl MailListing {
|
|||
match iter.peek() {
|
||||
Some(&(_, x))
|
||||
if mailbox.get_thread(*x).get_indentation() > indentation =>
|
||||
{
|
||||
indentations.push(false);
|
||||
}
|
||||
{
|
||||
indentations.push(false);
|
||||
}
|
||||
Some(&(_, x))
|
||||
if mailbox.get_thread(*x).get_indentation() < indentation =>
|
||||
{
|
||||
for _ in 0..(indentation - mailbox.get_thread(*x).get_indentation()) {
|
||||
indentations.pop();
|
||||
{
|
||||
for _ in 0..(indentation - mailbox.get_thread(*x).get_indentation()) {
|
||||
indentations.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Populate `CellBuffer` with every entry.
|
||||
// TODO: Lazy load?
|
||||
// Populate `CellBuffer` with every entry.
|
||||
// TODO: Lazy load?
|
||||
let mut idx = 0;
|
||||
for y in 0..=self.length {
|
||||
if idx >= self.length {
|
||||
|
@ -254,6 +256,40 @@ impl MailListing {
|
|||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
|
||||
/// Create a pager for the `Envelope` currently under the cursor.
|
||||
fn draw_header_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
{
|
||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||
let envelope: &Envelope = if threaded {
|
||||
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
|
||||
&mailbox.collection[i]
|
||||
} else {
|
||||
&mailbox.collection[self.cursor_pos.2]
|
||||
};
|
||||
|
||||
let pager_filter = context.settings.pager.filter.clone();
|
||||
self.pager = Some(Pager::new(&envelope, pager_filter));
|
||||
}
|
||||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
||||
}
|
||||
/// Create a pager for the `Envelope` currently under the cursor.
|
||||
fn draw_attachment_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
{
|
||||
let threaded = context.accounts[self.cursor_pos.0].settings.threaded;
|
||||
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1].as_ref().unwrap().as_ref().unwrap();
|
||||
let envelope: &Envelope = if threaded {
|
||||
let i = mailbox.get_threaded_mail(self.cursor_pos.2);
|
||||
&mailbox.collection[i]
|
||||
} else {
|
||||
&mailbox.collection[self.cursor_pos.2]
|
||||
};
|
||||
|
||||
let pager_filter = context.settings.pager.filter.clone();
|
||||
self.pager = Some(Pager::new(&envelope, pager_filter));
|
||||
}
|
||||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
||||
}
|
||||
/// Create a pager for the `Envelope` currently under the cursor.
|
||||
fn draw_mail_view(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
{
|
||||
|
@ -272,28 +308,12 @@ impl MailListing {
|
|||
self.pager.as_mut().map(|p| p.draw(grid, area, context));
|
||||
}
|
||||
fn make_thread_entry(envelope: &Envelope, idx: usize, indent: usize,
|
||||
container: &Container, highlight: bool, indentations: &Vec<bool>) -> String {
|
||||
container: &Container, indentations: &Vec<bool>) -> String {
|
||||
let has_sibling = container.has_sibling();
|
||||
let has_parent = container.has_parent();
|
||||
let show_subject = container.get_show_subject();
|
||||
|
||||
let fg_color = if !envelope.is_seen() {
|
||||
Color::Byte(0)
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
let bg_color = if highlight {
|
||||
if !envelope.is_seen() {
|
||||
Color::Byte(252)
|
||||
} else if idx % 2 == 0 {
|
||||
Color::Byte(236)
|
||||
} else {
|
||||
Color::Default
|
||||
}
|
||||
} else {
|
||||
Color::Byte(246)
|
||||
};
|
||||
let mut s = format!("{} {} ", idx, &envelope.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string()); // {} {:.85}",idx,),e.get_subject())
|
||||
let mut s = format!("{} {} ", idx, &envelope.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string());
|
||||
for i in 0..indent {
|
||||
if indentations.len() > i && indentations[i]
|
||||
{
|
||||
|
@ -398,50 +418,50 @@ impl Component for MailListing {
|
|||
};
|
||||
|
||||
let x = write_string_to_grid(&format!("Date: {}", envelope.get_date_as_str()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+1), set_y(bottom_right, mid+1)));
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+1), set_y(bottom_right, mid+1)));
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x, mid+1)].set_ch(' ');
|
||||
grid[(x, mid+1)].set_bg(Color::Default);
|
||||
grid[(x, mid+1)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("From: {}", envelope.get_from()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+2), set_y(bottom_right, mid+2)));
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+2), set_y(bottom_right, mid+2)));
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x, mid+2)].set_ch(' ');
|
||||
grid[(x, mid+2)].set_bg(Color::Default);
|
||||
grid[(x, mid+2)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("To: {}", envelope.get_to()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+3), set_y(bottom_right, mid+3)));
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+3), set_y(bottom_right, mid+3)));
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x, mid+3)].set_ch(' ');
|
||||
grid[(x, mid+3)].set_bg(Color::Default);
|
||||
grid[(x, mid+3)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("Subject: {}", envelope.get_subject()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+4), set_y(bottom_right, mid+4)));
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+4), set_y(bottom_right, mid+4)));
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x, mid+4)].set_ch(' ');
|
||||
grid[(x, mid+4)].set_bg(Color::Default);
|
||||
grid[(x, mid+4)].set_fg(Color::Default);
|
||||
}
|
||||
let x = write_string_to_grid(&format!("Message-ID: {}", envelope.get_message_id_raw()),
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+5), set_y(bottom_right, mid+5)));
|
||||
grid,
|
||||
Color::Byte(33),
|
||||
Color::Default,
|
||||
(set_y(upper_left, mid+5), set_y(bottom_right, mid+5)));
|
||||
for x in x..=get_x(bottom_right) {
|
||||
grid[(x, mid+5)].set_ch(' ');
|
||||
grid[(x, mid+5)].set_bg(Color::Default);
|
||||
|
@ -588,7 +608,7 @@ impl AccountMenu {
|
|||
|
||||
|
||||
let highlight = self.cursor.map(|(x,_)| x == a.index).unwrap_or(false);
|
||||
|
||||
|
||||
let mut parents: Vec<Option<usize>> = vec!(None; a.entries.len());
|
||||
|
||||
for (idx, e) in a.entries.iter().enumerate() {
|
||||
|
@ -648,9 +668,9 @@ impl AccountMenu {
|
|||
};
|
||||
let color_fg = if highlight {
|
||||
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||
Color::Byte(233)
|
||||
Color::Byte(233)
|
||||
} else {
|
||||
Color::Byte(15)
|
||||
Color::Byte(15)
|
||||
}
|
||||
} else {
|
||||
Color::Default
|
||||
|
@ -658,19 +678,19 @@ impl AccountMenu {
|
|||
|
||||
let color_bg = if highlight {
|
||||
if idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||
Color::Byte(15)
|
||||
Color::Byte(15)
|
||||
} else {
|
||||
Color::Byte(233)
|
||||
Color::Byte(233)
|
||||
}
|
||||
} else {
|
||||
Color::Default
|
||||
};
|
||||
|
||||
let x = write_string_to_grid(&s,
|
||||
grid,
|
||||
color_fg,
|
||||
color_bg,
|
||||
(set_y(upper_left, y), bottom_right));
|
||||
grid,
|
||||
color_fg,
|
||||
color_bg,
|
||||
(set_y(upper_left, y), bottom_right));
|
||||
|
||||
if highlight && idx > 1 && self.cursor.unwrap().1 == idx - 2 {
|
||||
change_colors(grid, ((x, y),(get_x(bottom_right)+1, y)), color_fg , color_bg);
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Components are ways to handle application data. They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.)
|
||||
|
||||
See the `Component` Trait for more details.
|
||||
*/
|
||||
|
||||
pub mod utilities;
|
||||
pub mod mail;
|
||||
pub mod notifications;
|
||||
|
@ -78,7 +84,7 @@ pub trait Component {
|
|||
}
|
||||
}
|
||||
|
||||
/// Copy Area src to dest
|
||||
/// Copy a source `Area` to a destination.
|
||||
pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area, src: Area) {
|
||||
if !is_valid_area!(dest) || !is_valid_area!(src) {
|
||||
eprintln!("BUG: Invalid areas in copy_area:\n src: {:?}\n dest: {:?}", src, dest);
|
||||
|
@ -104,6 +110,7 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area,
|
|||
}
|
||||
}
|
||||
|
||||
/// Change foreground and background colors in an `Area`
|
||||
pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_color: Color) {
|
||||
if !is_valid_area!(area) {
|
||||
eprintln!("BUG: Invalid area in change_colors:\n area: {:?}", area);
|
||||
|
@ -118,6 +125,7 @@ pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_colo
|
|||
}
|
||||
|
||||
|
||||
/// Write an `&str` to a `CellBuffer` in a specified `Area` with the passed colors.
|
||||
fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_color: Color, area: Area) -> usize {
|
||||
let bounds = grid.size();
|
||||
let upper_left = upper_left!(area);
|
||||
|
@ -144,6 +152,7 @@ fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_colo
|
|||
x
|
||||
}
|
||||
|
||||
/// Completely clear an `Area` with an empty char and the terminal's default colors.
|
||||
fn clear_area(grid: &mut CellBuffer, area: Area) {
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
/*!
|
||||
Notification handling components.
|
||||
*/
|
||||
use notify_rust::Notification as notify_Notification;
|
||||
|
||||
use ui::*;
|
||||
use ui::components::*;
|
||||
|
||||
/// Passes notifications to the OS using the XDG specifications.
|
||||
pub struct XDGNotifications {}
|
||||
|
||||
impl Component for XDGNotifications {
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*! Various useful components that can be used in a generic fashion.
|
||||
*/
|
||||
|
||||
use ui::components::*;
|
||||
use ui::cells::*;
|
||||
|
||||
|
@ -125,6 +128,7 @@ pub struct Pager {
|
|||
content: CellBuffer,
|
||||
}
|
||||
|
||||
// TODO: Make the `new` method content agnostic.
|
||||
impl Pager {
|
||||
pub fn new(mail: &Envelope, pager_filter: Option<String>) -> Self {
|
||||
let mut text = mail.get_body().get_text();
|
||||
|
@ -159,6 +163,20 @@ impl Pager {
|
|||
content: content,
|
||||
}
|
||||
}
|
||||
pub fn new_from_str(s: &str) -> Self {
|
||||
let lines: Vec<&str> = s.trim().split('\n').collect();
|
||||
let height = lines.len();
|
||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
|
||||
Pager::print_string(&mut content, s);
|
||||
Pager {
|
||||
cursor_pos: 0,
|
||||
height: height,
|
||||
width: width,
|
||||
dirty: true,
|
||||
content: content,
|
||||
}
|
||||
}
|
||||
pub fn print_string(content: &mut CellBuffer, s: &str) {
|
||||
let lines: Vec<&str> = s.trim().split('\n').collect();
|
||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
/*! A parser module for user commands passed through the Ex mode.
|
||||
*/
|
||||
use std;
|
||||
use nom::digit;
|
||||
|
||||
|
||||
named!(usize_c<usize>,
|
||||
map_res!(map_res!(ws!(digit), std::str::from_utf8), std::str::FromStr::from_str));
|
||||
map_res!(map_res!(ws!(digit), std::str::from_utf8), std::str::FromStr::from_str));
|
||||
|
||||
named!(pub goto<usize>,
|
||||
preceded!(tag!("b "),
|
||||
call!(usize_c))
|
||||
);
|
||||
preceded!(tag!("b "),
|
||||
call!(usize_c))
|
||||
);
|
||||
|
|
138
src/ui/mod.rs
138
src/ui/mod.rs
|
@ -19,57 +19,41 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
The UI module has an Entity-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct.
|
||||
|
||||
`State` owns all the Entities of the UI, which are currently plain Containers for `Component`s. In the application's main event loop, input is handed to the state in the form of `UIEvent` objects which traverse the entity graph. Components decide to handle each input or not.
|
||||
|
||||
Input is received in the main loop from threads which listen on the stdin for user input, observe folders for file changes etc. The relevant struct is `ThreadEvent`.
|
||||
*/
|
||||
|
||||
#[macro_use]
|
||||
pub mod position;
|
||||
mod position;
|
||||
pub mod components;
|
||||
pub mod cells;
|
||||
mod cells;
|
||||
|
||||
#[macro_use]
|
||||
mod execute;
|
||||
use self::execute::goto;
|
||||
|
||||
extern crate termion;
|
||||
extern crate melib;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
|
||||
pub use self::position::*;
|
||||
|
||||
use melib::*;
|
||||
|
||||
|
||||
use std;
|
||||
use termion::{clear, style, cursor};
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::event::{Key as TermionKey, };
|
||||
|
||||
use chan::Sender;
|
||||
|
||||
|
||||
use std::io::{Write, };
|
||||
use termion::input::TermRead;
|
||||
|
||||
use self::cells::*;
|
||||
pub use self::components::*;
|
||||
|
||||
/* Color pairs; foreground && background. */
|
||||
/// Default color.
|
||||
pub static COLOR_PAIR_DEFAULT: i16 = 1;
|
||||
/// Highlighted cursor line in index view.
|
||||
pub static COLOR_PAIR_CURSOR: i16 = 2;
|
||||
/// Header colour in pager view.
|
||||
pub static COLOR_PAIR_HEADERS: i16 = 3;
|
||||
/// Indentation symbol color in index view.
|
||||
pub static COLOR_PAIR_THREAD_INDENT: i16 = 4;
|
||||
/// Line color for odd entries in index view.
|
||||
pub static COLOR_PAIR_THREAD_ODD: i16 = 5;
|
||||
/// Line color for even entries in index view.
|
||||
pub static COLOR_PAIR_THREAD_EVEN: i16 = 6;
|
||||
/// Line color for unread odd entries in index view.
|
||||
pub static COLOR_PAIR_UNREAD_ODD: i16 = 7;
|
||||
/// Line color for unread even entries in index view.
|
||||
pub static COLOR_PAIR_UNREAD_EVEN: i16 = 8;
|
||||
extern crate melib;
|
||||
use melib::*;
|
||||
|
||||
use std;
|
||||
use std::io::{Write, };
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
extern crate termion;
|
||||
use termion::{clear, style, cursor};
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::event::{Key as TermionKey, };
|
||||
use termion::input::TermRead;
|
||||
|
||||
use chan::Sender;
|
||||
|
||||
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
||||
/// to the main process.
|
||||
|
@ -103,6 +87,7 @@ pub enum UIEventType {
|
|||
}
|
||||
|
||||
|
||||
/// An event passed from `State` to its Entities.
|
||||
#[derive(Debug)]
|
||||
pub struct UIEvent {
|
||||
pub id: u64,
|
||||
|
@ -124,6 +109,7 @@ impl fmt::Display for UIMode {
|
|||
}
|
||||
}
|
||||
|
||||
/// An event notification that is passed to Entities for handling.
|
||||
pub struct Notification {
|
||||
title: String,
|
||||
content: String,
|
||||
|
@ -131,6 +117,7 @@ pub struct Notification {
|
|||
timestamp: std::time::Instant,
|
||||
}
|
||||
|
||||
/// A context container for loaded settings, accounts, UI changes, etc.
|
||||
pub struct Context {
|
||||
pub accounts: Vec<Account>,
|
||||
settings: Settings,
|
||||
|
@ -149,6 +136,8 @@ impl Context {
|
|||
}
|
||||
|
||||
|
||||
/// A State object to manage and own components and entities of the UI. `State` is responsible for
|
||||
/// managing the terminal and interfacing with `melib`
|
||||
pub struct State<W: Write> {
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
|
@ -285,24 +274,22 @@ impl<W: Write> State<W> {
|
|||
pub fn register_entity(&mut self, entity: Entity) {
|
||||
self.entities.push(entity);
|
||||
}
|
||||
/// Convert user commands to actions/method calls.
|
||||
fn parse_command(&mut self, cmd: String) {
|
||||
//TODO: Make ex mode useful
|
||||
eprintln!("received command: {}", cmd);
|
||||
|
||||
let result = goto(&cmd.as_bytes()).to_full_result();
|
||||
eprintln!("result is {:?}", result);
|
||||
|
||||
if let Ok(v) = result {
|
||||
|
||||
self.refresh_mailbox(0, v);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn rcv_event(&mut self, event: UIEvent) {
|
||||
match event.event_type {
|
||||
// Command type is only for the State itself.
|
||||
// Command type is handled only by State.
|
||||
UIEventType::Command(cmd) => {
|
||||
self.parse_command(cmd);
|
||||
return;
|
||||
|
@ -331,29 +318,8 @@ impl<W: Write> State<W> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn convert_key(k: TermionKey ) -> Key {
|
||||
match k {
|
||||
TermionKey::Backspace => Key::Backspace,
|
||||
TermionKey::Left => Key::Left,
|
||||
TermionKey::Right => Key::Right,
|
||||
TermionKey::Up => Key::Up,
|
||||
TermionKey::Down => Key::Down,
|
||||
TermionKey::Home => Key::Home,
|
||||
TermionKey::End => Key::End,
|
||||
TermionKey::PageUp => Key::PageUp,
|
||||
TermionKey::PageDown => Key::PageDown,
|
||||
TermionKey::Delete => Key::Delete,
|
||||
TermionKey::Insert => Key::Insert,
|
||||
TermionKey::F(u) => Key::F(u),
|
||||
TermionKey::Char(c) => Key::Char(c),
|
||||
TermionKey::Alt(c) => Key::Alt(c),
|
||||
TermionKey::Ctrl(c) => Key::Ctrl(c),
|
||||
TermionKey::Null => Key::Null,
|
||||
TermionKey::Esc => Key::Esc,
|
||||
_ => Key::Char(' '),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Pass Ctrl C etc to the terminal.
|
||||
#[derive(Debug)]
|
||||
pub enum Key {
|
||||
/// Backspace.
|
||||
|
@ -396,12 +362,36 @@ pub enum Key {
|
|||
Esc,
|
||||
}
|
||||
|
||||
pub fn get_events<F>(stdin: std::io::Stdin, mut closure: F) where F: FnMut(Key) -> (){
|
||||
let stdin = stdin.lock();
|
||||
for c in stdin.keys() {
|
||||
if let Ok(k) = c {
|
||||
let k = convert_key(k);
|
||||
closure(k);
|
||||
impl From<TermionKey> for Key {
|
||||
fn from(k: TermionKey ) -> Self {
|
||||
match k {
|
||||
TermionKey::Backspace => Key::Backspace,
|
||||
TermionKey::Left => Key::Left,
|
||||
TermionKey::Right => Key::Right,
|
||||
TermionKey::Up => Key::Up,
|
||||
TermionKey::Down => Key::Down,
|
||||
TermionKey::Home => Key::Home,
|
||||
TermionKey::End => Key::End,
|
||||
TermionKey::PageUp => Key::PageUp,
|
||||
TermionKey::PageDown => Key::PageDown,
|
||||
TermionKey::Delete => Key::Delete,
|
||||
TermionKey::Insert => Key::Insert,
|
||||
TermionKey::F(u) => Key::F(u),
|
||||
TermionKey::Char(c) => Key::Char(c),
|
||||
TermionKey::Alt(c) => Key::Alt(c),
|
||||
TermionKey::Ctrl(c) => Key::Ctrl(c),
|
||||
TermionKey::Null => Key::Null,
|
||||
TermionKey::Esc => Key::Esc,
|
||||
_ => Key::Char(' '),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_events(stdin: std::io::Stdin, mut closure: impl FnMut(Key)) -> (){
|
||||
let stdin = stdin.lock();
|
||||
for c in stdin.keys() {
|
||||
if let Ok(k) = c {
|
||||
closure(Key::from(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
/*!
|
||||
Simple type definitions and macro helper for a (x,y) position on the terminal and the areas they define.
|
||||
|
||||
An `Area` consists of two points: the upper left and bottom right corners.
|
||||
*/
|
||||
|
||||
/// A `(x, y)` position on screen.
|
||||
pub type Pos = (usize, usize);
|
||||
|
||||
|
@ -18,6 +24,7 @@ pub fn set_y(p: Pos, new_y: usize) -> Pos {
|
|||
(p.0, new_y)
|
||||
}
|
||||
|
||||
/// An `Area` consists of two points: the upper left and bottom right corners.
|
||||
pub type Area = (Pos, Pos);
|
||||
|
||||
#[macro_export]
|
||||
|
@ -25,15 +32,19 @@ macro_rules! upper_left { ($a:expr) => ( $a.0 ) }
|
|||
#[macro_export]
|
||||
macro_rules! bottom_right { ($a:expr) => ( $a.1 ) }
|
||||
#[macro_export]
|
||||
macro_rules! is_valid_area { ($a:expr) => { {
|
||||
let upper_left = upper_left!($a);
|
||||
let bottom_right = bottom_right!($a);
|
||||
if get_y(upper_left) > get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
macro_rules! is_valid_area { ($a:expr) =>
|
||||
{
|
||||
{
|
||||
let upper_left = upper_left!($a);
|
||||
let bottom_right = bottom_right!($a);
|
||||
if get_y(upper_left) > get_y(bottom_right) || get_x(upper_left) > get_x(bottom_right) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
} } }
|
||||
}
|
||||
}
|
||||
|
||||
/// A `(cols, rows)` size.
|
||||
pub type Size = (usize, usize);
|
||||
|
@ -46,45 +57,3 @@ pub trait HasPosition {
|
|||
fn origin(&self) -> Pos;
|
||||
fn set_origin(&mut self, new_origin: Pos);
|
||||
}
|
||||
|
||||
/// A cursor position.
|
||||
pub struct Cursor {
|
||||
pos: Option<Pos>,
|
||||
last_pos: Option<Pos>,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
pub fn new() -> Cursor {
|
||||
Cursor {
|
||||
pos: None,
|
||||
last_pos: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the current and last coordinates are sequential and returns `true` if they
|
||||
/// are and `false` otherwise.
|
||||
pub fn is_seq(&self) -> bool {
|
||||
if let Some((cx, cy)) = self.pos {
|
||||
if let Some((lx, ly)) = self.last_pos {
|
||||
(lx + 1, ly) == (cx, cy)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> Option<Pos> {
|
||||
self.pos
|
||||
}
|
||||
|
||||
pub fn set_pos(&mut self, newpos: Option<Pos>) {
|
||||
self.last_pos = self.pos;
|
||||
self.pos = newpos;
|
||||
}
|
||||
|
||||
pub fn invalidate_last_pos(&mut self) {
|
||||
self.last_pos = None;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue