diff --git a/meli/src/contacts/editor.rs b/meli/src/contacts/editor.rs index 6549e615..4503b55f 100644 --- a/meli/src/contacts/editor.rs +++ b/meli/src/contacts/editor.rs @@ -24,8 +24,8 @@ use std::collections::HashMap; use melib::Card; use crate::{ - terminal::*, Action::*, CellBuffer, Component, ComponentId, Context, Field, FormWidget, Key, - StatusEvent, TabAction, ThemeAttribute, UIDialog, UIEvent, + terminal::*, CellBuffer, Component, ComponentId, Context, Field, FormWidget, Key, StatusEvent, + ThemeAttribute, UIDialog, UIEvent, }; #[derive(Debug)] @@ -33,7 +33,6 @@ enum ViewMode { ReadOnly, Discard(Box>), Edit, - //New, } #[derive(Debug)] @@ -44,11 +43,10 @@ pub struct ContactManager { mode: ViewMode, form: FormWidget, pub account_pos: usize, - content: CellBuffer, + content: Screen, theme_default: ThemeAttribute, dirty: bool, has_changes: bool, - initialized: bool, } @@ -68,7 +66,7 @@ impl ContactManager { mode: ViewMode::Edit, form: FormWidget::default(), account_pos: 0, - content: CellBuffer::new_with_context(100, 1, None, context), + content: Screen::::new(), theme_default, dirty: true, has_changes: false, @@ -77,34 +75,38 @@ impl ContactManager { } fn initialize(&mut self, context: &Context) { - let (width, _) = self.content.size(); + if !self.content.resize_with_context(100, 1, context) { + return; + } + let mut area = self.content.area(); - let (x, _) = self.content.write_string( + let (x, _) = self.content.grid_mut().write_string( "Last edited: ", self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, - ((0, 0), (width - 1, 0)), + area, None, ); - let (x, y) = self.content.write_string( + area = area.skip_cols(x); + let (x, y) = self.content.grid_mut().write_string( &self.card.last_edited(), self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, - ((x, 0), (width - 1, 0)), + area, None, ); + area = area.skip(x, y); if self.card.external_resource() { self.mode = ViewMode::ReadOnly; - _ = self.content.resize(self.content.size().0, 2, None); - self.content.write_string( + self.content.grid_mut().write_string( "This contact's origin is external and cannot be edited within meli.", self.theme_default.fg, self.theme_default.bg, self.theme_default.attrs, - ((x, y), (width - 1, y)), + area, None, ); } @@ -147,23 +149,15 @@ impl Component for ContactManager { self.initialized = true; } - let upper_left = area.upper_left(); - let bottom_right = area.bottom_right(); - - if self.dirty { - let (width, _height) = self.content.size(); - - grid.clear_area( - (upper_left, set_y(bottom_right, get_y(upper_left) + 1)), - self.theme_default, - ); - grid.copy_area(&self.content, area, ((0, 0), (width - 1, 0))); + if self.is_dirty() { + grid.clear_area(area, self.theme_default); + grid.copy_area(self.content.grid(), area.skip_rows(2), self.content.area()); self.dirty = false; } self.form.draw( grid, - (set_y(upper_left, get_y(upper_left) + 2), bottom_right), + area.skip_rows(2 + self.content.area().height()), context, ); if let ViewMode::Discard(ref mut selector) = self.mode { @@ -177,23 +171,27 @@ impl Component for ContactManager { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { if let UIEvent::ConfigReload { old_settings: _ } = event { self.theme_default = crate::conf::value(context, "theme_default"); - self.content = CellBuffer::new_with_context(100, 1, None, context); + self.content.grid_mut().empty(); self.initialized = false; self.set_dirty(true); } match self.mode { ViewMode::Discard(ref mut selector) => { + if matches!(event, UIEvent::ComponentUnrealize(ref id) if *id == selector.id()) { + selector.unrealize(context); + self.mode = ViewMode::Edit; + self.set_dirty(true); + return true; + } if selector.process_event(event, context) { self.set_dirty(true); return true; } } ViewMode::Edit => { - if let (Some(parent_id), &UIEvent::Input(Key::Esc)) = (self.parent_id, &event) { + if matches!(event, UIEvent::Input(Key::Esc)) { if self.can_quit_cleanly(context) { - context - .replies - .push_back(UIEvent::Action(Tab(TabAction::Kill(parent_id)))); + self.unrealize(context); } return true; } @@ -250,11 +248,7 @@ impl Component for ContactManager { fn is_dirty(&self) -> bool { self.dirty || self.form.is_dirty() - || if let ViewMode::Discard(ref selector) = self.mode { - selector.is_dirty() - } else { - false - } + || matches!(self.mode, ViewMode::Discard(ref selector) if selector.is_dirty()) } fn set_dirty(&mut self, value: bool) { @@ -274,28 +268,31 @@ impl Component for ContactManager { return true; } - if let Some(parent_id) = self.parent_id { + if matches!(self.mode, ViewMode::Discard(_)) { + true + } else { + let Some(parent_id) = self.parent_id else { + return true; + }; /* Play it safe and ask user for confirmation */ self.mode = ViewMode::Discard(Box::new(UIDialog::new( "this contact has unsaved changes", vec![ - ('x', "quit without saving".to_string()), - ('y', "save draft and quit".to_string()), + ('y', "quit without saving".to_string()), ('n', "cancel".to_string()), ], true, - Some(Box::new(move |_, results: &[char]| match results[0] { - 'x' => Some(UIEvent::Action(Tab(TabAction::Kill(parent_id)))), - 'n' => None, - 'y' => None, - _ => None, + Some(Box::new(move |id, results: &[char]| { + if matches!(results.first(), Some(&'y')) { + Some(UIEvent::ComponentUnrealize(parent_id)) + } else { + Some(UIEvent::ComponentUnrealize(id)) + } })), context, ))); self.set_dirty(true); false - } else { - true } } } diff --git a/meli/src/contacts/list.rs b/meli/src/contacts/list.rs index 81f6e396..4f07ec59 100644 --- a/meli/src/contacts/list.rs +++ b/meli/src/contacts/list.rs @@ -24,15 +24,15 @@ use std::cmp; use melib::{backends::AccountHash, text_processing::TextProcessing, Card, CardId, Draft}; use crate::{ - conf, /* contacts::editor::ContactManager, */ shortcut, terminal::*, Action::Tab, - Component, ComponentId, Composer, Context, DataColumns, PageMovement, ScrollContext, - ScrollUpdate, ShortcutMaps, Shortcuts, StatusEvent, TabAction, ThemeAttribute, UIEvent, UIMode, + conf, contacts::editor::ContactManager, shortcut, terminal::*, Action::Tab, Component, + ComponentId, Composer, Context, DataColumns, PageMovement, ScrollContext, ScrollUpdate, + ShortcutMaps, Shortcuts, StatusEvent, TabAction, ThemeAttribute, UIEvent, UIMode, }; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug)] enum ViewMode { List, - View(ComponentId), + View(Box), } #[derive(Debug)] @@ -66,7 +66,6 @@ pub struct ContactList { menu_visibility: bool, movement: Option, cmd_buf: String, - //view: Option, ratio: usize, // right/(container width) * 100 id: ComponentId, } @@ -104,7 +103,6 @@ impl ContactList { dirty: true, movement: None, cmd_buf: String::with_capacity(8), - //view: None, ratio: 90, sidebar_divider: context.settings.listing.sidebar_divider, sidebar_divider_theme: conf::value(context, "mail.sidebar_divider"), @@ -323,9 +321,11 @@ impl ContactList { } fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { + let total_area = area; + /* reserve top row for column headers */ + let header_area = area.nth_row(0); + let area = area.skip_rows(1); if self.length == 0 { - /* reserve top row for column headers */ - let area = area.skip_rows(1); grid.clear_area(area, self.theme_default); grid.copy_area( @@ -333,7 +333,7 @@ impl ContactList { area, self.data_columns.columns[0].area(), ); - context.dirty_areas.push_back(area); + context.dirty_areas.push_back(total_area); return; } let rows = area.height(); @@ -385,7 +385,7 @@ impl ContactList { ScrollUpdate::Update { id: self.id, context: ScrollContext { - shown_lines: top_idx + rows, + shown_lines: (top_idx + rows).min(self.length - top_idx), total_lines: self.length, has_more_lines: false, }, @@ -408,7 +408,7 @@ impl ContactList { if *idx >= self.length { continue; } - let new_area = area.nth_row(1 + *idx % rows); + let new_area = area.nth_row(*idx % rows); self.highlight_line(grid, new_area, *idx); context.dirty_areas.push_back(new_area); } @@ -422,17 +422,11 @@ impl ContactList { } /* Page_no has changed, so draw new page */ - grid.clear_area(area, self.theme_default); - _ = self - .data_columns - .recalc_widths((area.width(), area.height().saturating_sub(1)), top_idx); + grid.clear_area(total_area, self.theme_default); + _ = self.data_columns.recalc_widths(area.size(), top_idx); /* copy table columns */ - self.data_columns.draw( - grid, - top_idx, - self.cursor_pos, - grid.bounds_iter(area.skip_rows(1)), - ); + self.data_columns + .draw(grid, top_idx, self.cursor_pos, grid.bounds_iter(area)); let header_attrs = crate::conf::value(context, "widgets.list.header"); let mut x = 0; @@ -451,18 +445,19 @@ impl ContactList { header_attrs.fg, header_attrs.bg, header_attrs.attrs, - area.skip_cols(x) + header_area + .skip_cols(x) .take_cols(x + (self.data_columns.widths[i])), None, ); x += self.data_columns.widths[i] + 2; // + SEPARATOR - if x > area.width() { + if x > header_area.width() { break; } } - grid.change_theme(area.nth_row(0), header_attrs); + grid.change_theme(header_area, header_attrs); if top_idx + rows > self.length { grid.clear_area( @@ -470,21 +465,17 @@ impl ContactList { self.theme_default, ); } - self.highlight_line( - grid, - area.nth_row(1 + self.cursor_pos % rows), - self.cursor_pos, - ); - context.dirty_areas.push_back(area); + self.highlight_line(grid, area.nth_row(self.cursor_pos % rows), self.cursor_pos); + context.dirty_areas.push_back(total_area); } } impl Component for ContactList { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { - //if let Some(mgr) = self.view.as_mut() { - // mgr.draw(grid, area, context); - // return; - //} + if let ViewMode::View(ref mut mgr) = self.mode { + mgr.draw(grid, area, context); + return; + } if !self.dirty { return; @@ -531,7 +522,6 @@ impl Component for ContactList { UIEvent::VisibilityChange(true) => { self.initialized = false; self.set_dirty(true); - return true; } UIEvent::ConfigReload { old_settings: _ } => { self.theme_default = crate::conf::value(context, "theme_default"); @@ -544,12 +534,6 @@ impl Component for ContactList { self.initialized = false; self.set_dirty(true); } - UIEvent::ComponentUnrealize(ref kill_id) if self.mode == ViewMode::View(*kill_id) => { - self.mode = ViewMode::List; - //self.view.take(); - self.set_dirty(true); - return true; - } UIEvent::ChangeMode(UIMode::Normal) => { self.set_dirty(true); } @@ -559,314 +543,315 @@ impl Component for ContactList { _ => {} } - //if let Some(ref mut v) = self.view { - // if v.process_event(event, context) { - // return true; - // } - //} - - let shortcuts = self.shortcuts(context); - //if self.view.is_none() { - match *event { - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["create_contact"]) => - { - /* - let mut manager = ContactManager::new(context); - manager.set_parent_id(self.id); - manager.account_pos = self.account_pos; - - self.mode = ViewMode::View(manager.id()); - self.view = Some(manager); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate( - ScrollUpdate::End(self.id), - ))); - */ - + if let ViewMode::View(ref mut mgr) = self.mode { + if matches!(event, UIEvent::ComponentUnrealize(id) if *id == mgr.id()) { + mgr.unrealize(context); + self.mode = ViewMode::List; + self.set_dirty(true); return true; } - - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["edit_contact"]) => - { - if self.length == 0 { - return true; - } - /* - let account = &mut context.accounts[self.account_pos]; - let book = &mut account.address_book; - let card = book[&self.id_positions[self.cursor_pos]].clone(); - let mut manager = ContactManager::new(context); - manager.set_parent_id(self.id); - manager.card = card; - manager.account_pos = self.account_pos; - - self.mode = ViewMode::View(manager.id()); - self.view = Some(manager); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate( - ScrollUpdate::End(self.id), - ))); - */ - + if mgr.process_event(event, context) { return true; } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["mail_contact"]) => - { - if self.length == 0 { - return true; - } - let account = &context.accounts[self.account_pos]; - let account_hash = account.hash(); - let book = &account.address_book; - let card = &book[&self.id_positions[self.cursor_pos]]; - let mut draft: Draft = Draft::default(); - *draft.headers_mut().get_mut("To").unwrap() = - format!("{} <{}>", &card.name(), &card.email()); - let mut composer = Composer::with_account(account_hash, context); - composer.set_draft(draft, context); - context - .replies - .push_back(UIEvent::Action(Tab(TabAction::New(Some(Box::new( - composer, - )))))); + } - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["delete_contact"]) => - { - if self.length == 0 { + let shortcuts = self.shortcuts(context); + if matches!(self.mode, ViewMode::List) { + match *event { + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["create_contact"]) => + { + let mut manager = Box::new(ContactManager::new(context)); + manager.set_parent_id(self.id); + manager.account_pos = self.account_pos; + + self.mode = ViewMode::View(manager); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate( + ScrollUpdate::End(self.id), + ))); return true; } - // [ref:TODO]: add a confirmation dialog? - context.accounts[self.account_pos] - .address_book - .remove_card(self.id_positions[self.cursor_pos]); - self.initialized = false; - self.set_dirty(true); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["next_account"]) => - { - let amount = if self.cmd_buf.is_empty() { - 1 - } else if let Ok(amount) = self.cmd_buf.parse::() { - self.cmd_buf.clear(); + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["edit_contact"]) => + { + if self.length == 0 { + return true; + } + let account = &mut context.accounts[self.account_pos]; + let book = &mut account.address_book; + let card = book[&self.id_positions[self.cursor_pos]].clone(); + let mut manager = Box::new(ContactManager::new(context)); + manager.set_parent_id(self.id); + manager.card = card; + manager.account_pos = self.account_pos; + + self.mode = ViewMode::View(manager); context .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - amount - } else { - self.cmd_buf.clear(); + .push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate( + ScrollUpdate::End(self.id), + ))); + return true; + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["mail_contact"]) => + { + if self.length == 0 { + return true; + } + let account = &context.accounts[self.account_pos]; + let account_hash = account.hash(); + let book = &account.address_book; + let card = &book[&self.id_positions[self.cursor_pos]]; + let mut draft: Draft = Draft::default(); + *draft.headers_mut().get_mut("To").unwrap() = + format!("{} <{}>", &card.name(), &card.email()); + let mut composer = Composer::with_account(account_hash, context); + composer.set_draft(draft, context); context .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + .push_back(UIEvent::Action(Tab(TabAction::New(Some(Box::new( + composer, + )))))); + return true; - }; - if self.account_pos + amount < self.accounts.len() { - self.account_pos += amount; - self.set_dirty(true); + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["delete_contact"]) => + { + if self.length == 0 { + return true; + } + // [ref:TODO]: add a confirmation dialog? + context.accounts[self.account_pos] + .address_book + .remove_card(self.id_positions[self.cursor_pos]); self.initialized = false; - self.cursor_pos = 0; - self.new_cursor_pos = 0; - self.length = 0; + self.set_dirty(true); context .replies - .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( - self.status(context), - ))); + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + + return true; } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["next_account"]) => + { + let amount = if self.cmd_buf.is_empty() { + 1 + } else if let Ok(amount) = self.cmd_buf.parse::() { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + amount + } else { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + return true; + }; + if self.account_pos + amount < self.accounts.len() { + self.account_pos += amount; + self.set_dirty(true); + self.initialized = false; + self.cursor_pos = 0; + self.new_cursor_pos = 0; + self.length = 0; + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( + self.status(context), + ))); + } - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["prev_account"]) => - { - let amount = if self.cmd_buf.is_empty() { - 1 - } else if let Ok(amount) = self.cmd_buf.parse::() { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - amount - } else { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); return true; - }; - if self.accounts.is_empty() { + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["prev_account"]) => + { + let amount = if self.cmd_buf.is_empty() { + 1 + } else if let Ok(amount) = self.cmd_buf.parse::() { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + amount + } else { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + return true; + }; + if self.accounts.is_empty() { + return true; + } + if self.account_pos >= amount { + self.account_pos -= amount; + self.set_dirty(true); + self.cursor_pos = 0; + self.new_cursor_pos = 0; + self.length = 0; + self.initialized = false; + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( + self.status(context), + ))); + } return true; } - if self.account_pos >= amount { - self.account_pos -= amount; + UIEvent::Input(ref k) + if shortcut!( + k == shortcuts[Shortcuts::CONTACT_LIST]["toggle_menu_visibility"] + ) => + { + self.menu_visibility = !self.menu_visibility; self.set_dirty(true); - self.cursor_pos = 0; - self.new_cursor_pos = 0; - self.length = 0; - self.initialized = false; - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( - self.status(context), - ))); } - return true; - } - UIEvent::Input(ref k) - if shortcut!(k == shortcuts[Shortcuts::CONTACT_LIST]["toggle_menu_visibility"]) => - { - self.menu_visibility = !self.menu_visibility; - self.set_dirty(true); - } - UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) if !self.cmd_buf.is_empty() => { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - return true; - } - UIEvent::Input(Key::Char(c)) if c.is_ascii_digit() => { - self.cmd_buf.push(c); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufSet( - self.cmd_buf.clone(), - ))); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["scroll_up"]) => - { - let amount = if self.cmd_buf.is_empty() { - 1 - } else if let Ok(amount) = self.cmd_buf.parse::() { + UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) + if !self.cmd_buf.is_empty() => + { self.cmd_buf.clear(); context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - amount - } else { - self.cmd_buf.clear(); + return true; + } + UIEvent::Input(Key::Char(c)) if c.is_ascii_digit() => { + self.cmd_buf.push(c); context .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + .push_back(UIEvent::StatusEvent(StatusEvent::BufSet( + self.cmd_buf.clone(), + ))); return true; - }; - self.movement = Some(PageMovement::Up(amount)); - self.set_dirty(true); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["scroll_down"]) => - { - if self.cursor_pos >= self.length.saturating_sub(1) { + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["scroll_up"]) => + { + let amount = if self.cmd_buf.is_empty() { + 1 + } else if let Ok(amount) = self.cmd_buf.parse::() { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + amount + } else { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + return true; + }; + self.movement = Some(PageMovement::Up(amount)); + self.set_dirty(true); return true; } - let amount = if self.cmd_buf.is_empty() { - 1 - } else if let Ok(amount) = self.cmd_buf.parse::() { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - amount - } else { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::CONTACT_LIST]["scroll_down"]) => + { + if self.cursor_pos >= self.length.saturating_sub(1) { + return true; + } + let amount = if self.cmd_buf.is_empty() { + 1 + } else if let Ok(amount) = self.cmd_buf.parse::() { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + amount + } else { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + return true; + }; + self.set_dirty(true); + self.movement = Some(PageMovement::Down(amount)); return true; - }; - self.set_dirty(true); - self.movement = Some(PageMovement::Down(amount)); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::GENERAL]["prev_page"]) => - { - let mult = if self.cmd_buf.is_empty() { - 1 - } else if let Ok(mult) = self.cmd_buf.parse::() { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - mult - } else { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::GENERAL]["prev_page"]) => + { + let mult = if self.cmd_buf.is_empty() { + 1 + } else if let Ok(mult) = self.cmd_buf.parse::() { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + mult + } else { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + return true; + }; + self.set_dirty(true); + self.movement = Some(PageMovement::PageUp(mult)); return true; - }; - self.set_dirty(true); - self.movement = Some(PageMovement::PageUp(mult)); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::GENERAL]["next_page"]) => - { - let mult = if self.cmd_buf.is_empty() { - 1 - } else if let Ok(mult) = self.cmd_buf.parse::() { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - mult - } else { - self.cmd_buf.clear(); - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::GENERAL]["next_page"]) => + { + let mult = if self.cmd_buf.is_empty() { + 1 + } else if let Ok(mult) = self.cmd_buf.parse::() { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + mult + } else { + self.cmd_buf.clear(); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); + return true; + }; + self.set_dirty(true); + self.movement = Some(PageMovement::PageDown(mult)); return true; - }; - self.set_dirty(true); - self.movement = Some(PageMovement::PageDown(mult)); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::GENERAL]["home_page"]) => - { - self.set_dirty(true); - self.movement = Some(PageMovement::Home); - return true; - } - UIEvent::Input(ref key) - if shortcut!(key == shortcuts[Shortcuts::GENERAL]["end_page"]) => - { - self.set_dirty(true); - self.movement = Some(PageMovement::End); - return true; + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::GENERAL]["home_page"]) => + { + self.set_dirty(true); + self.movement = Some(PageMovement::Home); + return true; + } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Shortcuts::GENERAL]["end_page"]) => + { + self.set_dirty(true); + self.movement = Some(PageMovement::End); + return true; + } + _ => {} } - _ => {} } - //} false } fn is_dirty(&self) -> bool { - self.dirty //|| self.view.as_ref().map(|v| - //|| v.is_dirty()).unwrap_or(false) + self.dirty || matches!(self.mode, ViewMode::View(ref mgr) if mgr.is_dirty()) } fn set_dirty(&mut self, value: bool) { - //if let Some(p) = self.view.as_mut() { - // p.set_dirty(value); - //}; + if let ViewMode::View(ref mut mgr) = self.mode { + mgr.set_dirty(value); + } self.dirty = value; } @@ -876,12 +861,13 @@ impl Component for ContactList { .replies .push_back(UIEvent::Action(Tab(TabAction::Kill(uuid)))); } + fn shortcuts(&self, context: &Context) -> ShortcutMaps { - let mut map = ShortcutMaps::default(); //self - //.view - //.as_ref() - //.map(|p| p.shortcuts(context)) - //.unwrap_or_default(); + let mut map = if let ViewMode::View(ref mgr) = self.mode { + mgr.shortcuts(context) + } else { + ShortcutMaps::default() + }; map.insert( Shortcuts::CONTACT_LIST, @@ -899,10 +885,11 @@ impl Component for ContactList { self.id } - fn can_quit_cleanly(&mut self, _context: &Context) -> bool { + fn can_quit_cleanly(&mut self, context: &Context) -> bool { + if let ViewMode::View(ref mut mgr) = self.mode { + return mgr.can_quit_cleanly(context); + } true - //self.view .as_mut() .map(|p| p.can_quit_cleanly(context)) - // .unwrap_or(true) } fn status(&self, context: &Context) -> String { diff --git a/meli/src/contacts/mod.rs b/meli/src/contacts/mod.rs index 79e33e84..6d8f4a98 100644 --- a/meli/src/contacts/mod.rs +++ b/meli/src/contacts/mod.rs @@ -19,6 +19,5 @@ * along with meli. If not, see . */ +pub mod editor; pub mod list; - -//pub mod editor; diff --git a/meli/src/mail/compose.rs b/meli/src/mail/compose.rs index 6d0cc5c9..4f1f79b8 100644 --- a/meli/src/mail/compose.rs +++ b/meli/src/mail/compose.rs @@ -883,152 +883,139 @@ impl Component for Composer { self.update_form(context); self.initialized = true; } - let header_height = self.form.len(); + let theme_default = crate::conf::value(context, "theme_default"); + if self.dirty { + grid.clear_area(area, theme_default); + } - let mid = 0; - /* - let mid = if width > 80 { - let width = width - 80; - let mid = width / 2; - - if self.dirty { - for i in get_y(upper_left)..=get_y(bottom_right) { - //set_and_join_box(grid, (mid, i), VERT_BOUNDARY); - grid[(mid, i)] - .set_fg(theme_default.fg) - .set_bg(theme_default.bg); - //set_and_join_box(grid, (mid + 80, i), VERT_BOUNDARY); - grid[(mid + 80, i)] - .set_fg(theme_default.fg) - .set_bg(theme_default.bg); - } - } - mid - } else { - 0 - }; - */ + let header_height = self.form.len(); let header_area = area + .skip_rows(1) .take_rows(header_height) - .skip_cols(mid + 1) - .skip_cols_from_end(mid); + .skip_cols(1) + .skip_cols_from_end(1); let attachments_no = self.draft.attachments().len(); let attachment_area = area - .skip_rows(header_height) + .skip_rows(header_height + 1) .skip_rows( area.height() .saturating_sub(header_area.height() + 4 + attachments_no), ) - .skip_cols(mid + 1); + .skip_cols(1) + .skip_cols_from_end(1); let body_area = area - .skip_rows(header_height) - .skip_rows_from_end(attachment_area.height()); + .skip_rows(header_height + 2) + .skip_rows_from_end(attachment_area.height()) + .skip_cols(1) + .skip_cols_from_end(1); - grid.clear_area(area.nth_row(0), crate::conf::value(context, "highlight")); - grid.write_string( - if self.reply_context.is_some() { - "COMPOSING REPLY" - } else { - "COMPOSING MESSAGE" - }, - crate::conf::value(context, "highlight").fg, - crate::conf::value(context, "highlight").bg, - crate::conf::value(context, "highlight").attrs, - area.nth_row(0), - None, - ); - - /* - grid.change_theme( - ( - set_x(pos_dec(header_area.upper_left(), (0, 1)), x), - set_y(header_area.bottom_right(), y), - ), - crate::conf::value(context, "highlight"), - ); + if self.dirty { + grid.clear_area(area.nth_row(0), crate::conf::value(context, "highlight")); + grid.write_string( + if self.reply_context.is_some() { + "COMPOSING REPLY" + } else { + "COMPOSING MESSAGE" + }, + crate::conf::value(context, "highlight").fg, + crate::conf::value(context, "highlight").bg, + crate::conf::value(context, "highlight").attrs, + area.nth_row(0), + None, + ); + } - grid.clear_area( - ( - pos_dec(upper_left, (0, 1)), - set_x(bottom_right, get_x(upper_left) + mid), - ), - theme_default, - ); + /* Regardless of view mode, do the following */ - grid.clear_area( - ( - ( - get_x(bottom_right).saturating_sub(mid), - get_y(upper_left).saturating_sub(1), - ), - bottom_right, - ), - theme_default, - ); - */ + if self.dirty { + match self.cursor { + Cursor::Headers => { + grid.change_theme(header_area, theme_default); + } + Cursor::Body => { + grid.change_theme( + body_area, + ThemeAttribute { + fg: theme_default.fg, + bg: crate::conf::value(context, "highlight").bg, + attrs: if grid.use_color { + crate::conf::value(context, "highlight").attrs + } else { + crate::conf::value(context, "highlight").attrs | Attr::REVERSE + }, + }, + ); + } + Cursor::Sign | Cursor::Encrypt | Cursor::Attachments => {} + } + } - /* Regardless of view mode, do the following */ self.form.draw(grid, header_area, context); + if let Some(ref mut embedded) = self.embedded { let embed_pty = &mut embedded.status; let embed_area = area; match embed_pty { EmbedStatus::Running(_, _) => { - let mut guard = embed_pty.lock().unwrap(); - grid.clear_area(embed_area, theme_default); - - grid.copy_area(guard.grid.buffer(), embed_area, guard.grid.area()); - guard.set_terminal_size((embed_area.width(), embed_area.height())); - context.dirty_areas.push_back(area); - self.dirty = false; + if self.dirty { + let mut guard = embed_pty.lock().unwrap(); + grid.clear_area(embed_area, theme_default); + + grid.copy_area(guard.grid.buffer(), embed_area, guard.grid.area()); + guard.set_terminal_size((embed_area.width(), embed_area.height())); + context.dirty_areas.push_back(embed_area); + self.dirty = false; + } return; } EmbedStatus::Stopped(_, _) => { - let guard = embed_pty.lock().unwrap(); - - grid.copy_area(guard.grid.buffer(), embed_area, guard.grid.buffer().area()); - grid.change_colors(embed_area, Color::Byte(8), theme_default.bg); - let our_map: ShortcutMap = - account_settings!(context[self.account_hash].shortcuts.composing) - .key_values(); - let mut shortcuts: ShortcutMaps = Default::default(); - shortcuts.insert(Shortcuts::COMPOSING, our_map); - let stopped_message: String = - format!("Process with PID {} has stopped.", guard.child_pid); - let stopped_message_2: String = format!( - "-press '{}' (edit shortcut) to re-activate.", - shortcuts[Shortcuts::COMPOSING]["edit"] - ); - const STOPPED_MESSAGE_3: &str = - "-press Ctrl-C to forcefully kill it and return to editor."; - let max_len = std::cmp::max( - stopped_message.len(), - std::cmp::max(stopped_message_2.len(), STOPPED_MESSAGE_3.len()), - ); - let inner_area = create_box(grid, area.center_inside((max_len + 5, 5))); - grid.clear_area(inner_area, theme_default); - for (i, l) in [ - stopped_message.as_str(), - stopped_message_2.as_str(), - STOPPED_MESSAGE_3, - ] - .iter() - .enumerate() - { - grid.write_string( - l, - theme_default.fg, - theme_default.bg, - theme_default.attrs, - inner_area.skip_rows(i), - None, //Some(get_x(inner_area.upper_left())), + if self.dirty { + let guard = embed_pty.lock().unwrap(); + + grid.copy_area(guard.grid.buffer(), embed_area, guard.grid.buffer().area()); + grid.change_colors(embed_area, Color::Byte(8), theme_default.bg); + let our_map: ShortcutMap = + account_settings!(context[self.account_hash].shortcuts.composing) + .key_values(); + let mut shortcuts: ShortcutMaps = Default::default(); + shortcuts.insert(Shortcuts::COMPOSING, our_map); + let stopped_message: String = + format!("Process with PID {} has stopped.", guard.child_pid); + let stopped_message_2: String = format!( + "-press '{}' (edit shortcut) to re-activate.", + shortcuts[Shortcuts::COMPOSING]["edit"] + ); + const STOPPED_MESSAGE_3: &str = + "-press Ctrl-C to forcefully kill it and return to editor."; + let max_len = std::cmp::max( + stopped_message.len(), + std::cmp::max(stopped_message_2.len(), STOPPED_MESSAGE_3.len()), ); + let inner_area = create_box(grid, area.center_inside((max_len + 5, 5))); + grid.clear_area(inner_area, theme_default); + for (i, l) in [ + stopped_message.as_str(), + stopped_message_2.as_str(), + STOPPED_MESSAGE_3, + ] + .iter() + .enumerate() + { + grid.write_string( + l, + theme_default.fg, + theme_default.bg, + theme_default.attrs, + inner_area.skip_rows(i), + None, + ); + } + context.dirty_areas.push_back(area); + self.dirty = false; } - context.dirty_areas.push_back(area); - self.dirty = false; return; } } @@ -1039,51 +1026,13 @@ impl Component for Composer { if self.pager.size().0 > body_area.width() { self.pager.set_initialised(false); } - // Force clean pager area, because if body height is less than body_area it will - // might leave draw artifacts in the remaining area. - grid.clear_area(body_area, theme_default); - self.set_dirty(true); - self.pager.draw(grid, body_area, context); - - match self.cursor { - Cursor::Headers => { - /* - grid.change_theme( - ( - pos_dec(body_area.upper_left(), (1, 0)), - pos_dec( - set_y(body_area.upper_left(), get_y(body_area.bottom_right())), - (1, 0), - ), - ), - theme_default, - ); - */ - } - Cursor::Body => { - /* - grid.change_theme( - ( - pos_dec(body_area.upper_left(), (1, 0)), - pos_dec( - set_y(body_area.upper_left(), get_y(body_area.bottom_right())), - (1, 0), - ), - ), - ThemeAttribute { - fg: theme_default.fg, - bg: crate::conf::value(context, "highlight").bg, - attrs: if grid.use_color { - crate::conf::value(context, "highlight").attrs - } else { - crate::conf::value(context, "highlight").attrs | Attr::REVERSE - }, - }, - ); - */ - } - Cursor::Sign | Cursor::Encrypt | Cursor::Attachments => {} + if self.dirty { + // Force clean pager area, because if body height is less than body_area it will + // might leave draw artifacts in the remaining area. + grid.clear_area(body_area, theme_default); + self.pager.set_dirty(true); } + self.pager.draw(grid, body_area, context); //if !self.mode.is_edit_attachments() { self.draw_attachments(grid, attachment_area, context); @@ -1125,8 +1074,11 @@ impl Component for Composer { s.draw(grid, body_area, context); } } - self.dirty = false; - context.dirty_areas.push_back(area); + + if self.dirty { + self.dirty = false; + context.dirty_areas.push_back(area); + } } fn process_event(&mut self, mut event: &mut UIEvent, context: &mut Context) -> bool { diff --git a/meli/src/types.rs b/meli/src/types.rs index 7d1aba26..7aca12d8 100644 --- a/meli/src/types.rs +++ b/meli/src/types.rs @@ -154,7 +154,6 @@ pub enum UIEvent { Contacts(ContactEvent), Compose(ComposeEvent), FinishedUIDialog(ComponentId, UIMessage), - CanceledUIDialog(ComponentId), IntraComm { from: ComponentId, to: ComponentId, diff --git a/meli/src/utilities/dialogs.rs b/meli/src/utilities/dialogs.rs index 750e8496..50f3ba97 100644 --- a/meli/src/utilities/dialogs.rs +++ b/meli/src/utilities/dialogs.rs @@ -21,11 +21,9 @@ use super::*; -const OK_CANCEL: &str = "OK Cancel"; -const OK_OFFSET: usize = 0; -const OK_LENGTH: usize = "OK".len(); +const OK: &str = "OK"; +const CANCEL: &str = "Cancel"; const CANCEL_OFFSET: usize = "OK ".len(); -const CANCEL_LENGTH: usize = "Cancel".len(); #[derive(Debug, Copy, PartialEq, Eq, Clone)] enum SelectorCursor { @@ -114,10 +112,6 @@ impl Component f } let shortcuts = self.shortcuts(context); - let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted"); - if !context.settings.terminal.use_color() { - highlighted_attrs.attrs |= Attr::REVERSE; - } match (event, self.cursor) { (UIEvent::Input(Key::Char('\n')), _) if self.single_only => { /* User can only select one entry, so Enter key finalises the selection */ @@ -152,8 +146,8 @@ impl Component f } self.done = true; _ = self.done(); - context.replies.push_back(self.cancel()); - + self.cancel(context); + self.set_dirty(true); return false; } (UIEvent::Input(Key::Char('\n')), SelectorCursor::Cancel) if !self.single_only => { @@ -211,26 +205,26 @@ impl Component f self.dirty = true; return true; } - (UIEvent::Input(ref key), SelectorCursor::Entry(c)) + (UIEvent::Input(ref key), SelectorCursor::Entry(_)) if !self.single_only && shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_down"]) => { self.cursor = SelectorCursor::Ok; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Ok) if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_right"]) => { self.cursor = SelectorCursor::Cancel; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Cancel) if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_left"]) => { self.cursor = SelectorCursor::Ok; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), _) @@ -282,10 +276,6 @@ impl Component for UIConfirmationDialog { } let shortcuts = self.shortcuts(context); - let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted"); - if !context.settings.terminal.use_color() { - highlighted_attrs.attrs |= Attr::REVERSE; - } match (event, self.cursor) { (UIEvent::Input(Key::Char('\n')), _) if self.single_only => { /* User can only select one entry, so Enter key finalises the selection */ @@ -294,13 +284,14 @@ impl Component for UIConfirmationDialog { context.replies.push_back(event); self.unrealize(context); } + self.set_dirty(true); return true; } (UIEvent::Input(Key::Char('\n')), SelectorCursor::Entry(c)) if !self.single_only => { /* User can select multiple entries, so Enter key toggles the entry under the * cursor */ self.entries[c].1 = !self.entries[c].1; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(Key::Char('\n')), SelectorCursor::Ok) if !self.single_only => { @@ -309,6 +300,7 @@ impl Component for UIConfirmationDialog { context.replies.push_back(event); self.unrealize(context); } + self.set_dirty(true); return true; } (UIEvent::Input(Key::Esc), _) => { @@ -320,8 +312,8 @@ impl Component for UIConfirmationDialog { } self.done = true; _ = self.done(); - context.replies.push_back(self.cancel()); - + self.cancel(context); + self.set_dirty(true); return false; } (UIEvent::Input(Key::Char('\n')), SelectorCursor::Cancel) if !self.single_only => { @@ -333,6 +325,7 @@ impl Component for UIConfirmationDialog { context.replies.push_back(event); self.unrealize(context); } + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Entry(c)) @@ -344,7 +337,7 @@ impl Component for UIConfirmationDialog { self.entries[c - 1].1 = true; } self.cursor = SelectorCursor::Entry(c - 1); - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Ok) @@ -353,7 +346,7 @@ impl Component for UIConfirmationDialog { { let c = self.entries.len().saturating_sub(1); self.cursor = SelectorCursor::Entry(c); - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Unfocused) @@ -363,7 +356,7 @@ impl Component for UIConfirmationDialog { self.entries[0].1 = true; } self.cursor = SelectorCursor::Entry(0); - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Entry(c)) @@ -376,29 +369,29 @@ impl Component for UIConfirmationDialog { self.entries[c + 1].1 = true; } self.cursor = SelectorCursor::Entry(c + 1); - self.dirty = true; + self.set_dirty(true); return true; } - (UIEvent::Input(ref key), SelectorCursor::Entry(c)) + (UIEvent::Input(ref key), SelectorCursor::Entry(_)) if !self.single_only && shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_down"]) => { self.cursor = SelectorCursor::Ok; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Ok) if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_right"]) => { self.cursor = SelectorCursor::Cancel; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), SelectorCursor::Cancel) if shortcut!(key == shortcuts[Shortcuts::GENERAL]["scroll_left"]) => { self.cursor = SelectorCursor::Ok; - self.dirty = true; + self.set_dirty(true); return true; } (UIEvent::Input(ref key), _) @@ -493,6 +486,11 @@ impl UIDialog }) } - fn cancel(&mut self) -> UIEvent { - let Self { ref id, .. } = self; - UIEvent::CanceledUIDialog(*id) + fn cancel(&mut self, context: &mut Context) { + context.unrealized.insert(self.id()); + context + .replies + .push_back(UIEvent::ComponentUnrealize(self.id())); } } @@ -640,8 +644,10 @@ impl UIConfirmationDialog { }) } - fn cancel(&mut self) -> UIEvent { - let Self { ref id, .. } = self; - UIEvent::CanceledUIDialog(*id) + fn cancel(&mut self, context: &mut Context) { + context.unrealized.insert(self.id()); + context + .replies + .push_back(UIEvent::ComponentUnrealize(self.id())); } } diff --git a/meli/src/utilities/widgets.rs b/meli/src/utilities/widgets.rs index b35d868c..14063283 100644 --- a/meli/src/utilities/widgets.rs +++ b/meli/src/utilities/widgets.rs @@ -24,7 +24,7 @@ use std::{borrow::Cow, collections::HashMap, time::Duration}; use super::*; use crate::melib::text_processing::TextProcessing; -#[derive(Debug, PartialEq, Eq, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] enum FormFocus { #[default] Fields, @@ -350,17 +350,27 @@ impl Component for if self.is_dirty() { let theme_default = crate::conf::value(context, "theme_default"); - grid.clear_area(area.take_rows(self.layout.len()), theme_default); + grid.clear_area(area, theme_default); let label_attrs = crate::conf::value(context, "widgets.form.label"); + let mut highlighted = crate::conf::value(context, "highlight"); + if !context.settings.terminal.use_color() { + highlighted.attrs |= Attr::REVERSE; + } for (i, k) in self.layout.iter().enumerate().rev() { + let theme_attr = if i == self.cursor && self.focus == FormFocus::Fields { + grid.change_theme(area.nth_row(i), highlighted); + highlighted + } else { + label_attrs + }; let v = self.fields.get_mut(k).unwrap(); /* Write field label */ grid.write_string( k.as_ref(), - label_attrs.fg, - label_attrs.bg, - label_attrs.attrs, + theme_attr.fg, + theme_attr.bg, + theme_attr.attrs, area.nth_row(i).skip_cols(1), None, ); @@ -370,47 +380,28 @@ impl Component for area.nth_row(i).skip_cols(self.field_name_max_length + 3), context, ); + grid.change_theme(area.nth_row(i), theme_attr); /* Highlight if necessary */ - if i == self.cursor { - if self.focus == FormFocus::Fields { - let mut field_attrs = - crate::conf::value(context, "widgets.form.highlighted"); - if !context.settings.terminal.use_color() { - field_attrs.attrs |= Attr::REVERSE; - } - for row in grid - .bounds_iter(area.nth_row(i).take_cols(area.width().saturating_sub(1))) - { - for c in row { - grid[c] - .set_fg(field_attrs.fg) - .set_bg(field_attrs.bg) - .set_attrs(field_attrs.attrs); - } - } - } - if self.focus == FormFocus::TextInput { - v.draw_cursor( - grid, - area.nth_row(i).skip_cols(self.field_name_max_length + 3), - area.nth_row(i + 1) - .skip_cols(self.field_name_max_length + 3), - context, - ); - } + if i == self.cursor && self.focus == FormFocus::TextInput { + v.draw_cursor( + grid, + area.nth_row(i).skip_cols(self.field_name_max_length + 3), + area.nth_row(i + 1) + .skip_cols(self.field_name_max_length + 3), + context, + ); } } let length = self.layout.len(); - grid.clear_area(area.skip_rows(length).take_rows(length + 2), theme_default); if !self.hide_buttons { self.buttons - .draw(grid, area.nth_row(length + 3).skip_cols(1), context); + .draw(grid, area.skip_rows(length + 3).skip_cols(1), context); } if length + 4 < area.height() { - grid.clear_area(area.skip_rows(length + 3), theme_default); + grid.clear_area(area.skip_rows(length + 3 + 1), theme_default); } self.set_dirty(false); context.dirty_areas.push_back(area); @@ -623,7 +614,7 @@ where theme_default.bg }, Attr::BOLD, - area.skip_cols(len).take_cols(cur_len + len), + area.skip_cols(len), None, ); len += cur_len + 3;