widgets: add button type parameter to FormWidget

jmap-eventsource
Manos Pitsidianakis 2020-10-13 17:04:40 +03:00
parent cd68008e67
commit b343530f0c
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
3 changed files with 86 additions and 51 deletions

View File

@ -40,7 +40,7 @@ pub struct ContactManager {
parent_id: ComponentId, parent_id: ComponentId,
pub card: Card, pub card: Card,
mode: ViewMode, mode: ViewMode,
form: FormWidget, form: FormWidget<bool>,
account_pos: usize, account_pos: usize,
content: CellBuffer, content: CellBuffer,
theme_default: ThemeAttribute, theme_default: ThemeAttribute,
@ -118,24 +118,30 @@ impl ContactManager {
); );
} }
self.form = FormWidget::new("Save".into()); self.form = FormWidget::new(("Save".into(), true));
self.form.add_button(("Cancel(Esc)".into(), false)); self.form.add_button(("Cancel(Esc)".into(), false));
self.form self.form
.push(("NAME".into(), self.card.name().to_string())); .push(("NAME".into(), self.card.name().to_string().into()));
self.form.push(( self.form.push((
"ADDITIONAL NAME".into(), "ADDITIONAL NAME".into(),
self.card.additionalname().to_string(), self.card.additionalname().to_string().into(),
));
self.form.push((
"NAME PREFIX".into(),
self.card.name_prefix().to_string().into(),
));
self.form.push((
"NAME SUFFIX".into(),
self.card.name_suffix().to_string().into(),
)); ));
self.form self.form
.push(("NAME PREFIX".into(), self.card.name_prefix().to_string())); .push(("E-MAIL".into(), self.card.email().to_string().into()));
self.form self.form
.push(("NAME SUFFIX".into(), self.card.name_suffix().to_string())); .push(("URL".into(), self.card.url().to_string().into()));
self.form self.form
.push(("E-MAIL".into(), self.card.email().to_string())); .push(("KEY".into(), self.card.key().to_string().into()));
self.form.push(("URL".into(), self.card.url().to_string()));
self.form.push(("KEY".into(), self.card.key().to_string()));
for (k, v) in self.card.extra_properties() { for (k, v) in self.card.extra_properties() {
self.form.push((k.to_string(), v.to_string())); self.form.push((k.to_string().into(), v.to_string().into()));
} }
} }
@ -209,10 +215,10 @@ impl Component for ContactManager {
.into_iter() .into_iter()
.map(|(s, v)| { .map(|(s, v)| {
( (
s, s.to_string(),
match v { match v {
Field::Text(v, _) => v.as_str().to_string(), Field::Text(v, _) => v.as_str().to_string(),
Field::Choice(mut v, c) => v.remove(c), Field::Choice(mut v, c) => v.remove(c).to_string(),
}, },
) )
}) })

View File

@ -78,7 +78,7 @@ pub struct Composer {
pager: Pager, pager: Pager,
draft: Draft, draft: Draft,
form: FormWidget, form: FormWidget<bool>,
mode: ViewMode, mode: ViewMode,
@ -403,7 +403,7 @@ impl Composer {
fn update_form(&mut self) { fn update_form(&mut self) {
let old_cursor = self.form.cursor(); let old_cursor = self.form.cursor();
self.form = FormWidget::new("Save".into()); self.form = FormWidget::new(("Save".into(), true));
self.form.hide_buttons(); self.form.hide_buttons();
self.form.set_cursor(old_cursor); self.form.set_cursor(old_cursor);
let headers = self.draft.headers(); let headers = self.draft.headers();
@ -412,7 +412,7 @@ impl Composer {
if k == "To" || k == "Cc" || k == "Bcc" { if k == "To" || k == "Cc" || k == "Bcc" {
self.form.push_cl(( self.form.push_cl((
k.into(), k.into(),
headers[k].to_string(), headers[k].to_string().into(),
Box::new(move |c, term| { Box::new(move |c, term| {
let book: &AddressBook = &c.accounts[&account_hash].address_book; let book: &AddressBook = &c.accounts[&account_hash].address_book;
let results: Vec<String> = book.search(term); let results: Vec<String> = book.search(term);
@ -423,7 +423,7 @@ impl Composer {
}), }),
)); ));
} else { } else {
self.form.push((k.into(), headers[k].to_string())); self.form.push((k.into(), headers[k].to_string().into()));
} }
} }
} }

View File

@ -20,6 +20,7 @@
*/ */
use super::*; use super::*;
use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
type AutoCompleteFn = Box<dyn Fn(&Context, &str) -> Vec<AutoCompleteEntry> + Send + Sync>; type AutoCompleteFn = Box<dyn Fn(&Context, &str) -> Vec<AutoCompleteEntry> + Send + Sync>;
@ -41,7 +42,7 @@ impl Default for FormFocus {
pub enum Field { pub enum Field {
Text(UText, Option<(AutoCompleteFn, AutoComplete)>), Text(UText, Option<(AutoCompleteFn, AutoComplete)>),
Choice(Vec<String>, Cursor), Choice(Vec<Cow<'static, str>>, Cursor),
} }
impl Debug for Field { impl Debug for Field {
@ -69,7 +70,7 @@ impl Field {
if v.is_empty() { if v.is_empty() {
"" ""
} else { } else {
v[*cursor].as_str() v[*cursor].as_ref()
} }
} }
} }
@ -89,7 +90,7 @@ impl Field {
pub fn into_string(self) -> String { pub fn into_string(self) -> String {
match self { match self {
Text(s, _) => s.into_string(), Text(s, _) => s.into_string(),
Choice(mut v, cursor) => v.remove(cursor), Choice(mut v, cursor) => v.remove(cursor).to_string(),
} }
} }
@ -288,18 +289,35 @@ impl fmt::Display for Field {
f, f,
"{}", "{}",
match self { match self {
Text(ref s, _) => s.as_str().to_string(), Text(ref s, _) => s.as_str(),
Choice(ref v, ref cursor) => v[*cursor].clone(), Choice(ref v, ref cursor) => v[*cursor].as_ref(),
} }
) )
} }
} }
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FormButtonActions {
Accept,
Reset,
Cancel,
Other(&'static str),
}
impl Default for FormButtonActions {
fn default() -> Self {
FormButtonActions::Cancel
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct FormWidget { pub struct FormWidget<T>
fields: HashMap<String, Field>, where
layout: Vec<String>, T: 'static + std::fmt::Debug + Copy + Default + Send + Sync,
buttons: ButtonWidget<bool>, {
fields: HashMap<Cow<'static, str>, Field>,
layout: Vec<Cow<'static, str>>,
buttons: ButtonWidget<T>,
field_name_max_length: usize, field_name_max_length: usize,
cursor: usize, cursor: usize,
@ -309,16 +327,16 @@ pub struct FormWidget {
id: ComponentId, id: ComponentId,
} }
impl fmt::Display for FormWidget { impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> fmt::Display for FormWidget<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("", f) Display::fmt("", f)
} }
} }
impl FormWidget { impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T> {
pub fn new(action: String) -> FormWidget { pub fn new(action: (Cow<'static, str>, T)) -> FormWidget<T> {
FormWidget { FormWidget {
buttons: ButtonWidget::new((action, true)), buttons: ButtonWidget::new(action),
focus: FormFocus::Fields, focus: FormFocus::Fields,
hide_buttons: false, hide_buttons: false,
id: ComponentId::new_v4(), id: ComponentId::new_v4(),
@ -344,16 +362,16 @@ impl FormWidget {
self.layout.len() self.layout.len()
} }
pub fn add_button(&mut self, val: (String, bool)) { pub fn add_button(&mut self, val: (Cow<'static, str>, T)) {
self.buttons.push(val); self.buttons.push(val);
} }
pub fn push_choices(&mut self, value: (String, Vec<String>)) { pub fn push_choices(&mut self, value: (Cow<'static, str>, Vec<Cow<'static, str>>)) {
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));
} }
pub fn push_cl(&mut self, value: (String, String, AutoCompleteFn)) { pub fn push_cl(&mut self, value: (Cow<'static, str>, String, AutoCompleteFn)) {
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( self.fields.insert(
@ -364,43 +382,43 @@ impl FormWidget {
), ),
); );
} }
pub fn push(&mut self, value: (String, String)) { pub fn push(&mut self, value: (Cow<'static, str>, 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(UText::new(value.1), None)); self.fields.insert(value.0, Text(UText::new(value.1), None));
} }
pub fn insert(&mut self, index: usize, value: (String, Field)) { pub fn insert(&mut self, index: usize, value: (Cow<'static, str>, 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);
} }
pub fn values(&self) -> &HashMap<String, Field> { pub fn values(&self) -> &HashMap<Cow<'static, str>, Field> {
&self.fields &self.fields
} }
pub fn values_mut(&mut self) -> &mut HashMap<String, Field> { pub fn values_mut(&mut self) -> &mut HashMap<Cow<'static, str>, Field> {
&mut self.fields &mut self.fields
} }
pub fn collect(self) -> Option<HashMap<String, Field>> { pub fn collect(self) -> Option<HashMap<Cow<'static, str>, Field>> {
if let Some(true) = self.buttons_result() { if self.buttons_result().is_some() {
Some(self.fields) Some(self.fields)
} else { } else {
None None
} }
} }
pub fn buttons_result(&self) -> Option<bool> { pub fn buttons_result(&self) -> Option<T> {
self.buttons.result self.buttons.result
} }
} }
impl Component for FormWidget { impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for FormWidget<T> {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
let upper_left = upper_left!(area); let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area); let bottom_right = bottom_right!(area);
if self.dirty { if self.is_dirty() {
let theme_default = crate::conf::value(context, "theme_default"); let theme_default = crate::conf::value(context, "theme_default");
clear_area( clear_area(
grid, grid,
@ -416,7 +434,7 @@ impl Component for FormWidget {
let v = self.fields.get_mut(k).unwrap(); let v = self.fields.get_mut(k).unwrap();
/* Write field label */ /* Write field label */
write_string_to_grid( write_string_to_grid(
k.as_str(), k.as_ref(),
grid, grid,
label_attrs.fg, label_attrs.fg,
label_attrs.bg, label_attrs.bg,
@ -621,10 +639,10 @@ impl Component for FormWidget {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ButtonWidget<T> pub struct ButtonWidget<T>
where where
T: 'static + std::fmt::Debug + Default + Send + Sync, T: 'static + std::fmt::Debug + Copy + Default + Send + Sync,
{ {
buttons: HashMap<String, T>, buttons: HashMap<Cow<'static, str>, T>,
layout: Vec<String>, layout: Vec<Cow<'static, str>>,
result: Option<T>, result: Option<T>,
cursor: usize, cursor: usize,
@ -636,7 +654,7 @@ where
impl<T> fmt::Display for ButtonWidget<T> impl<T> fmt::Display for ButtonWidget<T>
where where
T: 'static + std::fmt::Debug + Default + Send + Sync, T: 'static + std::fmt::Debug + Copy + Default + Send + Sync,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("", f) Display::fmt("", f)
@ -645,9 +663,9 @@ where
impl<T> ButtonWidget<T> impl<T> ButtonWidget<T>
where where
T: 'static + std::fmt::Debug + Default + Send + Sync, T: 'static + std::fmt::Debug + Copy + Default + Send + Sync,
{ {
pub fn new(init_val: (String, T)) -> ButtonWidget<T> { pub fn new(init_val: (Cow<'static, str>, T)) -> ButtonWidget<T> {
ButtonWidget { ButtonWidget {
layout: vec![init_val.0.clone()], layout: vec![init_val.0.clone()],
buttons: vec![init_val].into_iter().collect(), buttons: vec![init_val].into_iter().collect(),
@ -659,7 +677,7 @@ where
} }
} }
pub fn push(&mut self, value: (String, T)) { pub fn push(&mut self, value: (Cow<'static, str>, T)) {
self.layout.push(value.0.clone()); self.layout.push(value.0.clone());
self.buttons.insert(value.0, value.1); self.buttons.insert(value.0, value.1);
} }
@ -668,14 +686,25 @@ where
self.result.is_some() self.result.is_some()
} }
pub fn result(&self) -> Option<T> {
self.result
}
pub fn set_focus(&mut self, new_val: bool) { pub fn set_focus(&mut self, new_val: bool) {
self.focus = new_val; self.focus = new_val;
} }
pub fn set_cursor(&mut self, new_val: usize) {
if self.buttons.is_empty() {
return;
}
self.cursor = new_val % self.buttons.len();
}
} }
impl<T> Component for ButtonWidget<T> impl<T> Component for ButtonWidget<T>
where where
T: 'static + std::fmt::Debug + Default + Send + Sync, T: 'static + std::fmt::Debug + Copy + Default + Send + Sync,
{ {
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.dirty { if self.dirty {
@ -687,7 +716,7 @@ where
for (i, k) in self.layout.iter().enumerate() { for (i, k) in self.layout.iter().enumerate() {
let cur_len = k.len(); let cur_len = k.len();
write_string_to_grid( write_string_to_grid(
k.as_str(), k.as_ref(),
grid, grid,
theme_default.fg, theme_default.fg,
if i == self.cursor && self.focus { if i == self.cursor && self.focus {