mirror of
https://git.meli.delivery/meli/meli
synced 2024-10-30 21:20:34 +00:00
Add FormWidget/ButtonWidget and use them in Contacts
This commit is contained in:
parent
bbaf87e345
commit
4ee5447cf9
17
src/bin.rs
17
src/bin.rs
@ -66,7 +66,7 @@ fn main() {
|
||||
let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts)));
|
||||
let listing = listing::Listing::from(IndexStyle::Compact);
|
||||
let b = Entity::from(Box::new(listing));
|
||||
let tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true)), Box::new(AccountsPanel::new(&state.context)), Box::new(ContactManager::default())]));
|
||||
let tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true)), Box::new(AccountsPanel::new(&state.context)), Box::new(ContactList::default())]));
|
||||
let window = Entity::from(tabs);
|
||||
|
||||
let status_bar = Entity::from(Box::new(StatusBar::new(window)));
|
||||
@ -123,6 +123,19 @@ fn main() {
|
||||
},
|
||||
}
|
||||
},
|
||||
UIMode::Insert => {
|
||||
match k {
|
||||
Key::Char('\n') | Key::Esc => {
|
||||
state.mode = UIMode::Normal;
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(UIMode::Normal)});
|
||||
state.redraw();
|
||||
},
|
||||
k => {
|
||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::InsertInput(k)});
|
||||
state.redraw();
|
||||
},
|
||||
}
|
||||
}
|
||||
UIMode::Execute => {
|
||||
match k {
|
||||
Key::Char('\n') | Key::Esc => {
|
||||
@ -147,8 +160,10 @@ fn main() {
|
||||
},
|
||||
ThreadEvent::UIEvent(UIEventType::ChangeMode(f)) => {
|
||||
state.mode = f;
|
||||
if f == UIMode::Fork {
|
||||
break 'inner; // `goto` 'reap loop, and wait on child.
|
||||
}
|
||||
}
|
||||
ThreadEvent::UIEvent(UIEventType::StartupCheck) => {
|
||||
let mut render_flag = false;
|
||||
for idx_a in 0..state.context.accounts.len() {
|
||||
|
@ -91,7 +91,7 @@ impl From<Box<Component>> for Entity {
|
||||
let id = Uuid::new_v4();
|
||||
kind.set_id(id);
|
||||
Entity {
|
||||
id: id,
|
||||
id,
|
||||
component: kind,
|
||||
}
|
||||
}
|
||||
@ -105,7 +105,7 @@ where
|
||||
let id = Uuid::new_v4();
|
||||
kind.set_id(id);
|
||||
Entity {
|
||||
id: id,
|
||||
id,
|
||||
component: kind,
|
||||
}
|
||||
}
|
||||
@ -151,8 +151,8 @@ pub trait Component: Display + Debug {
|
||||
true
|
||||
}
|
||||
fn set_dirty(&mut self);
|
||||
fn kill(&mut self, id: EntityId) {}
|
||||
fn set_id(&mut self, id: EntityId) {}
|
||||
fn kill(&mut self, _id: EntityId) {}
|
||||
fn set_id(&mut self, _id: EntityId) {}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -218,7 +218,7 @@ fn ch_to_bin(ch: char) -> Option<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(never_loop)]
|
||||
#[allow(clippy::never_loop)]
|
||||
fn set_and_join_vert(grid: &mut CellBuffer, idx: Pos) -> u32 {
|
||||
let (x, y) = idx;
|
||||
let mut bin_set = 0b1010;
|
||||
@ -309,7 +309,7 @@ fn set_and_join_vert(grid: &mut CellBuffer, idx: Pos) -> u32 {
|
||||
bin_set
|
||||
}
|
||||
|
||||
#[allow(never_loop)]
|
||||
#[allow(clippy::never_loop)]
|
||||
fn set_and_join_horz(grid: &mut CellBuffer, idx: Pos) -> u32 {
|
||||
let (x, y) = idx;
|
||||
let mut bin_set = 0b0101;
|
||||
|
@ -25,28 +25,6 @@ mod contact_list;
|
||||
|
||||
pub use self::contact_list::*;
|
||||
|
||||
macro_rules! write_field {
|
||||
($title:expr, $value:expr, $target_grid:expr, $fg_color:expr, $bg_color:expr, $width:expr, $y:expr) => {{
|
||||
let (x, y) = write_string_to_grid(
|
||||
$title,
|
||||
&mut $target_grid,
|
||||
$fg_color,
|
||||
$bg_color,
|
||||
((1, $y + 2), ($width - 1, $y + 2)),
|
||||
false,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&$value,
|
||||
&mut $target_grid,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
((x, y), ($width - 1, y)),
|
||||
false,
|
||||
);
|
||||
y
|
||||
}}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ViewMode {
|
||||
ReadOnly,
|
||||
@ -60,6 +38,7 @@ pub struct ContactManager {
|
||||
id: Uuid,
|
||||
pub card: Card,
|
||||
mode: ViewMode,
|
||||
form: FormWidget,
|
||||
content: CellBuffer,
|
||||
dirty: bool,
|
||||
initialized: bool,
|
||||
@ -71,6 +50,7 @@ impl Default for ContactManager {
|
||||
id: Uuid::nil(),
|
||||
card: Card::new(),
|
||||
mode: ViewMode::Read,
|
||||
form: FormWidget::default(),
|
||||
content: CellBuffer::new(200, 100, Cell::with_char(' ')),
|
||||
dirty: true,
|
||||
initialized: false,
|
||||
@ -112,27 +92,16 @@ impl ContactManager {
|
||||
((x, 0), (width, 0)),
|
||||
false,
|
||||
);
|
||||
for x in 0..width {
|
||||
set_and_join_box(&mut self.content, (x, 2), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 4), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 6), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 8), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 10), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 12), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 14), HORZ_BOUNDARY);
|
||||
set_and_join_box(&mut self.content, (x, 16), HORZ_BOUNDARY);
|
||||
}
|
||||
for y in 0..height {
|
||||
set_and_join_box(&mut self.content, (width - 1, y), VERT_BOUNDARY);
|
||||
}
|
||||
let mut y = write_field!("First Name: ", self.card.firstname(), self.content, Color::Byte(250), Color::Default, width, 1);
|
||||
y = write_field!("Last Name: ", self.card.lastname(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
y = write_field!("Additional Name: ", self.card.additionalname(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
y = write_field!("Name Prefix: ", self.card.name_prefix(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
y = write_field!("Name Suffix: ", self.card.name_suffix(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
y = write_field!("E-mail: ", self.card.email(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
y = write_field!("url: ", self.card.url(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
y = write_field!("key: ", self.card.key(), self.content, Color::Byte(250), Color::Default, width, y);
|
||||
self.form = FormWidget::new("Save".into());
|
||||
self.form.add_button(("Cancel".into(), false));
|
||||
self.form.push(("First Name".into(), self.card.firstname().to_string()));
|
||||
self.form.push(("Last Name".into(), self.card.lastname().to_string()));
|
||||
self.form.push(("Additional Name".into(), self.card.additionalname().to_string()));
|
||||
self.form.push(("Name Prefix".into(), self.card.name_prefix().to_string()));
|
||||
self.form.push(("Name Suffix".into(), self.card.name_suffix().to_string()));
|
||||
self.form.push(("E-mail".into(), self.card.email().to_string()));
|
||||
self.form.push(("url".into(), self.card.url().to_string()));
|
||||
self.form.push(("key".into(), self.card.key().to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,11 +113,40 @@ impl Component for ContactManager {
|
||||
}
|
||||
clear_area(grid, area);
|
||||
let (width, height) = self.content.size();
|
||||
copy_area(grid, &self.content, area, ((0, 0), (width - 1, height -1)));
|
||||
copy_area(grid, &self.content, area, ((0, 0), (width - 1, 0)));
|
||||
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
self.form.draw(grid, (set_y(upper_left, get_y(upper_left) + 1), bottom_right), context);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||
if self.form.process_event(event, context) {
|
||||
match self.form.buttons_result() {
|
||||
None => {},
|
||||
Some(true) => {
|
||||
eprintln!("fields: {:?}", std::mem::replace(&mut self.form, FormWidget::default()).collect());
|
||||
context.replies.push_back(UIEvent {
|
||||
id: 0,
|
||||
event_type: UIEventType::StatusEvent(StatusEvent::DisplayMessage("Saved.".into())),
|
||||
});
|
||||
context.replies.push_back(UIEvent {
|
||||
id: 0,
|
||||
event_type: UIEventType::EntityKill(self.id),
|
||||
});
|
||||
},
|
||||
Some(false) => {
|
||||
context.replies.push_back(UIEvent {
|
||||
id: 0,
|
||||
event_type: UIEventType::EntityKill(self.id),
|
||||
});
|
||||
|
||||
},
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
match event.event_type {
|
||||
UIEventType::Input(Key::Char('\n')) => {
|
||||
context.replies.push_back(UIEvent {
|
||||
@ -159,16 +157,18 @@ impl Component for ContactManager {
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
*/
|
||||
false
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty
|
||||
self.dirty | self.form.is_dirty()
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self) {
|
||||
self.dirty = true;
|
||||
self.initialized = false;
|
||||
self.form.set_dirty();
|
||||
}
|
||||
|
||||
fn set_id(&mut self, uuid: Uuid) {
|
||||
|
@ -19,7 +19,6 @@ pub struct ContactList {
|
||||
id_positions: Vec<EntityId>,
|
||||
|
||||
mode: ViewMode,
|
||||
initialized: bool,
|
||||
dirty: bool,
|
||||
view: Option<Entity>,
|
||||
}
|
||||
@ -47,7 +46,6 @@ impl ContactList {
|
||||
id_positions: Vec::new(),
|
||||
mode: ViewMode::List,
|
||||
content,
|
||||
initialized: false,
|
||||
dirty: true,
|
||||
view: None,
|
||||
}
|
||||
@ -57,7 +55,7 @@ impl ContactList {
|
||||
let account = &mut context.accounts[self.account_pos];
|
||||
let book = &mut account.address_book;
|
||||
self.content.resize(MAX_COLS, book.len(), Cell::with_char(' '));
|
||||
eprintln!("{:?}", book);
|
||||
eprintln!("initialize {:?}", book);
|
||||
|
||||
self.id_positions.clear();
|
||||
if self.id_positions.capacity() < book.len() {
|
||||
@ -81,24 +79,33 @@ impl ContactList {
|
||||
|
||||
impl Component for ContactList {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if !self.initialized {
|
||||
self.initialize(context);
|
||||
self.initialized = true;
|
||||
}
|
||||
|
||||
if let Some(mgr) = self.view.as_mut() {
|
||||
mgr.draw(grid, area, context);
|
||||
self.dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if self.dirty {
|
||||
self.initialize(context);
|
||||
clear_area(grid, area);
|
||||
copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS - 1, self.content.size().1 - 1)));
|
||||
context.dirty_areas.push_back(area);
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
/* Reset previously highlighted line */
|
||||
let fg_color = Color::Default;
|
||||
let bg_color = Color::Default;
|
||||
change_colors(grid, (pos_inc(upper_left, (0, self.cursor_pos)), set_y(bottom_right, get_y(upper_left) + self.cursor_pos)), fg_color, bg_color);
|
||||
|
||||
/* Highlight current line */
|
||||
let bg_color = Color::Byte(246);
|
||||
change_colors(grid, (pos_inc(upper_left, (0, self.new_cursor_pos)), set_y(bottom_right, get_y(upper_left) + self.new_cursor_pos)), fg_color, bg_color);
|
||||
self.cursor_pos = self.new_cursor_pos;
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||
if let Some(ref mut v) = self.view {
|
||||
if v.process_event(event, context) {
|
||||
@ -112,14 +119,10 @@ impl Component for ContactList {
|
||||
let card = book[&self.id_positions[self.cursor_pos]].clone();
|
||||
let mut manager = ContactManager::default();
|
||||
manager.card = card;
|
||||
|
||||
|
||||
|
||||
let entity = Entity::from(Box::new(manager));
|
||||
|
||||
self.mode = ViewMode::View(*entity.id());
|
||||
self.view = Some(entity);
|
||||
self.set_dirty();
|
||||
|
||||
return true;
|
||||
},
|
||||
@ -134,9 +137,11 @@ impl Component for ContactList {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty
|
||||
self.dirty || self.view.as_ref().map(|v| v.is_dirty()).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_dirty(&mut self) {
|
||||
if let Some(p) = self.view.as_mut() {
|
||||
p.set_dirty();
|
||||
|
@ -133,7 +133,7 @@ impl AccountMenu {
|
||||
inc: &mut usize,
|
||||
index: usize, //account index
|
||||
context: &mut Context,
|
||||
) -> () {
|
||||
) {
|
||||
let len = s.len();
|
||||
match context.accounts[index].status(root) {
|
||||
Ok(_) => {}
|
||||
|
@ -45,7 +45,7 @@ impl Component for ContactsPanel {
|
||||
copy_area(grid, &self.content, area, ((0, 0), (width - 1, height - 1)));
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||
fn process_event(&mut self, _event: &UIEvent, _context: &mut Context) -> bool {
|
||||
false
|
||||
}
|
||||
fn is_dirty(&self) -> bool {
|
||||
|
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use std::dbg;
|
||||
|
||||
use melib::Draft;
|
||||
use std::str::FromStr;
|
||||
|
@ -209,7 +209,7 @@ impl CompactListing {
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
if mailbox.len() == 0 {
|
||||
if mailbox.is_empty() {
|
||||
return;
|
||||
}
|
||||
let threads = &mailbox.collection.threads;
|
||||
|
@ -224,7 +224,7 @@ impl ThreadListing {
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
if mailbox.len() == 0 {
|
||||
if mailbox.is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.locations[idx] != 0 {
|
||||
@ -255,7 +255,7 @@ impl ThreadListing {
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
if mailbox.len() == 0 || mailbox.len() <= idx {
|
||||
if mailbox.is_empty() || mailbox.len() <= idx {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -391,9 +391,8 @@ impl Component for MailView {
|
||||
|
||||
eprintln!("{:?}", new_card);
|
||||
|
||||
*/
|
||||
match self.mode {
|
||||
ViewMode::ContactSelector(_) => {
|
||||
*/
|
||||
if let ViewMode::ContactSelector(_) = self.mode {
|
||||
if let ViewMode::ContactSelector(s) = std::mem::replace(&mut self.mode, ViewMode::Normal) {
|
||||
for c in s.collect() {
|
||||
let mut new_card: Card = Card::new();
|
||||
@ -405,8 +404,6 @@ impl Component for MailView {
|
||||
//eprintln!("{:?}", s.collect());
|
||||
}
|
||||
return true;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
let accounts = &context.accounts;
|
||||
@ -604,7 +601,7 @@ impl Component for MailView {
|
||||
true
|
||||
}
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty || true
|
||||
self.dirty
|
||||
|| self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
||||
|| self.subview.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|
||||
}
|
||||
|
@ -23,6 +23,10 @@
|
||||
*/
|
||||
use super::*;
|
||||
|
||||
mod widgets;
|
||||
|
||||
pub use self::widgets::*;
|
||||
|
||||
/// A horizontally split in half container.
|
||||
#[derive(Debug)]
|
||||
pub struct HSplit {
|
||||
@ -212,7 +216,7 @@ impl fmt::Display for Pager {
|
||||
}
|
||||
|
||||
impl Pager {
|
||||
pub fn update_from_str(&mut self, text: &str) -> () {
|
||||
pub fn update_from_str(&mut self, text: &str) {
|
||||
let lines: Vec<&str> = text.trim().split('\n').collect();
|
||||
let height = lines.len() + 1;
|
||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||
@ -673,7 +677,7 @@ impl Progress {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_work(&mut self, n: usize) -> () {
|
||||
pub fn add_work(&mut self, n: usize) {
|
||||
if self.finished >= self.total_work {
|
||||
return;
|
||||
}
|
||||
|
277
ui/src/components/utilities/widgets.rs
Normal file
277
ui/src/components/utilities/widgets.rs
Normal file
@ -0,0 +1,277 @@
|
||||
use super::*;
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum FormFocus {
|
||||
Fields,
|
||||
Buttons,
|
||||
TextInput,
|
||||
}
|
||||
|
||||
impl Default for FormFocus {
|
||||
fn default() -> FormFocus {
|
||||
FormFocus::Fields
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FormWidget {
|
||||
fields: FnvHashMap<String, String>,
|
||||
cursors: Vec<usize>,
|
||||
layout: Vec<String>,
|
||||
buttons: ButtonWidget<bool>,
|
||||
|
||||
field_name_max_length: usize,
|
||||
cursor: usize,
|
||||
focus: FormFocus,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for FormWidget {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt("", f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FormWidget {
|
||||
pub fn new(action: String) -> FormWidget {
|
||||
FormWidget {
|
||||
buttons: ButtonWidget::new((action, true)),
|
||||
focus: FormFocus::Fields,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_button(&mut self, val: (String, bool)) {
|
||||
self.buttons.push(val);
|
||||
}
|
||||
|
||||
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.layout.push(value.0.clone());
|
||||
self.fields.insert(value.0, value.1);
|
||||
self.cursors.push(0);
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, index: usize, value: (String, String)) {
|
||||
self.layout.insert(index, value.0.clone());
|
||||
self.fields.insert(value.0, value.1);
|
||||
self.cursors.insert(index, 0);
|
||||
}
|
||||
|
||||
pub fn collect(self) -> Option<FnvHashMap<String, String>> {
|
||||
if let Some(true) = self.buttons_result() {
|
||||
Some(self.fields)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn buttons_result(&self) -> Option<bool> {
|
||||
self.buttons.result
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for FormWidget {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
for (i, k) in self.layout.iter().enumerate() {
|
||||
let v = &self.fields[k];
|
||||
write_string_to_grid(
|
||||
k.as_str(),
|
||||
grid,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
(pos_inc(upper_left, (1, i * 2)), set_y(bottom_right, i * 2 + get_y(upper_left))),
|
||||
false,
|
||||
);
|
||||
write_string_to_grid(
|
||||
v.as_str(),
|
||||
grid,
|
||||
Color::Default,
|
||||
Color::Default,
|
||||
(pos_inc(upper_left, (self.field_name_max_length + 3, i * 2)), set_y(bottom_right, i * 2 + get_y(upper_left))),
|
||||
false,
|
||||
);
|
||||
if i == self.cursor {
|
||||
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));
|
||||
}
|
||||
if self.focus == FormFocus::TextInput {
|
||||
change_colors(grid,
|
||||
(pos_inc(upper_left, (self.field_name_max_length + 3 + self.cursors[i], i * 2)),
|
||||
(get_x(upper_left) + self.field_name_max_length + 3 + self.cursors[i], i * 2 + get_y(upper_left))),
|
||||
Color::Default, Color::Byte(248));
|
||||
}
|
||||
}
|
||||
}
|
||||
let length = self.layout.len();
|
||||
self.buttons.draw(grid,
|
||||
(pos_inc(upper_left, (1, length * 2 + 3)), set_y(bottom_right, length * 2 + 3 + get_y(upper_left))),
|
||||
context);
|
||||
self.dirty = false;
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||
if self.focus == FormFocus::Buttons {
|
||||
if self.buttons.process_event(event, context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
match event.event_type {
|
||||
UIEventType::Input(Key::Up) => {
|
||||
self.cursor = self.cursor.saturating_sub(1);
|
||||
},
|
||||
UIEventType::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => {
|
||||
self.cursor += 1;
|
||||
},
|
||||
UIEventType::Input(Key::Down) => {
|
||||
self.focus = FormFocus::Buttons;
|
||||
},
|
||||
UIEventType::Input(Key::Char('\n')) if self.focus == FormFocus::Fields => {
|
||||
self.focus = FormFocus::TextInput;
|
||||
context.replies.push_back(UIEvent {
|
||||
id: 0,
|
||||
event_type: UIEventType::ChangeMode(UIMode::Insert),
|
||||
});
|
||||
},
|
||||
UIEventType::Input(Key::Up) if self.focus == FormFocus::Buttons => {
|
||||
self.focus = FormFocus::Fields;
|
||||
},
|
||||
UIEventType::InsertInput(Key::Right) if self.focus == FormFocus::TextInput => {
|
||||
if self.cursors[self.cursor] < self.fields[&self.layout[self.cursor]].len().saturating_sub(1) {
|
||||
self.cursors[self.cursor] += 1;
|
||||
}
|
||||
},
|
||||
UIEventType::InsertInput(Key::Left) if self.focus == FormFocus::TextInput => {
|
||||
if self.cursors[self.cursor] == 0 {
|
||||
self.focus = FormFocus::Fields;
|
||||
context.replies.push_back(UIEvent {
|
||||
id: 0,
|
||||
event_type: UIEventType::ChangeMode(UIMode::Normal),
|
||||
});
|
||||
} else {
|
||||
self.cursors[self.cursor] = self.cursors[self.cursor] - 1;
|
||||
}
|
||||
},
|
||||
UIEventType::ChangeMode(UIMode::Normal) if self.focus == FormFocus::TextInput => {
|
||||
self.focus = FormFocus::Fields;
|
||||
},
|
||||
UIEventType::InsertInput(Key::Char(k)) if self.focus == FormFocus::TextInput => {
|
||||
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
|
||||
field.insert(self.cursors[self.cursor], k);
|
||||
self.cursors[self.cursor] += 1;
|
||||
},
|
||||
UIEventType::InsertInput(Key::Backspace) if self.focus == FormFocus::TextInput => {
|
||||
let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap();
|
||||
match self.cursors[self.cursor] {
|
||||
i if i == 0 => {},
|
||||
i if i == field.len() => {
|
||||
field.pop();
|
||||
self.cursors[self.cursor] -= 1;
|
||||
},
|
||||
_ => {
|
||||
field.remove(self.cursors[self.cursor]);
|
||||
self.cursors[self.cursor] -= 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
self.set_dirty();
|
||||
true
|
||||
}
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty
|
||||
}
|
||||
fn set_dirty(&mut self) {
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ButtonWidget<T> where T: std::fmt::Debug + Default{
|
||||
buttons: FnvHashMap<String, T>,
|
||||
layout: Vec<String>,
|
||||
|
||||
result: Option<T>,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for ButtonWidget<T> where T: std::fmt::Debug + Default {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt("", f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ButtonWidget<T> where T: std::fmt::Debug + Default {
|
||||
pub fn new(init_val: (String, T)) -> ButtonWidget<T> {
|
||||
ButtonWidget {
|
||||
layout: vec![init_val.0.clone()],
|
||||
buttons: vec![init_val].into_iter().collect(),
|
||||
result: None,
|
||||
cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: (String, T)) {
|
||||
self.layout.push(value.0.clone());
|
||||
self.buttons.insert(value.0, value.1);
|
||||
}
|
||||
|
||||
pub fn is_resolved(&self) -> bool {
|
||||
self.result.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T> Component for ButtonWidget<T> where T: std::fmt::Debug + Default {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
||||
let mut len = 0;
|
||||
for (i, k) in self.layout.iter().enumerate() {
|
||||
let cur_len = k.len();
|
||||
let (x, y) = write_string_to_grid(
|
||||
k.as_str(),
|
||||
grid,
|
||||
Color::Default,
|
||||
if i == self.cursor { Color::Byte(246) } else { Color::Default },
|
||||
(pos_inc(upper_left, (len, 0)), pos_inc(upper_left, (cur_len + len, 0))),
|
||||
false,
|
||||
);
|
||||
len += cur_len + 3;
|
||||
}
|
||||
}
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
|
||||
match event.event_type {
|
||||
UIEventType::Input(Key::Char('\n')) => {
|
||||
self.result = Some(self.buttons.remove(&self.layout[self.cursor]).unwrap_or_default());
|
||||
return true;
|
||||
},
|
||||
UIEventType::Input(Key::Left) => {
|
||||
self.cursor = self.cursor.saturating_sub(1);
|
||||
return true;
|
||||
},
|
||||
UIEventType::Input(Key::Right) if self.cursor < self.layout.len().saturating_sub(1) => {
|
||||
self.cursor += 1;
|
||||
return true;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
fn is_dirty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn set_dirty(&mut self) {}
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ impl Account {
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn watch(&self, r: RefreshEventConsumer) -> () {
|
||||
pub fn watch(&self, r: RefreshEventConsumer) {
|
||||
self.backend.watch(r).unwrap();
|
||||
}
|
||||
/* This doesn't represent the number of correctly parsed mailboxes though */
|
||||
|
@ -473,7 +473,12 @@ impl State {
|
||||
self.flush();
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
UIEventType::ChangeMode(m) => {
|
||||
self.context
|
||||
.sender
|
||||
.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(m)));
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
/* inform each entity */
|
||||
|
@ -77,6 +77,7 @@ pub enum ForkType {
|
||||
pub enum UIEventType {
|
||||
Input(Key),
|
||||
ExInput(Key),
|
||||
InsertInput(Key),
|
||||
RefreshMailbox((usize, usize)),
|
||||
//Quit?
|
||||
Resize,
|
||||
@ -113,6 +114,7 @@ pub struct UIEvent {
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum UIMode {
|
||||
Normal,
|
||||
Insert,
|
||||
Execute,
|
||||
Fork,
|
||||
}
|
||||
@ -124,6 +126,7 @@ impl fmt::Display for UIMode {
|
||||
"{}",
|
||||
match *self {
|
||||
UIMode::Normal => "NORMAL",
|
||||
UIMode::Insert => "INSERT",
|
||||
UIMode::Execute => "EX",
|
||||
UIMode::Fork => "FORK",
|
||||
}
|
||||
|
@ -658,6 +658,18 @@ 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) {
|
||||
let bounds = grid.size();
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
let (x, y) = upper_left;
|
||||
if y > (get_y(bottom_right))
|
||||
|| x > get_x(bottom_right)
|
||||
|| y >= get_y(bounds)
|
||||
|| x >= get_x(bounds)
|
||||
{
|
||||
eprintln!("BUG: Invalid area in change_colors:\n area: {:?}", area);
|
||||
return;
|
||||
}
|
||||
if !is_valid_area!(area) {
|
||||
eprintln!("BUG: Invalid area in change_colors:\n area: {:?}", area);
|
||||
return;
|
||||
@ -685,8 +697,8 @@ pub fn write_string_to_grid(
|
||||
let (mut x, mut y) = upper_left;
|
||||
if y > (get_y(bottom_right))
|
||||
|| x > get_x(bottom_right)
|
||||
|| y > get_y(bounds)
|
||||
|| x > get_x(bounds)
|
||||
|| y >= get_y(bounds)
|
||||
|| x >= get_x(bounds)
|
||||
{
|
||||
eprintln!(" Invalid area with string {} and area {:?}", s, area);
|
||||
return (x, y);
|
||||
|
@ -119,7 +119,7 @@ pub fn get_events(
|
||||
mut closure: impl FnMut(Key),
|
||||
mut exit: impl FnMut(),
|
||||
rx: &chan::Receiver<bool>,
|
||||
) -> () {
|
||||
) {
|
||||
let mut input_mode = InputMode::Normal;
|
||||
let mut paste_buf = String::with_capacity(256);
|
||||
for c in stdin.events() {
|
||||
|
@ -44,6 +44,10 @@ pub fn set_x(p: Pos, new_x: usize) -> Pos {
|
||||
pub fn set_y(p: Pos, new_y: usize) -> Pos {
|
||||
(p.0, new_y)
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn pos_inc(p: Pos, inc: (usize, usize)) -> Pos {
|
||||
(p.0 + inc.0, p.1 + inc.1)
|
||||
}
|
||||
|
||||
/// An `Area` consists of two points: the upper left and bottom right corners.
|
||||
///
|
||||
|
Loading…
Reference in New Issue
Block a user