Browse Source

Index scrolling, dummy backends, and some pager settings

embed
Manos Pitsidianakis 4 years ago
parent
commit
8c7a0ae540
Signed by: epilys GPG Key ID: 73627C2F690DF710
  1. 125
      src/bin.rs
  2. 12
      src/conf/mod.rs
  3. 15
      src/conf/pager.rs
  4. 79
      src/mailbox/backends/imap.rs
  5. 1
      src/mailbox/backends/maildir.rs
  6. 81
      src/mailbox/backends/mbox.rs
  7. 6
      src/mailbox/backends/mod.rs
  8. 106
      src/ui/components/mail.rs
  9. 6
      src/ui/components/mod.rs
  10. 6
      src/ui/components/utilities.rs
  11. 61
      src/ui/mod.rs

125
src/bin.rs

@ -30,6 +30,8 @@ pub use melib::*;
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
use std::thread;
use std::io::{stdout, stdin, };
use std::collections::VecDeque;
use std::time::{Duration, Instant};
fn main() {
/* Lock all stdios */
@ -38,9 +40,9 @@ fn main() {
let stdin = stdin();
let stdin = stdin;
/*
let _stderr = stderr();
let mut _stderr = _stderr.lock();
*/
let _stderr = stderr();
let mut _stderr = _stderr.lock();
*/
let set = Settings::new();
@ -48,16 +50,84 @@ fn main() {
let (sender, receiver): (SyncSender<ThreadEvent>, Receiver<ThreadEvent>) = sync_channel(::std::mem::size_of::<ThreadEvent>());
{
let mut cmd_queue = VecDeque::with_capacity(5);
let sender = sender.clone();
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
get_events(stdin, |k| { sender.send(ThreadEvent::Input(k)).unwrap(); })
}).unwrap();
get_events(stdin, move | k| {
eprintln!("{:?}: queue is {:?}", Instant::now(), cmd_queue);
let front: Option<(Instant, char)> = cmd_queue.front().map(|v: &(Instant, char)| { v.clone() });
let back: Option<(Instant, char)> = cmd_queue.back().map(|v: &(Instant, char)| { v.clone() });
let mut push: Option<(Instant, char)> = None;
if let Key::Char(v) = k {
if v == 'g' {
eprintln!("{:?}: got 'g' in thread",Instant::now());
push = Some((Instant::now(), v));
} else if v > '/' && v < ':' {
eprintln!("{:?}: got '{}' in thread", Instant::now(), v);
if let Some((_, 'g')) = front {
eprintln!("{:?}: 'g' is front", Instant::now());
match back {
Some((i, cmd)) if cmd != 'g' => {
let (i, cmd) = back.unwrap();
let n = cmd as u8;
eprintln!("{:?}: check for num c={}, n={}", Instant::now(),cmd, n);
if n > 0x2f && n < 0x3a {
eprintln!("{:?}: got a num {}", Instant::now(), cmd);
let now = Instant::now();
if now - i < Duration::from_millis(300) {
push = Some((now,cmd));
let ten_millis = Duration::from_millis(10);
return;
}
}
},
Some((i, cmd)) => {
let n = v as u8;
eprintln!("{:?}: check for num c={}, n={}", Instant::now(),v, n);
if n > 0x2f && n < 0x3a {
eprintln!("{:?}: got a num {}", Instant::now(), v);
let now = Instant::now();
if now - i < Duration::from_millis(300) {
push = Some((now,v));
}
cmd_queue.pop_front();
let mut s = String::with_capacity(3);
for (_, c) in cmd_queue.iter() {
s.push(*c);
}
s.push(v);
let times = s.parse::<usize>();
eprintln!("{:?}: parsed {:?}", Instant::now(), times);
if let Ok(g) = times {
sender.send(ThreadEvent::GoCmd(g)).unwrap();
return;
}
}
},
None => {},
}
}
}
if let Some(v) = push {
cmd_queue.push_back(v);
return;
}
}
if push.is_none() {sender.send(ThreadEvent::Input(k)).unwrap();}
})}).unwrap();
}
//let mailbox = Mailbox::new("/home/epilys/Downloads/rust/nutt/Inbox4");
let mut j = 0;
let folder_length = set.accounts["norn"].folders.len();
let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone(), backends);
let folder_length = set.accounts["test_account"].folders.len();
let mut account = Account::new("test_account".to_string(), set.accounts["test_account"].clone(), backends);
{
let sender = sender.clone();
account.watch(RefreshEventConsumer::new(Box::new(move |r| {
@ -69,13 +139,14 @@ fn main() {
let mut state = State::new(_stdout);
let mut state = State::new(_stdout, set);
let a = Entity {component: Box::new(AccountMenu::new(&account)) };
let listing = MailListing::new(Mailbox::new_dummy());
let b = Entity { component: Box::new(listing) };
let window = Entity { component: Box::new(VSplit::new(a,b,90)) };
state.register_entity(window);
state.render();
'main: loop {
let mailbox = &mut account[j];
@ -93,20 +164,20 @@ fn main() {
ThreadEvent::Input(k) => {
match k {
key @ Key::Char('j') | key @ Key::Char('k') => {
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
state.render();
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
state.render();
},
key @ Key::Up | key @ Key::Down => {
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
state.render();
}
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(key)});
state.render();
}
Key::Char('\n') => {
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Char('\n'))});
state.render();
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Char('\n'))});
state.render();
}
Key::Char('i') | Key::Esc => {
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Esc)});
state.render();
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::Input(Key::Esc)});
state.render();
}
Key::F(_) => {
},
@ -121,13 +192,33 @@ fn main() {
j -= 1;
break 'inner;
},
Key::Char(k @ 'g') => {
},
Key::Char(v) if v > '/' && v < ':' => {
},
_ => {}
}
},
ThreadEvent::RefreshMailbox { name : n } => {
eprintln!("Refresh mailbox {}", n);
},
ThreadEvent::UIEventType(e) => {
state.rcv_event(UIEvent { id: 0, event_type: e});
state.render();
},
ThreadEvent::GoCmd(v) => {
eprintln!("got go cmd with {:?}", v);
},
}
/*
if let Some((inst, 'g')) = cmd_queue.front() {
eprintln!("g at front");
if (Instant::now - inst >= Duration::from_millis(300)) {
}
}
*/
}
}
}

12
src/conf/mod.rs

@ -22,6 +22,11 @@
extern crate config;
extern crate xdg;
extern crate serde;
pub mod pager;
use pager::PagerSettings;
use std::collections::HashMap;
use std::io;
@ -63,9 +68,12 @@ struct FileAccount {
threaded: bool,
}
#[derive(Debug, Deserialize, Default)]
struct FileSettings {
accounts: HashMap<String, FileAccount>,
#[serde(default)]
pager: PagerSettings,
}
#[derive(Debug, Clone)]
@ -89,6 +97,8 @@ impl AccountSettings {
#[derive(Debug)]
pub struct Settings {
pub accounts: HashMap<String, AccountSettings>,
pub pager: PagerSettings,
}
@ -166,6 +176,6 @@ impl Settings {
);
}
Settings { accounts: s }
Settings { accounts: s, pager: fs.pager }
}
}

15
src/conf/pager.rs

@ -0,0 +1,15 @@
fn false_val () -> bool {
false
}
fn zero_val () -> usize {
0
}
#[derive(Debug, Deserialize, Default)]
pub struct PagerSettings {
#[serde(default = "zero_val")]
pager_context: usize,
#[serde(default = "false_val")]
pager_stop: bool,
}

79
src/mailbox/backends/imap.rs

@ -0,0 +1,79 @@
/*
* meli - mailbox module.
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use mailbox::email::{Envelope, Flag};
use error::{Result};
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
use conf::Folder;
/// `BackendOp` implementor for Imap
#[derive(Debug, Default, Clone)]
pub struct ImapOp {
}
impl ImapOp {
pub fn new(path: String) -> Self {
ImapOp {
}
}
}
impl BackendOp for ImapOp {
fn description(&self) -> String {
unimplemented!();
}
fn as_bytes(&mut self) -> Result<&[u8]> {
unimplemented!();
}
fn fetch_headers(&mut self) -> Result<&[u8]> {
unimplemented!();
}
fn fetch_body(&mut self) -> Result<&[u8]> {
unimplemented!();
}
fn fetch_flags(&self) -> Flag {
unimplemented!();
}
}
/// Imap backend
#[derive(Debug)]
pub struct ImapType {
}
impl MailBackend for ImapType {
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
unimplemented!();
}
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
unimplemented!();
}
}
impl ImapType {
pub fn new(path: &str) -> Self {
ImapType {
}
}
}

1
src/mailbox/backends/maildir.rs

@ -158,6 +158,7 @@ impl MailBackend for MaildirType {
continue;
}
let mut p = PathBuf::from(&f.get_path());
eprintln!("watching {:?}", f);
p.push("cur");
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
p.pop();

81
src/mailbox/backends/mbox.rs

@ -0,0 +1,81 @@
/*
* meli - mailbox module.
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use mailbox::email::{Envelope, Flag};
use error::{Result};
use mailbox::backends::{BackendOp, MailBackend, RefreshEventConsumer};
use conf::Folder;
/// `BackendOp` implementor for Mbox
#[derive(Debug, Default, Clone)]
pub struct MboxOp {
}
impl MboxOp {
pub fn new(path: String) -> Self {
MboxOp {
}
}
}
impl BackendOp for MboxOp {
fn description(&self) -> String {
unimplemented!();
}
fn as_bytes(&mut self) -> Result<&[u8]> {
unimplemented!();
}
fn fetch_headers(&mut self) -> Result<&[u8]> {
unimplemented!();
}
fn fetch_body(&mut self) -> Result<&[u8]> {
unimplemented!();
}
fn fetch_flags(&self) -> Flag {
unimplemented!();
}
}
/// Mbox backend
#[derive(Debug)]
pub struct MboxType {
}
impl MailBackend for MboxType {
fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
unimplemented!();
}
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
unimplemented!();
}
}
impl MboxType {
pub fn new(path: &str) -> Self {
MboxType {
}
}
}

6
src/mailbox/backends/mod.rs

@ -19,10 +19,14 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
pub mod maildir;
pub mod mbox;
pub mod imap;
use conf::Folder;
use mailbox::email::{Envelope, Flag};
use mailbox::backends::maildir::MaildirType;
use mailbox::backends::mbox::MboxType;
use mailbox::backends::imap::ImapType;
use error::Result;
use std::fmt;
@ -43,6 +47,8 @@ impl Backends {
map: FnvHashMap::with_capacity_and_hasher(1, Default::default())
};
b.register("maildir".to_string(), Box::new(|| Box::new(MaildirType::new(""))));
b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new(""))));
b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new(""))));
b
}

106
src/ui/components/mail.rs

@ -5,7 +5,7 @@ fn make_entry_string(e: &Envelope, idx: usize) -> String {
format!("{} {} {:.85}",idx,&e.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string(),e.get_subject())
}
const MAX_WIDTH: usize = 500;
const MAX_cols: usize = 500;
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
/// `Pager`.
@ -34,7 +34,7 @@ impl MailListing {
cursor_pos: 0,
new_cursor_pos: 0,
length: length,
content: CellBuffer::new(MAX_WIDTH, length+1, Cell::with_char(' ')),
content: CellBuffer::new(MAX_cols, length+1, Cell::with_char(' ')),
dirty: false,
unfocused: false,
mailbox: mailbox,
@ -52,33 +52,51 @@ impl MailListing {
write_string_to_grid(&format!("Folder `{}` is empty.", self.mailbox.folder.get_name()), grid, Color::Default, Color::Default, upper_left, upper_left);
return;
}
let rows = get_y(bottom_right) - get_y(upper_left) + 1;
let prev_page_no = (self.cursor_pos).wrapping_div(rows);
let page_no = (self.new_cursor_pos).wrapping_div(rows);
/* If cursor position has changed, remove the highlight from the previous position and
* apply it in the new one. */
if self.cursor_pos != self.new_cursor_pos {
if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
for idx in [self.cursor_pos, self.new_cursor_pos].iter() {
if *idx >= self.length {
continue; //bounds check
}
let color = if self.cursor_pos == *idx { if *idx % 2 == 0 { Color::Byte(236) } else {Color::Default } } else { Color::Byte(246) };
let x = write_string_to_grid(&make_entry_string(&self.mailbox.collection[*idx], *idx), grid, Color::Default, color, set_y(upper_left, get_y(upper_left) + *idx), bottom_right);
for x in x..get_x(bottom_right)+1 {
grid[(x,get_y(upper_left)+idx)].set_ch(' ');
grid[(x,get_y(upper_left)+idx)].set_bg(color);
let x = write_string_to_grid(&make_entry_string(&self.mailbox.collection[*idx], *idx), grid, Color::Default, color, set_y(upper_left, get_y(upper_left)+(*idx % rows)), bottom_right);
for x in x..=get_x(bottom_right) {
grid[(x,get_y(upper_left)+(*idx % rows))].set_ch(' ');
grid[(x,get_y(upper_left)+(*idx % rows))].set_bg(color);
}
}
self.cursor_pos = self.new_cursor_pos;
return;
} else if self.cursor_pos != self.new_cursor_pos {
self.cursor_pos = self.new_cursor_pos;
}
let mut idx = 0;
for y in get_y(upper_left)..get_y(bottom_right) {
if idx == self.length {
let mut idx = page_no*rows;
for y in get_y(upper_left)..=get_y(bottom_right) {
if idx >= self.length {
clear_area(grid, set_y(upper_left, y), bottom_right);
break;
}
/* Write an entire line for each envelope entry. */
let color = if self.cursor_pos == idx { Color::Byte(246) } else { if idx % 2 == 0 { Color::Byte(236) } else {Color::Default } };
let color = if self.cursor_pos == idx {
Color::Byte(246)
} else {
if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
}
};
let x = write_string_to_grid(&make_entry_string(&self.mailbox.collection[idx], idx), grid, Color::Default, color, set_y(upper_left, y), bottom_right);
for x in x..get_x(bottom_right)+1 {
for x in x..=get_x(bottom_right) {
grid[(x,y)].set_ch(' ');
grid[(x,y)].set_bg(color);
}
@ -91,10 +109,10 @@ impl MailListing {
fn draw_mail_view(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
let ref mail = self.mailbox.collection[self.cursor_pos];
let height = get_y(bottom_right) - get_y(upper_left);
let width = get_x(bottom_right) - get_x(upper_left);
let rows = get_y(bottom_right) - get_y(upper_left);
let cols = get_x(bottom_right) - get_x(upper_left);
self.pager = Some(Pager::new(mail, height, width));
self.pager = Some(Pager::new(mail, rows, cols));
let pager = self.pager.as_mut().unwrap();
pager.draw(grid, upper_left,bottom_right);
}
@ -116,8 +134,8 @@ impl Component for MailListing {
/* Render the mail body in a pager, basically copy what HSplit does */
let total_rows = get_y(bottom_right) - get_y(upper_left);
/* TODO: define ratio in Configuration file */
let bottom_entity_height = (80*total_rows )/100;
let mid = get_y(upper_left) + total_rows - bottom_entity_height;
let bottom_entity_rows = (80*total_rows )/100;
let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
if !self.dirty {
if let Some(ref mut p) = self.pager {
@ -126,7 +144,7 @@ impl Component for MailListing {
return;
}
self.dirty = false;
self.draw_list(grid, upper_left, (get_x(bottom_right), get_y(upper_left)+ mid -1));
self.draw_list(grid, upper_left, (get_x(bottom_right), get_y(upper_left)+ mid-3));
if self.length == 0 {
return;
}
@ -138,42 +156,42 @@ impl Component for MailListing {
}
}
for i in get_x(upper_left)..get_x(bottom_right)+1 {
for i in get_x(upper_left)..get_x(bottom_right) {
grid[(i, mid)].set_ch('─');
}
}
let headers_height: usize = 6;
let headers_rows: usize = 6;
/* Draw header */
{
let ref mail = self.mailbox.collection[self.cursor_pos];
let x = write_string_to_grid(&format!("Date: {}", mail.get_date_as_str()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+1), set_y(upper_left, mid+1));
for x in x..get_x(bottom_right)+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: {}", mail.get_from()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+2), set_y(upper_left, mid+2));
for x in x..get_x(bottom_right)+1 {
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: {}", mail.get_to()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+3), set_y(upper_left, mid+3));
for x in x..get_x(bottom_right)+1 {
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: {}", mail.get_subject()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+4), set_y(upper_left, mid+4));
for x in x..get_x(bottom_right)+1 {
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: {}", mail.get_message_id_raw()), grid, Color::Byte(33), Color::Default, set_y(upper_left, mid+5), set_y(upper_left, mid+5));
for x in x..get_x(bottom_right)+1 {
for x in x..get_x(bottom_right) {
grid[(x, mid+5)].set_ch(' ');
grid[(x, mid+5)].set_bg(Color::Default);
grid[(x, mid+5)].set_fg(Color::Default);
@ -181,7 +199,7 @@ impl Component for MailListing {
}
/* Draw body */
self.draw_mail_view(grid, (get_x(upper_left), get_y(upper_left) + mid + headers_height), bottom_right);
self.draw_mail_view(grid, (get_x(upper_left), get_y(upper_left) + mid + headers_rows), bottom_right);
}
}
@ -194,7 +212,7 @@ impl Component for MailListing {
}
},
UIEventType::Input(Key::Down) => {
if self.length > 0 && self.cursor_pos < self.length - 1 {
if self.length > 0 && self.new_cursor_pos < self.length - 1 {
self.new_cursor_pos += 1;
self.dirty = true;
}
@ -232,7 +250,7 @@ pub struct AccountMenu {
entries: Vec<(usize, Folder)>,
dirty: bool,
name: String,
cursor: Option<String>,
}
impl AccountMenu {
@ -246,8 +264,13 @@ impl AccountMenu {
entries: entries,
dirty: true,
name: account.get_name().to_string(),
cursor: None,
}
}
fn highlight_folder(&mut self, m: &Mailbox) {
self.dirty = true;
self.cursor = Some(m.folder.get_name().to_string());
}
}
impl Component for AccountMenu {
@ -271,8 +294,8 @@ impl Component for AccountMenu {
}
let mut ind = 0;
let mut depth = String::from("");
let mut s = String::from(format!("{}\n", self.name));
let mut depth = String::from(" ");
let mut s = String::from(format!("\n\n {}\n", self.name));
fn pop(depth: &mut String) {
depth.pop();
depth.pop();
@ -289,7 +312,7 @@ impl Component for AccountMenu {
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, mut s: String) -> String {
let len = s.len();
s.insert_str(len, &format!("{}: {}\n", &entries[root].1.get_name(), entries[root].0));
s.insert_str(len, &format!("{}: {}\n ", entries[root].0, &entries[root].1.get_name()));
let children_no = entries[root].1.get_children().len();
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
let len = s.len();
@ -306,17 +329,28 @@ impl Component for AccountMenu {
}
let lines: Vec<&str> = s.lines().collect();
let lines_len = lines.len();
let mut idx = 0;
for y in get_y(upper_left)..get_y(bottom_right) {
if idx == self.entries.len() {
if idx == lines_len {
break;
}
let s = format!("{}", lines[idx]);
write_string_to_grid(&s, grid, Color::Red, Color::Default, set_y(upper_left, y), bottom_right);
let s = if idx == lines_len - 2 {
format!("{}", lines[idx].replace("├", "└"))
} else {
format!("{}", lines[idx])
};
write_string_to_grid(&s, grid, Color::Byte(30), Color::Default, set_y(upper_left, y), bottom_right);
idx += 1;
}
}
fn process_event(&mut self, _event: &UIEvent, _queue: &mut VecDeque<UIEvent>) {
return;
fn process_event(&mut self, event: &UIEvent, _queue: &mut VecDeque<UIEvent>) {
match event.event_type {
UIEventType::RefreshMailbox(ref m) => {
self.highlight_folder(m);
},
_ => {
},
}
}
}

6
src/ui/components/mod.rs

@ -94,7 +94,7 @@ fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_colo
if x == (get_x(bottom_right)) {
x = get_x(upper_left);
y += 1;
if y > (get_y(bottom_right)) {
if y == (get_y(bottom_right)) {
return x;
}
}
@ -103,8 +103,8 @@ fn write_string_to_grid(s: &str, grid: &mut CellBuffer, fg_color: Color, bg_colo
}
fn clear_area(grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
for y in get_y(upper_left)..get_y(bottom_right) {
for x in get_x(upper_left)..get_x(bottom_right) {
for y in get_y(upper_left)..=get_y(bottom_right) {
for x in get_x(upper_left)..=get_x(bottom_right) {
grid[(x,y)].set_ch(' ');
grid[(x,y)].set_bg(Color::Default);
grid[(x,y)].set_fg(Color::Default);

6
src/ui/components/utilities.rs

@ -49,15 +49,11 @@ impl HSplit {
impl Component for HSplit {
fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
grid[upper_left].set_ch('u');
let (a,b) = upper_left;
grid[(a+1,b)].set_ch('h');
let total_rows = get_y(bottom_right) - get_y(upper_left);
let bottom_entity_height = (self.ratio*total_rows )/100;
let mid = get_y(upper_left) + total_rows - bottom_entity_height;
for i in get_x(upper_left)..get_x(bottom_right)+1 {
for i in get_x(upper_left)..=get_x(bottom_right) {
grid[(i, mid)].set_ch('─');
}
let _ = self.top.component.draw(grid, upper_left, (get_x(bottom_right), get_y(upper_left) + mid-1));

61
src/ui/mod.rs

@ -54,6 +54,8 @@ pub enum ThreadEvent {
Input(Key),
/// A watched folder has been refreshed.
RefreshMailbox{ name: String },
UIEventType(UIEventType),
GoCmd(usize),
//Decode { _ }, // For gpg2 signature check
}
@ -93,6 +95,7 @@ pub enum UIEventType {
RefreshMailbox(Mailbox),
//Quit?
Resize,
ChangeMailbox(usize),
}
@ -103,12 +106,13 @@ pub struct UIEvent {
}
pub struct State<W: Write> {
width: usize,
height: usize,
cols: usize,
rows: usize,
grid: CellBuffer,
pub stdout: termion::raw::RawTerminal<W>,
stdout: termion::raw::RawTerminal<W>,
entities: Vec<Entity>,
settings: Settings,
}
@ -120,34 +124,37 @@ impl<W: Write> Drop for State<W> {
}
impl<W: Write> State<W> {
pub fn new(stdout: W) -> Self {
pub fn new(stdout: W, settings: Settings) -> Self {
let termsize = termion::terminal_size().ok();
let termwidth = termsize.map(|(w,_)| w);
let termheight = termsize.map(|(_,h)| h);
let width = termwidth.unwrap_or(0) as usize;
let height = termheight.unwrap_or(0) as usize;
let termcols = termsize.map(|(w,_)| w);
let termrows = termsize.map(|(_,h)| h);
let cols = termcols.unwrap_or(0) as usize;
let rows = termrows.unwrap_or(0) as usize;
let mut s = State {
width: width,
height: height,
cols: cols,
rows: rows,
//queue: VecDeque::new();
grid: CellBuffer::new(width+1, height+1, Cell::with_char(' ')),
grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
stdout: stdout.into_raw_mode().unwrap(),
entities: Vec::with_capacity(2),
entities: Vec::with_capacity(1),
settings: settings,
};
write!(s.stdout, "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1,1)).unwrap();
s
}
pub fn hello_w(&mut self) {
write!(self.stdout, "Hey there.").unwrap();
}
fn update_size(&mut self) {
/* update dimensions. TODO: Only do that in size change events. ie SIGWINCH */
let termsize = termion::terminal_size().ok();
let termwidth = termsize.map(|(w,_)| w);
let termheight = termsize.map(|(_,h)| h);
self.width = termwidth.unwrap_or(72) as usize;
self.height = termheight.unwrap_or(120) as usize;
let termcols = termsize.map(|(w,_)| w);
let termrows = termsize.map(|(_,h)| h);
if termcols.unwrap_or(72) as usize != self.cols || termrows.unwrap_or(120) as usize != self.rows {
eprintln!("Size updated, from ({}, {}) -> ({:?}, {:?})", self.cols, self.rows, termcols, termrows);
}
self.cols = termcols.unwrap_or(72) as usize;
self.rows = termrows.unwrap_or(120) as usize;
self.grid.resize(self.cols, self.rows, Cell::with_char(' '));
}
pub fn render(&mut self) {
@ -158,9 +165,9 @@ impl<W: Write> State<W> {
}
/* Only draw dirty areas */
for y in 0..self.height {
write!(self.stdout, "{}", cursor::Goto(1,y as u16)).unwrap();
for x in 0..self.width {
for y in 0..self.rows {
write!(self.stdout, "{}", cursor::Goto(1,(y+1) as u16)).unwrap();
for x in 0..self.cols {
let c = self.grid[(x,y)];
if c.get_bg() != cells::Color::Default {
@ -179,13 +186,12 @@ impl<W: Write> State<W> {
}
}
self.stdout.flush().unwrap();
}
pub fn draw_entity(&mut self, idx: usize) {
let ref mut entity = self.entities[idx];
eprintln!("Entity is {:?}", entity);
let upper_left = (1,1);
let bottom_right = (self.width, self.height);
eprintln!("Upper left is {:?} and bottom_right is {:?}", upper_left, bottom_right);
let upper_left = (0,0);
let bottom_right = (self.cols-1, self.rows-1);
entity.component.draw(&mut self.grid, upper_left, bottom_right);
}
@ -267,12 +273,11 @@ pub enum Key {
Esc,
}
pub fn get_events<F>(stdin: std::io::Stdin, closure: F) where F: Fn(Key) -> (){
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);
eprintln!("Received key: {:?}", k);
closure(k);
}
}

Loading…
Cancel
Save