edit headers with a form in composer
parent
f93310d424
commit
f748f1e692
|
@ -32,10 +32,6 @@ impl Default for Draft {
|
||||||
headers.insert("To".into(), "".into());
|
headers.insert("To".into(), "".into());
|
||||||
headers.insert("Cc".into(), "".into());
|
headers.insert("Cc".into(), "".into());
|
||||||
headers.insert("Bcc".into(), "".into());
|
headers.insert("Bcc".into(), "".into());
|
||||||
header_order.push("From".into());
|
|
||||||
header_order.push("To".into());
|
|
||||||
header_order.push("Cc".into());
|
|
||||||
header_order.push("Bcc".into());
|
|
||||||
|
|
||||||
let now: DateTime<Local> = Local::now();
|
let now: DateTime<Local> = Local::now();
|
||||||
headers.insert("Date".into(), now.to_rfc2822());
|
headers.insert("Date".into(), now.to_rfc2822());
|
||||||
|
@ -43,6 +39,10 @@ impl Default for Draft {
|
||||||
headers.insert("Message-ID".into(), random::gen_message_id());
|
headers.insert("Message-ID".into(), random::gen_message_id());
|
||||||
headers.insert("User-Agent".into(), "meli".into());
|
headers.insert("User-Agent".into(), "meli".into());
|
||||||
header_order.push("Date".into());
|
header_order.push("Date".into());
|
||||||
|
header_order.push("From".into());
|
||||||
|
header_order.push("To".into());
|
||||||
|
header_order.push("Cc".into());
|
||||||
|
header_order.push("Bcc".into());
|
||||||
header_order.push("Subject".into());
|
header_order.push("Subject".into());
|
||||||
header_order.push("Message-ID".into());
|
header_order.push("Message-ID".into());
|
||||||
header_order.push("User-Agent".into());
|
header_order.push("User-Agent".into());
|
||||||
|
|
|
@ -132,7 +132,7 @@ impl Component for ContactManager {
|
||||||
let mut fields = std::mem::replace(&mut self.form, FormWidget::default()).collect().unwrap();
|
let mut fields = std::mem::replace(&mut self.form, FormWidget::default()).collect().unwrap();
|
||||||
let fields: FnvHashMap<String, String> = fields.into_iter().map(|(s, v)| {
|
let fields: FnvHashMap<String, String> = fields.into_iter().map(|(s, v)| {
|
||||||
(s, match v {
|
(s, match v {
|
||||||
Field::Text(v, _) => v,
|
Field::Text(v, _) | Field::TextArea(v, _) => v,
|
||||||
Field::Choice(mut v, c) => v.remove(c),
|
Field::Choice(mut v, c) => v.remove(c),
|
||||||
})}).collect();
|
})}).collect();
|
||||||
let mut new_card = Card::from(fields);
|
let mut new_card = Card::from(fields);
|
||||||
|
|
|
@ -27,10 +27,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum Cursor {
|
enum Cursor {
|
||||||
From,
|
Headers,
|
||||||
To,
|
|
||||||
Cc,
|
|
||||||
Bcc,
|
|
||||||
Body,
|
Body,
|
||||||
Attachments,
|
Attachments,
|
||||||
}
|
}
|
||||||
|
@ -44,6 +41,7 @@ pub struct Composer {
|
||||||
|
|
||||||
pager: Pager,
|
pager: Pager,
|
||||||
draft: Draft,
|
draft: Draft,
|
||||||
|
form: FormWidget,
|
||||||
|
|
||||||
mode: ViewMode,
|
mode: ViewMode,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
@ -56,10 +54,11 @@ impl Default for Composer {
|
||||||
reply_context: None,
|
reply_context: None,
|
||||||
account_cursor: 0,
|
account_cursor: 0,
|
||||||
|
|
||||||
cursor: Cursor::To,
|
cursor: Cursor::Headers,
|
||||||
|
|
||||||
pager: Pager::default(),
|
pager: Pager::default(),
|
||||||
draft: Draft::default(),
|
draft: Draft::default(),
|
||||||
|
form: FormWidget::default(),
|
||||||
|
|
||||||
mode: ViewMode::Overview,
|
mode: ViewMode::Overview,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
|
@ -162,6 +161,28 @@ impl Composer {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_draft(&mut self) {
|
||||||
|
let header_values = self.form.values_mut();
|
||||||
|
let draft_header_map = self.draft.headers_mut();
|
||||||
|
/* avoid extra allocations by updating values instead of inserting */
|
||||||
|
for (k, v) in draft_header_map.iter_mut() {
|
||||||
|
if let Some(vn) = header_values.remove(k) {
|
||||||
|
std::mem::swap(v, &mut vn.into_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_form(&mut self) {
|
||||||
|
let old_cursor = self.form.cursor();
|
||||||
|
self.form = FormWidget::new("Save".into());
|
||||||
|
self.form.hide_buttons();
|
||||||
|
self.form.set_cursor(old_cursor);
|
||||||
|
let headers = self.draft.headers();
|
||||||
|
for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] {
|
||||||
|
self.form.push((k.into(), headers[k].to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_header_table(&mut self, grid: &mut CellBuffer, area: Area) {
|
fn draw_header_table(&mut self, grid: &mut CellBuffer, area: Area) {
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
@ -170,12 +191,7 @@ impl Composer {
|
||||||
{
|
{
|
||||||
let (mut x, mut y) = upper_left;
|
let (mut x, mut y) = upper_left;
|
||||||
for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] {
|
for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] {
|
||||||
let bg_color = match self.cursor {
|
let bg_color = Color::Default;
|
||||||
Cursor::Cc if k == "Cc" => Color::Byte(240),
|
|
||||||
Cursor::Bcc if k == "Bcc" => Color::Byte(240),
|
|
||||||
Cursor::To if k == "To" => Color::Byte(240),
|
|
||||||
_ => Color::Default,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let update = {
|
let update = {
|
||||||
|
@ -239,6 +255,7 @@ impl Component for Composer {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
if !self.initialized {
|
if !self.initialized {
|
||||||
clear_area(grid, area);
|
clear_area(grid, area);
|
||||||
|
self.update_form();
|
||||||
self.initialized = true;
|
self.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +263,7 @@ impl Component for Composer {
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
let upper_left = set_y(upper_left, get_y(upper_left) + 1);
|
let upper_left = set_y(upper_left, get_y(upper_left) + 1);
|
||||||
let header_height = 5;
|
let header_height = self.form.len() + 1;
|
||||||
let width = if width!(area) > 80 && self.reply_context.is_some() {
|
let width = if width!(area) > 80 && self.reply_context.is_some() {
|
||||||
width!(area) / 2
|
width!(area) / 2
|
||||||
} else {
|
} else {
|
||||||
|
@ -309,13 +326,16 @@ impl Component for Composer {
|
||||||
"From".into(),
|
"From".into(),
|
||||||
get_display_name(context, self.account_cursor),
|
get_display_name(context, self.account_cursor),
|
||||||
);
|
);
|
||||||
|
clear_area(grid, body_area);
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Regardless of view mode, do the following */
|
/* Regardless of view mode, do the following */
|
||||||
clear_area(grid, header_area);
|
clear_area(grid, header_area);
|
||||||
clear_area(grid, body_area);
|
/*
|
||||||
self.draw_header_table(grid, header_area);
|
self.draw_header_table(grid, header_area);
|
||||||
|
*/
|
||||||
|
self.form.draw(grid, header_area, context);
|
||||||
|
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Overview | ViewMode::Pager => {
|
ViewMode::Overview | ViewMode::Pager => {
|
||||||
|
@ -409,54 +429,11 @@ impl Component for Composer {
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
if self.form.process_event(event, context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
match event.event_type {
|
match event.event_type {
|
||||||
UIEventType::Input(Key::Up) if self.mode.is_overview() => {
|
|
||||||
match self.cursor {
|
|
||||||
Cursor::From => {},
|
|
||||||
Cursor::To => {
|
|
||||||
self.cursor = Cursor::From;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::Cc => {
|
|
||||||
self.cursor = Cursor::To;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::Bcc => {
|
|
||||||
self.cursor = Cursor::Cc;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::Body => {
|
|
||||||
self.cursor = Cursor::Bcc;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
UIEventType::Input(Key::Down) if self.mode.is_overview() => {
|
|
||||||
match self.cursor {
|
|
||||||
Cursor::From => {
|
|
||||||
self.cursor = Cursor::To;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::To => {
|
|
||||||
self.cursor = Cursor::Cc;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::Cc => {
|
|
||||||
self.cursor = Cursor::Bcc;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::Bcc => {
|
|
||||||
self.cursor = Cursor::Body;
|
|
||||||
self.dirty = true;
|
|
||||||
},
|
|
||||||
Cursor::Body => {},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
UIEventType::Input(Key::Esc) if self.mode.is_selector() => {
|
UIEventType::Input(Key::Esc) if self.mode.is_selector() => {
|
||||||
self.mode = ViewMode::Overview;
|
self.mode = ViewMode::Overview;
|
||||||
return true;
|
return true;
|
||||||
|
@ -473,6 +450,7 @@ impl Component for Composer {
|
||||||
UIEventType::Resize => {
|
UIEventType::Resize => {
|
||||||
self.set_dirty();
|
self.set_dirty();
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
/* Switch e-mail From: field to the `left` configured account. */
|
/* Switch e-mail From: field to the `left` configured account. */
|
||||||
UIEventType::Input(Key::Left) if self.cursor == Cursor::From => {
|
UIEventType::Input(Key::Left) if self.cursor == Cursor::From => {
|
||||||
self.account_cursor = self.account_cursor.saturating_sub(1);
|
self.account_cursor = self.account_cursor.saturating_sub(1);
|
||||||
|
@ -494,7 +472,13 @@ impl Component for Composer {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}*/
|
||||||
|
UIEventType::Input(Key::Up) => {
|
||||||
|
self.cursor = Cursor::Headers;
|
||||||
|
},
|
||||||
|
UIEventType::Input(Key::Down) => {
|
||||||
|
self.cursor = Cursor::Body;
|
||||||
|
},
|
||||||
UIEventType::Input(Key::Char(key)) if self.mode.is_discard() => {
|
UIEventType::Input(Key::Char(key)) if self.mode.is_discard() => {
|
||||||
match (key, &self.mode) {
|
match (key, &self.mode) {
|
||||||
('x', ViewMode::Discard(u)) => {
|
('x', ViewMode::Discard(u)) => {
|
||||||
|
@ -537,15 +521,15 @@ impl Component for Composer {
|
||||||
self.set_dirty();
|
self.set_dirty();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEventType::Input(Key::Char('e')) => {
|
UIEventType::Input(Key::Char('e')) if self.cursor == Cursor::Body => {
|
||||||
match self.cursor {
|
|
||||||
Cursor::Body => {
|
|
||||||
/* Edit draft in $EDITOR */
|
/* Edit draft in $EDITOR */
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
||||||
{
|
{
|
||||||
context.input_kill();
|
context.input_kill();
|
||||||
}
|
}
|
||||||
|
/* update Draft's headers based on form values */
|
||||||
|
self.update_draft();
|
||||||
let mut f =
|
let mut f =
|
||||||
create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None);
|
create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None);
|
||||||
//let mut f = Box::new(std::fs::File::create(&dir).unwrap());
|
//let mut f = Box::new(std::fs::File::create(&dir).unwrap());
|
||||||
|
@ -561,12 +545,14 @@ impl Component for Composer {
|
||||||
let result = f.read_to_string();
|
let result = f.read_to_string();
|
||||||
self.draft = Draft::from_str(result.as_str()).unwrap();
|
self.draft = Draft::from_str(result.as_str()).unwrap();
|
||||||
self.pager.update_from_str(self.draft.body());
|
self.pager.update_from_str(self.draft.body());
|
||||||
|
self.update_form();
|
||||||
context.replies.push_back(UIEvent {
|
context.replies.push_back(UIEvent {
|
||||||
id: 0,
|
id: 0,
|
||||||
event_type: UIEventType::Fork(ForkType::Finished),
|
event_type: UIEventType::Fork(ForkType::Finished),
|
||||||
});
|
});
|
||||||
context.restore_input();
|
context.restore_input();
|
||||||
},
|
/*
|
||||||
|
|
||||||
Cursor::To | Cursor::Cc | Cursor::Bcc => {
|
Cursor::To | Cursor::Cc | Cursor::Bcc => {
|
||||||
let account = &context.accounts[self.account_cursor];
|
let account = &context.accounts[self.account_cursor];
|
||||||
let mut entries = account.address_book.values().map(|v| (v.id().as_bytes().to_vec(), v.email().to_string())).collect();
|
let mut entries = account.address_book.values().map(|v| (v.id().as_bytes().to_vec(), v.email().to_string())).collect();
|
||||||
|
@ -578,7 +564,7 @@ impl Component for Composer {
|
||||||
Cursor::From => {
|
Cursor::From => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -592,13 +578,13 @@ impl Component for Composer {
|
||||||
.reply_context
|
.reply_context
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|(_, p)| p.is_dirty())
|
.map(|(_, p)| p.is_dirty())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false) || self.form.is_dirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_dirty(&mut self) {
|
fn set_dirty(&mut self) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.initialized = false;
|
|
||||||
self.pager.set_dirty();
|
self.pager.set_dirty();
|
||||||
|
self.form.set_dirty();
|
||||||
if let Some((_, ref mut view)) = self.reply_context {
|
if let Some((_, ref mut view)) = self.reply_context {
|
||||||
view.set_dirty();
|
view.set_dirty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ impl Default for FormFocus {
|
||||||
pub enum Field {
|
pub enum Field {
|
||||||
Text(String, Cursor),
|
Text(String, Cursor),
|
||||||
Choice(Vec<String>, Cursor),
|
Choice(Vec<String>, Cursor),
|
||||||
|
TextArea(String, Cursor),
|
||||||
}
|
}
|
||||||
|
|
||||||
use Field::*;
|
use Field::*;
|
||||||
|
@ -36,6 +37,9 @@ impl Field {
|
||||||
Text(ref s, _) => {
|
Text(ref s, _) => {
|
||||||
s
|
s
|
||||||
},
|
},
|
||||||
|
TextArea(ref s, _) => {
|
||||||
|
s
|
||||||
|
},
|
||||||
Choice(ref v, cursor) => {
|
Choice(ref v, cursor) => {
|
||||||
if v.is_empty() {
|
if v.is_empty() {
|
||||||
""
|
""
|
||||||
|
@ -46,11 +50,27 @@ impl Field {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
match self {
|
||||||
|
Text(s, _) => {
|
||||||
|
s
|
||||||
|
},
|
||||||
|
TextArea(s, _) => {
|
||||||
|
s
|
||||||
|
},
|
||||||
|
Choice(mut v, cursor) => {
|
||||||
|
v.remove(cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_cursor(&self, grid: &mut CellBuffer, area: Area) {
|
fn draw_cursor(&self, grid: &mut CellBuffer, area: Area) {
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
match self {
|
match self {
|
||||||
Text(_, cursor) => {
|
Text(_, cursor) => {
|
||||||
change_colors(grid, (pos_inc(upper_left, (*cursor, 0)), (pos_inc(upper_left, (*cursor, 0)))), Color::Default, Color::Byte(248));
|
change_colors(grid, (pos_inc(upper_left, (*cursor, 0)), (pos_inc(upper_left, (*cursor, 0)))), Color::Default, Color::Byte(248));
|
||||||
|
},
|
||||||
|
TextArea(_, _) => {
|
||||||
},
|
},
|
||||||
Choice(_, cursor) => {
|
Choice(_, cursor) => {
|
||||||
|
|
||||||
|
@ -67,14 +87,14 @@ impl Component for Field {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
area,
|
area,
|
||||||
false);
|
true);
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
match event.event_type {
|
match event.event_type {
|
||||||
UIEventType::InsertInput(Key::Right) => {
|
UIEventType::InsertInput(Key::Right) => {
|
||||||
match self {
|
match self {
|
||||||
Text(ref s, ref mut cursor) => {
|
TextArea(ref s, ref mut cursor) | Text(ref s, ref mut cursor) => {
|
||||||
if *cursor < s.len().saturating_sub(1) {
|
if *cursor < s.len() {
|
||||||
*cursor += 1;
|
*cursor += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -89,7 +109,7 @@ impl Component for Field {
|
||||||
},
|
},
|
||||||
UIEventType::InsertInput(Key::Left) => {
|
UIEventType::InsertInput(Key::Left) => {
|
||||||
match self {
|
match self {
|
||||||
Text(_, ref mut cursor) => {
|
TextArea(_, ref mut cursor) | Text(_, ref mut cursor) => {
|
||||||
if *cursor == 0 {
|
if *cursor == 0 {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -106,17 +126,23 @@ impl Component for Field {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UIEventType::InsertInput(Key::Char(k)) => {
|
UIEventType::InsertInput(Key::Char(k)) => {
|
||||||
if let Text(ref mut s, ref mut cursor) = self {
|
match self {
|
||||||
s.insert(*cursor, k);
|
Text(ref mut s, ref mut cursor) | TextArea(ref mut s, ref mut cursor) => {
|
||||||
*cursor += 1;
|
s.insert(*cursor, k);
|
||||||
|
*cursor += 1;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UIEventType::InsertInput(Key::Backspace) => {
|
UIEventType::InsertInput(Key::Backspace) => {
|
||||||
if let Text(ref mut s, ref mut cursor) = self {
|
match self {
|
||||||
|
Text(ref mut s, ref mut cursor) | TextArea(ref mut s, ref mut cursor) => {
|
||||||
if *cursor > 0 {
|
if *cursor > 0 {
|
||||||
*cursor -= 1;
|
*cursor -= 1;
|
||||||
s.pop();
|
s.remove(*cursor);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -142,13 +168,13 @@ impl fmt::Display for Field {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct FormWidget {
|
pub struct FormWidget {
|
||||||
fields: FnvHashMap<String, Field>,
|
fields: FnvHashMap<String, Field>,
|
||||||
cursors: Vec<usize>,
|
|
||||||
layout: Vec<String>,
|
layout: Vec<String>,
|
||||||
buttons: ButtonWidget<bool>,
|
buttons: ButtonWidget<bool>,
|
||||||
|
|
||||||
field_name_max_length: usize,
|
field_name_max_length: usize,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
focus: FormFocus,
|
focus: FormFocus,
|
||||||
|
hide_buttons: bool,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,10 +189,27 @@ impl FormWidget {
|
||||||
FormWidget {
|
FormWidget {
|
||||||
buttons: ButtonWidget::new((action, true)),
|
buttons: ButtonWidget::new((action, true)),
|
||||||
focus: FormFocus::Fields,
|
focus: FormFocus::Fields,
|
||||||
|
hide_buttons: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cursor(&self) -> usize {
|
||||||
|
self.cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor(&mut self, new_cursor: usize) {
|
||||||
|
self.cursor = new_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide_buttons(&mut self) {
|
||||||
|
self.hide_buttons = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.layout.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_button(&mut self, val: (String, bool)) {
|
pub fn add_button(&mut self, val: (String, bool)) {
|
||||||
self.buttons.push(val);
|
self.buttons.push(val);
|
||||||
}
|
}
|
||||||
|
@ -175,19 +218,25 @@ impl FormWidget {
|
||||||
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
||||||
self.layout.push(value.0.clone());
|
self.layout.push(value.0.clone());
|
||||||
self.fields.insert(value.0, Choice(value.1, 0));
|
self.fields.insert(value.0, Choice(value.1, 0));
|
||||||
self.cursors.push(0);
|
}
|
||||||
|
pub fn push_text_area(&mut self, value: (String, String)) {
|
||||||
|
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
||||||
|
self.layout.push(value.0.clone());
|
||||||
|
self.fields.insert(value.0, TextArea(value.1, 0));
|
||||||
}
|
}
|
||||||
pub fn push(&mut self, value: (String, String)) {
|
pub fn push(&mut self, value: (String, String)) {
|
||||||
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len());
|
||||||
self.layout.push(value.0.clone());
|
self.layout.push(value.0.clone());
|
||||||
self.fields.insert(value.0, Text(value.1, 0));
|
self.fields.insert(value.0, Text(value.1, 0));
|
||||||
self.cursors.push(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, index: usize, value: (String, Field)) {
|
pub fn insert(&mut self, index: usize, value: (String, Field)) {
|
||||||
self.layout.insert(index, value.0.clone());
|
self.layout.insert(index, value.0.clone());
|
||||||
self.fields.insert(value.0, value.1);
|
self.fields.insert(value.0, value.1);
|
||||||
self.cursors.insert(index, 0);
|
}
|
||||||
|
|
||||||
|
pub fn values_mut(&mut self) -> &mut FnvHashMap<String, Field> {
|
||||||
|
&mut self.fields
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect(self) -> Option<FnvHashMap<String, Field>> {
|
pub fn collect(self) -> Option<FnvHashMap<String, Field>> {
|
||||||
|
@ -209,31 +258,38 @@ impl Component for FormWidget {
|
||||||
|
|
||||||
for (i, k) in self.layout.iter().enumerate() {
|
for (i, k) in self.layout.iter().enumerate() {
|
||||||
let v = self.fields.get_mut(k).unwrap();
|
let v = self.fields.get_mut(k).unwrap();
|
||||||
|
/* Write field label */
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
k.as_str(),
|
k.as_str(),
|
||||||
grid,
|
grid,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
(pos_inc(upper_left, (1, i * 2)), set_y(bottom_right, i * 2 + get_y(upper_left))),
|
(pos_inc(upper_left, (1, i)), set_y(bottom_right, i + get_y(upper_left))),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
/* draw field */
|
||||||
v.draw(grid,
|
v.draw(grid,
|
||||||
(pos_inc(upper_left, (self.field_name_max_length + 3, i * 2)), set_y(bottom_right, i * 2 + get_y(upper_left))), context);
|
(pos_inc(upper_left, (self.field_name_max_length + 3, i)), set_y(bottom_right, i + get_y(upper_left))), context);
|
||||||
|
|
||||||
|
/* Highlight if necessary */
|
||||||
if i == self.cursor {
|
if i == self.cursor {
|
||||||
if self.focus == FormFocus::Fields {
|
if self.focus == FormFocus::Fields {
|
||||||
change_colors(grid, (pos_inc(upper_left, (0, i * 2)), set_y(bottom_right, i * 2 + get_y(upper_left))), Color::Default, Color::Byte(246));
|
change_colors(grid, (pos_inc(upper_left, (0, i)), set_y(bottom_right, i + get_y(upper_left))), Color::Default, Color::Byte(246));
|
||||||
}
|
}
|
||||||
if self.focus == FormFocus::TextInput {
|
if self.focus == FormFocus::TextInput {
|
||||||
v.draw_cursor(grid,
|
v.draw_cursor(grid,
|
||||||
(pos_inc(upper_left, (self.field_name_max_length + 3 , i * 2)),
|
(pos_inc(upper_left, (self.field_name_max_length + 3 , i)),
|
||||||
(get_x(upper_left) + self.field_name_max_length + 3, i * 2 + get_y(upper_left))));
|
(get_x(upper_left) + self.field_name_max_length + 3, i + get_y(upper_left))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let length = self.layout.len();
|
if !self.hide_buttons {
|
||||||
self.buttons.draw(grid,
|
let length = self.layout.len();
|
||||||
(pos_inc(upper_left, (1, length * 2 + 3)), set_y(bottom_right, length * 2 + 3 + get_y(upper_left))),
|
self.buttons.draw(grid,
|
||||||
context);
|
(pos_inc(upper_left, (1, length * 2 + 3)), set_y(bottom_right, length * 2 + 3 + get_y(upper_left))),
|
||||||
|
context);
|
||||||
|
|
||||||
|
}
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
@ -254,8 +310,12 @@ impl Component for FormWidget {
|
||||||
UIEventType::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => {
|
UIEventType::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => {
|
||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
},
|
},
|
||||||
UIEventType::Input(Key::Down) => {
|
UIEventType::Input(Key::Down) if self.focus == FormFocus::Fields => {
|
||||||
self.focus = FormFocus::Buttons;
|
self.focus = FormFocus::Buttons;
|
||||||
|
if self.hide_buttons {
|
||||||
|
self.set_dirty();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
UIEventType::Input(Key::Char('\n')) if self.focus == FormFocus::Fields => {
|
UIEventType::Input(Key::Char('\n')) if self.focus == FormFocus::Fields => {
|
||||||
self.focus = FormFocus::TextInput;
|
self.focus = FormFocus::TextInput;
|
||||||
|
|
Loading…
Reference in New Issue