summaryrefslogtreecommitdiffstats
path: root/ui/src/components
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-02-04 15:52:12 +0200
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-02-04 17:29:55 +0200
commit8b6ea8de9a89b3ad42276eb7835992f7b875128b (patch)
tree864a7136b4c23d76761ee3e7d034307f8c302838 /ui/src/components
parent6fcc792b83f715644c041f728049de65f7da2e38 (diff)
Remove ui crate
Merge ui crate with root crate. In preparation for uploading `meli` as a separate crate on crates.io. Workspace crates will need to be published as well and having a separate `ui` crate and binary perhaps doesn't make sense anymore.
Diffstat (limited to 'ui/src/components')
-rw-r--r--ui/src/components/contacts.rs307
-rw-r--r--ui/src/components/contacts/contact_list.rs897
-rw-r--r--ui/src/components/indexer.rs134
-rw-r--r--ui/src/components/indexer/index.rs185
-rw-r--r--ui/src/components/mail.rs48
-rw-r--r--ui/src/components/mail/compose.rs1219
-rw-r--r--ui/src/components/mail/listing.rs1192
-rw-r--r--ui/src/components/mail/listing/compact.rs1381
-rw-r--r--ui/src/components/mail/listing/conversations.rs1339
-rw-r--r--ui/src/components/mail/listing/plain.rs1243
-rw-r--r--ui/src/components/mail/listing/thread.rs689
-rw-r--r--ui/src/components/mail/pgp.rs130
-rw-r--r--ui/src/components/mail/status.rs670
-rw-r--r--ui/src/components/mail/view.rs1464
-rw-r--r--ui/src/components/mail/view/envelope.rs571
-rw-r--r--ui/src/components/mail/view/html.rs174
-rw-r--r--ui/src/components/mail/view/thread.rs1152
-rw-r--r--ui/src/components/notifications.rs209
-rw-r--r--ui/src/components/utilities.rs2451
-rw-r--r--ui/src/components/utilities/widgets.rs979
20 files changed, 0 insertions, 16434 deletions
diff --git a/ui/src/components/contacts.rs b/ui/src/components/contacts.rs
deleted file mode 100644
index 91907a58..00000000
--- a/ui/src/components/contacts.rs
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * meli - contacts module
- *
- * Copyright 2019 Manos Pitsidianakis
- *
- * This file is part of meli.
- *
- * meli is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * meli is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with meli. If not, see <http://www.gnu.org/licenses/>.
- */
-
-use super::*;
-use fnv::FnvHashMap;
-
-mod contact_list;
-
-pub use self::contact_list::*;
-
-#[derive(Debug)]
-enum ViewMode {
- ReadOnly,
- Discard(Selector<char>),
- Edit,
- //New,
-}
-
-#[derive(Debug)]
-pub struct ContactManager {
- id: ComponentId,
- parent_id: ComponentId,
- pub card: Card,
- mode: ViewMode,
- form: FormWidget,
- account_pos: usize,
- content: CellBuffer,
- dirty: bool,
- has_changes: bool,
-
- initialized: bool,
-}
-
-impl Default for ContactManager {
- fn default() -> Self {
- ContactManager {
- id: Uuid::nil(),
- parent_id: Uuid::nil(),
- card: Card::new(),
- mode: ViewMode::Edit,
- form: FormWidget::default(),
- account_pos: 0,
- content: CellBuffer::new(100, 1, Cell::with_char(' ')),
- dirty: true,
- has_changes: false,
- initialized: false,
- }
- }
-}
-
-impl fmt::Display for ContactManager {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "contacts")
- }
-}
-
-impl ContactManager {
- fn initialize(&mut self) {
- let (width, _) = self.content.size();
-
- let (x, _) = write_string_to_grid(
- "Last edited: ",
- &mut self.content,
- Color::Byte(250),
- Color::Default,
- Attr::Default,
- ((0, 0), (width - 1, 0)),
- None,
- );
- let (x, y) = write_string_to_grid(
- &self.card.last_edited(),
- &mut self.content,
- Color::Byte(250),
- Color::Default,
- Attr::Default,
- ((x, 0), (width - 1, 0)),
- None,
- );
-
- if self.card.external_resource() {
- self.mode = ViewMode::ReadOnly;
- self.content
- .resize(self.content.size().0, 2, Cell::default());
- write_string_to_grid(
- "This contact's origin is external and cannot be edited within meli.",
- &mut self.content,
- Color::Byte(250),
- Color::Default,
- Attr::Default,
- ((x, y), (width - 1, y)),
- None,
- );
- }
-
- self.form = FormWidget::new("Save".into());
- self.form.add_button(("Cancel(Esc)".into(), false));
- self.form
- .push(("NAME".into(), self.card.name().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()));
- for (k, v) in self.card.extra_properties() {
- self.form.push((k.to_string(), v.to_string()));
- }
- }
-
- pub fn set_parent_id(&mut self, new_val: ComponentId) {
- self.parent_id = new_val;
- }
-}
-
-impl Component for ContactManager {
- fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
- if !self.initialized {
- self.initialize();
- self.initialized = true;
- }
-
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
-
- if self.dirty {
- let (width, _height) = self.content.size();
- clear_area(
- grid,
- (upper_left, set_y(bottom_right, get_y(upper_left) + 1)),
- );
- copy_area_with_break(grid, &self.content, area, ((0, 0), (width - 1, 0)));
- self.dirty = false;
- }
-
- self.form.draw(
- grid,
- (set_y(upper_left, get_y(upper_left) + 2), bottom_right),
- context,
- );
- match self.mode {
- ViewMode::Discard(ref mut selector) => {
- /* Let user choose whether to quit with/without saving or cancel */
- selector.draw(grid, center_area(area, selector.content.size()), context);
- }
- _ => {}
- }
-
- context.dirty_areas.push_back(area);
- }
-
- fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
- match self.mode {
- ViewMode::Discard(ref mut selector) => {
- if selector.process_event(event, context) {
- if selector.is_done() {
- let s = match std::mem::replace(&mut self.mode, ViewMode::Edit) {
- ViewMode::Discard(s) => s,
- _ => unreachable!(),
- };
- let key = s.collect()[0] as char;
- match key {
- 'x' => {
- context
- .replies
- .push_back(UIEvent::Action(Tab(Kill(self.parent_id))));
- return true;
- }
- 'n' => {}
- 'y' => {}
- _ => {}
- }
- }
- self.set_dirty(true);
- return true;
- }
- }
- ViewMode::Edit => {
- if let &mut UIEvent::Input(Key::Esc) = event {
- if self.can_quit_cleanly(context) {
- context
- .replies
- .push_back(UIEvent::Action(Tab(Kill(self.parent_id))));
- }
- return true;
- }
- if self.form.process_event(event, context) {
- match self.form.buttons_result() {
- None => {}
- Some(true) => {
- let fields = std::mem::replace(&mut self.form, FormWidget::default())
- .collect()
- .unwrap();
- let fields: FnvHashMap<String, String> = fields
- .into_iter()
- .map(|(s, v)| {
- (
- s,
- match v {
- Field::Text(v, _) => v.as_str().to_string(),
- Field::Choice(mut v, c) => v.remove(c),
- },
- )
- })
- .collect();
- let mut new_card = Card::from(fields);
- new_card.set_id(*self.card.id());
- context.accounts[self.account_pos]
- .address_book
- .add_card(new_card);
- context.replies.push_back(UIEvent::StatusEvent(
- StatusEvent::DisplayMessage("Saved.".into()),
- ));
- context.replies.push_back(UIEvent::ComponentKill(self.id));
- }
- Some(false) => {
- context.replies.push_back(UIEvent::ComponentKill(self.id));
- }
- }
- self.set_dirty(true);
- if let UIEvent::InsertInput(_) = event {
- self.has_changes = true;
- }
- return true;
- }
- }
- ViewMode::ReadOnly => {
- if let &mut UIEvent::Input(Key::Esc) = event {
- if self.can_quit_cleanly(context) {
- context.replies.push_back(UIEvent::ComponentKill(self.id));
- }
- return true;
- }
- }
- }
- false
- }
-
- fn is_dirty(&self) -> bool {
- self.dirty
- || self.form.is_dirty()
- || if let ViewMode::Discard(ref selector) = self.mode {
- selector.is_dirty()
- } else {
- false
- }
- }
-
- fn set_dirty(&mut self, value: bool) {
- self.dirty = value;
- self.form.set_dirty(value);
- if let ViewMode::Discard(ref mut selector) = self.mode {
- selector.set_dirty(value);
- }
- }
-
- fn id(&self) -> ComponentId {
- self.id
- }
-
- fn set_id(&mut self, id: ComponentId) {
- self.id = id;
- }
-
- fn can_quit_cleanly(&mut self, context: &Context) -> bool {
- if !self.has_changes {
- return true;
- }
-
- /* Play it safe and ask user for confirmation */
- self.mode = ViewMode::Discard(Selector::new(
- "this contact has unsaved changes",
- vec![
- ('x', "quit without saving".to_string()),
- ('y', "save draft and quit".to_string()),
- ('n', "cancel".to_string()),
- ],
- true,
- context,
- ));
- self.set_dirty(true);
- false
- }
-}
diff --git a/ui/src/components/contacts/contact_list.rs b/ui/src/components/contacts/contact_list.rs
deleted file mode 100644
index a79d4cfd..00000000
--- a/ui/src/components/contacts/contact_list.rs
+++ /dev/null
@@ -1,897 +0,0 @@
-/*
- * meli
- *
- * Copyright 2019 Manos Pitsidianakis
- *
- * This file is part of meli.
- *
- * meli is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * meli is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with meli. If not, see <http://www.gnu.org/licenses/>.
- */
-use super::*;
-
-use melib::CardId;
-use std::cmp;
-
-const MAX_COLS: usize = 500;
-
-#[derive(Debug, PartialEq)]
-enum ViewMode {
- List,
- View(ComponentId),
-}
-
-#[derive(Debug)]
-struct AccountMenuEntry {
- name: String,
- // Index in the config account vector.
- index: usize,
-}
-
-#[derive(Debug)]
-pub struct ContactList {
- accounts: Vec<AccountMenuEntry>,
- cursor_pos: usize,
- new_cursor_pos: usize,
- account_pos: usize,
- length: usize,
- data_columns: DataColumns,
- initialized: bool,
-
- id_positions: Vec<CardId>,
-
- mode: ViewMode,
- dirty: bool,
- show_divider: bool,
- menu_visibility: bool,
- movement: Option<PageMovement>,
- cmd_buf: String,
- view: Option<ContactManager>,
- ratio: usize, // right/(container width) * 100
- id: ComponentId,
-}
-
-impl fmt::Display for ContactList {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", ContactList::DESCRIPTION)
- }
-}
-
-impl ContactList {
- const DESCRIPTION: &'static str = "contact list";
- pub fn new(context: &Context) -> Self {
- let accounts = context
- .accounts
- .iter()
- .enumerate()
- .map(|(i, a)| AccountMenuEntry {
- name: a.name().to_string(),
- index: i,
- })
- .collect();
- ContactList {
- accounts,
- cursor_pos: 0,
- new_cursor_pos: 0,
- length: 0,
- account_pos: 0,
- id_positions: Vec::new(),
- mode: ViewMode::List,
- data_columns: DataColumns::default(),
- initialized: false,
- dirty: true,
- movement: None,
- cmd_buf: String::with_capacity(8),
- view: None,
- ratio: 90,
- show_divider: false,
- menu_visibility: true,
- id: ComponentId::new_v4(),
- }
- }
-
- pub fn for_account(pos: usize, context: &Context) -> Self {
- ContactList {
- account_pos: pos,
- ..Self::new(context)
- }
- }
-
- fn initialize(&mut self, context: &mut Context) {
- let account = &context.accounts[self.account_pos];
- let book = &account.address_book;
- self.length = book.len();
-
- self.id_positions.clear();
- if self.id_positions.capacity() < book.len() {
- self.id_positions.reserve(book.len());
- }
- self.dirty = true;
- let mut min_width = ("Name".len(), "E-mail".len(), 0, "external".len(), 0, 0);
-
- for c in book.values() {
- /* name */
- min_width.0 = cmp::max(min_width.0, c.name().split_graphemes().len());
- /* email */
- min_width.1 = cmp::max(min_width.1, c.email().split_graphemes().len());
- /* url */
- min_width.2 = cmp::max(min_width.2, c.url().split_graphemes().len());
- }
-
- /* name column */
- self.data_columns.columns[0] =
- CellBuffer::new_with_context(min_width.0, self.length, Cell::with_char(' '), context);
- /* email column */
- self.data_columns.columns[1] =
- CellBuffer::new_with_context(min_width.1, self.length, Cell::with_char(' '), context);
- /* url column */
- self.data_columns.columns[2] =
- CellBuffer::new_with_context(min_width.2, self.length, Cell::with_char(' '), context);
- /* source column */
- self.data_columns.columns[3] = CellBuffer::new_with_context(
- "external".len(),
- self.length,
- Cell::with_char(' '),
- context,
- );
-
- let account = &context.accounts[self.account_pos];
- let book = &account.address_book;
- let mut book_values = book.values().collect::<Vec<&Card>>();
- book_values.sort_unstable_by_key(|c| c.name());
- for (idx, c) in book_values.iter().enumerate() {
- self.id_positions.push(*c.id());
-
- write_string_to_grid(
- c.name(),
- &mut self.data_columns.columns[0],
- Color::Default,
- Color::Default,
- Attr::Default,
- ((0, idx), (min_width.0, idx)),
- None,
- );
-
- write_string_to_grid(
- c.email(),
- &mut self.data_columns.columns[1],
- Color::Default,
- Color::Default,
- Attr::Default,
- ((0, idx), (min_width.1, idx)),
- None,
- );
-
- write_string_to_grid(
- c.url(),
- &mut self.data_columns.columns[2],
- Color::Default,
- Color::Default,
- Attr::Default,
- ((0, idx), (min_width.2, idx)),
- None,
- );
-
- write_string_to_grid(
- if c.external_resource() {
- "external"
- } else {
- "local"
- },
- &mut self.data_columns.columns[3],
- Color::Default,
- Color::Default,
- Attr::Default,
- ((0, idx), (min_width.3, idx)),
- None,
- );
- }
-
- if self.length == 0 {
- let message = "Address book is empty.".to_string();
- self.data_columns.columns[0] = CellBuffer::new_with_context(
- message.len(),
- self.length,
- Cell::with_char(' '),
- context,
- );
- write_string_to_grid(
- &message,
- &mut self.data_columns.columns[0],
- Color::Default,
- Color::Default,
- Attr::Default,
- ((0, 0), (MAX_COLS - 1, 0)),
- None,
- );
- return;
- }
- }
-
- fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize) {
- /* Reset previously highlighted line */
- let fg_color = Color::Default;
- let bg_color = if idx == self.new_cursor_pos {
- Color::Byte(246)
- } else {
- Color::Default
- };
- change_colors(grid, area, fg_color, bg_color);
- }
-
- fn draw_menu(&mut self, grid: &mut CellBuffer, mut area: Area, context: &mut Context) {
- if !self.is_dirty() {
- return;
- }
- clear_area(grid, area);
- /* visually divide menu and listing */
- area = (area.0, pos_dec(area.1, (1, 0)));
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
- self.dirty = false;
- let mut y = get_y(upper_left);
- for a in &self.accounts {
- self.print_account(grid, (set_y(upper_left, y), bottom_right), &a, context);
- y += 1;
- }
-
- context.dirty_areas.push_back(area);
- }
- /*
- * Print a single account in the menu area.
- */
- fn print_account(
- &self,
- grid: &mut CellBuffer,
- area: Area,
- a: &AccountMenuEntry,
- context: &mut Context,
- ) {
- if !is_valid_area!(area) {
- debug!("BUG: invalid area in print_account");
- }
-
- let width = width!(area);
- let must_highlight_account: bool = self.account_pos == a.index;
- let (fg_color, bg_color) = if must_highlight_account {
- if self.account_pos == a.index {
- (Color::Byte(233), Color::Byte(15))
- } else {
- (Color::Byte(15), Color::Byte(233))
- }
- } else {
- (Color::Default, Color::Default)
- };
-
- let s = format!(" [{}]", context.accounts[a.index].address_book.len());
-
- if a.name.grapheme_len() + s.len() > width + 1 {
- /* Print account name */
- let (x, y) =
- write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, None);
- write_string_to_grid(
- &s,
- grid,
- fg_color,
- bg_color,
- Attr::Bold,
- (
- pos_dec(
- (get_x(bottom_right!(area)), get_y(upper_left!(area))),
- (s.len() - 1, 0),
- ),
- bottom_right!(area),
- ),
- None,
- );
- write_string_to_grid(
- "…",
- grid,
- fg_color,
- bg_color,
- Attr::Bold,
- (
- pos_dec(
- (get_x(bottom_right!(area)), get_y(upper_left!(area))),
- (s.len() - 1, 0),
- ),
- bottom_right!(area),
- ),
- None,
- );
-
- for x in x..=get_x(bottom_right!(area)) {
- grid[(x, y)].set_fg(fg_color);
- grid[(x, y)].set_bg(bg_color);
- }
- } else {
- /* Print account name */
-
- let (x, y) =
- write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, None);
- write_string_to_grid(
- &s,
- grid,
- fg_color,
- bg_color,
- Attr::Bold,
- (
- pos_dec(
- (get_x(bottom_right!(area)), get_y(upper_left!(area))),
- (s.len() - 1, 0),
- ),
- bottom_right!(area),
- ),
- None,
- );
- for x in x..=get_x(bottom_right!(area)) {
- grid[(x, y)].set_fg(fg_color);
- grid[(x, y)].set_bg(bg_color);
- }
- }
- }
-
- fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
- /* reserve top row for column headers */
- let upper_left = pos_inc(upper_left!(area), (0, 1));
- let bottom_right = bottom_right!(area);
-
- if self.length == 0 {
- clear_area(grid, area);
- copy_area(
- grid,
- &self.data_columns.columns[0],
- area,
- ((0, 0), pos_dec(self.data_columns.columns[0].size(), (1, 1))),
- );
- context.dirty_areas.push_back(area);
- return;
- }
- let rows = get_y(bottom_right) - get_y(upper_left) + 1;
-
- if let Some(mvm) = self.movement.take() {
- match mvm {
- PageMovement::Up(amount) => {
- self.new_cursor_pos = self.new_cursor_pos.saturating_sub(amount);
- }
- PageMovement::PageUp(multiplier) => {
- self.new_cursor_pos = self.new_cursor_pos.saturating_sub(rows * multiplier);
- }
- PageMovement::Down(amount) => {
- if self.new_cursor_pos + amount < self.length {
- self.new_cursor_pos += amount;
- } else {
- self.new_cursor_pos = self.length - 1;
- }
- }
- PageMovement::PageDown(multiplier) => {
- if self.new_cursor_pos + rows * multiplier < self.length {
- self.new_cursor_pos += rows * multiplier;
- } else if self.new_cursor_pos + rows * multiplier > self.length {
- self.new_cursor_pos = self.length - 1;
- } else {
- self.new_cursor_pos = (self.length / rows) * rows;
- }
- }
- PageMovement::Right(_) | PageMovement::Left(_) => {}
- PageMovement::Home => {
- self.new_cursor_pos = 0;
- }
- PageMovement::End => {
- self.new_cursor_pos = self.length - 1;
- }
- }
- }
-
- let prev_page_no = (self.cursor_pos).wrapping_div(rows);
- let page_no = (self.new_cursor_pos).wrapping_div(rows);
-
- let top_idx = page_no * rows;
-
- /* If cursor position has changed, remove the highlight from the previous position and
- * apply it in the new one. */
- if self.cursor_pos != self.new_cursor_pos && prev_page_no == page_no {
- let old_cursor_pos = self.cursor_pos;
- self.cursor_pos = self.new_cursor_pos;
- for idx in &[old_cursor_pos, self.new_cursor_pos] {
- if *idx >= self.length {
- continue; //bounds check
- }
- let new_area = (
- set_y(upper_left, get_y(upper_left) + (*idx % rows)),
- set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
- );
- self.highlight_line(grid, new_area, *idx);
- context.dirty_areas.push_back(new_area);
- }
- return;
- } else if self.cursor_pos != self.new_cursor_pos {
- self.cursor_pos = self.new_cursor_pos;
- }
- if self.new_cursor_pos >= self.length {
- self.new_cursor_pos = self.length - 1;
- self.cursor_pos = self.new_cursor_pos;
- }
-
- let width = width!(area);
- self.data_columns.widths = Default::default();
- self.data_columns.widths[0] = self.data_columns.columns[0].size().0; /* name */
- self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* email*/
- self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* url */
- self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* source */
-
- let min_col_width = std::cmp::min(
- 15,
- std::cmp::min(self.data_columns.widths[0], self.data_columns.widths[1]),
- );
- if self.data_columns.widths[0] + self.data_columns.widths[1] + 3 * min_col_width + 8 > width
- {
- let remainder =
- width.saturating_sub(self.data_columns.widths[0] + self.data_columns.widths[1] + 4);
- self.data_columns.widths[2] = remainder / 6;
- }
- clear_area(grid, area);
- /* Page_no has changed, so draw new page */
- let mut x = get_x(upper_left);
- for i in 0..self.data_columns.columns.len() {
- if self.data_columns.widths[i] == 0 {
- continue;
- }
- let (column_width, column_height) = self.data_columns.columns[i].size();
- write_string_to_grid(
- match i {
- 0 => "NAME",
- 1 => "E-MAIL",
- 2 => "URL",
- 3 => "SOURCE",
- _ => "",
- },
- grid,
- Color::Black,
- Color::White,
- Attr::Bold,
- (
- set_x(upper_left!(area), x),
- (
- std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
- get_y(upper_left!(area)),
- ),
- ),
- None,
- );
- copy_area(
- grid,
- &self.data_columns.columns[i],
- (
- set_x(upper_left, x),
- set_x(
- bottom_right,
- std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
- ),
- ),
- (
- (0, top_idx),
- (
- column_width.saturating_sub(1),
- column_height.saturating_sub(1),
- ),
- ),
- );
- x += self.data_columns.widths[i] + 2; // + SEPARATOR
- if x > get_x(bottom_right) {
- break;
- }
- }
-
- change_colors(
- grid,
- (
- upper_left!(area),
- set_y(bottom_right, get_y(upper_left!(area))),
- ),
- Color::Black,
- Color::White,
- );
-
- if top_idx + rows > self.length {
- clear_area(
- grid,
- (
- pos_inc(upper_left, (0, self.length - top_idx + 2)),
- bottom_right,
- ),
- );
- }
- self.highlight_line(
- grid,
- (
- set_y(upper_left, get_y(upper_left) + (self.cursor_pos % rows)),
- set_y(bottom_right, get_y(upper_left) + (self.cursor_pos % rows)),
- ),
- self.cursor_pos,
- );
- context.dirty_areas.push_back(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 !self.dirty {
- return;
- }
- if !self.initialized {
- self.initialize(context);
- }
-
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
- let total_cols = get_x(bottom_right) - get_x(upper_left);
-
- let right_component_width = if self.menu_visibility {
- (self.ratio * total_cols) / 100
- } else {
- total_cols
- };
- let mid = get_x(bottom_right) - right_component_width;
- if self.dirty && mid != get_x(upper_left) {
- if self.show_divider {
- for i in get_y(upper_left)..=get_y(bottom_right) {
- grid[(mid, i)].set_ch(VERT_BOUNDARY);
- grid[(mid, i)].set_fg(Color::Default);
- grid[(mid, i)].set_bg(Color::Default);
- }
- } else {
- for i in get_y(upper_left)..=get_y(bottom_right) {
- grid[(mid, i)].set_fg(Color::Default);
- grid[(mid, i)].set_bg(Color::Default);
- }
- }
- context
- .dirty_areas
- .push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
- }
-
- if right_component_width == total_cols {
- self.draw_list(grid, area, co