widgets: add button type parameter to FormWidget
parent
cd68008e67
commit
b343530f0c
|
@ -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(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue