term_do 0.7.0

This is a small terminal tool for tracking tasks and also my first project! It is simple, but hope it is as helpful for other people as it is for me.
use tui::backend::Backend;
use tui::Frame;
use tui::layout::{Rect};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, BorderType, ListItem};
use crate::{App, centered_rect, DataManager, FocusedLayout, LayoutCommon, LayoutCommonTrait};

use tui::{widgets::{List}};

use unicode_width::UnicodeWidthStr;
use crate::data_manager::GroupItem;
use crate::enums::InputMode;

pub struct GroupLayout {
    pub(crate) layout_common: LayoutCommon
}

impl GroupLayout {
    pub fn new() -> GroupLayout {
        GroupLayout {
            layout_common: LayoutCommon::new()
        }
    }
}






impl LayoutCommonTrait for GroupLayout {

    fn handle_input(&mut self, data_manager: &mut DataManager, key_code: crossterm::event::KeyEvent) {
        match self.layout_common.input_mode {
            InputMode::Navigate => {
                if data_manager.config.get_key("add_group") == key_code.code {
                    self.layout_common.input_mode = InputMode::Add;
                    self.layout_common.input = String::new();
                    self.layout_common.cursor_pos = self.layout_common.input.width();

                    LayoutCommon::recalculate_input_string_starting_point(&mut self.layout_common);
                } else if data_manager.config.get_key("edit_group") == key_code.code {
                    if data_manager.get_group_items().is_empty() {
                        return;
                    }

                    self.layout_common.input_mode = InputMode::Edit;
                    self.layout_common.input = data_manager.get_group(data_manager.selected_group).name.clone();
                    self.layout_common.cursor_pos = self.layout_common.input.width();

                    LayoutCommon::recalculate_input_string_starting_point(&mut self.layout_common);
                } else if data_manager.config.get_key("delete_group") == key_code.code {
                    if data_manager.get_group_items().is_empty() {
                        return;
                    }

                    data_manager.apply();
                    data_manager.delete_group_item(data_manager.selected_group);
                    data_manager.save_state();
                    data_manager.selected_group = 0;
                } else if data_manager.config.get_key("up_group") == key_code.code {
                    if data_manager.get_group_items().is_empty() {
                        return;
                    }

                    if data_manager.selected_group > 0 {
                        data_manager.selected_group -= 1;
                        data_manager.load_folding(data_manager.selected_group);
                    }
                } else if data_manager.config.get_key("down_group") == key_code.code {
                    if data_manager.get_group_items().is_empty() {
                        return;
                    }

                    if data_manager.selected_group < data_manager.get_group_items().len() - 1 {
                        data_manager.selected_group += 1;
                        data_manager.load_folding(data_manager.selected_group);
                    }
                }
            },
            InputMode::Add => {
                if data_manager.config.get_key("apply_add_or_edit_group") == key_code.code {
                    data_manager.apply();

                    let mut gi = GroupItem::new(data_manager);
                    gi.name = self.layout_common.input.drain(..).collect();
                    data_manager.add_group_item(gi);
                    self.layout_common.input_mode = InputMode::Navigate;
                    data_manager.save_state();
                } else {
                    <GroupLayout as LayoutCommonTrait>::poll_common_keys_input_mode(&key_code, &mut self.layout_common)
                }
            },
            InputMode::Edit => {
                if data_manager.config.get_key("apply_add_or_edit_group") == key_code.code {
                    data_manager.apply();

                    data_manager.edit_group_item(data_manager.selected_group, self.layout_common.input.drain(..).collect());
                    self.layout_common.input_mode = InputMode::Navigate;
                    data_manager.save_state();
                } else {
                    <GroupLayout as LayoutCommonTrait>::poll_common_keys_input_mode(&key_code, &mut self.layout_common)
                }
            }
        }
    }

     fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App, chunk: &Vec<Rect>, frame_size: &Rect) {
        GroupLayout::create_and_render_base_block(f, app, chunk);
        GroupLayout::create_and_render_item_list(f, app, chunk, frame_size);
    }

    fn create_and_render_base_block<B: Backend>(f: &mut Frame<B>, app: &mut App, chunk: &Vec<Rect>) {
        let mut groups_block = Block::default()
            .title("Groups")
            .borders(Borders::ALL).style(Style::default().fg(app.data_manager.config.get_color("group", "border_color")));

        if app.focused_layout == FocusedLayout::GroupsLayout {
            groups_block = groups_block.style(Style::default().add_modifier(Modifier::BOLD).fg(app.data_manager.config.get_color("group", "border_color")))
                .border_type(BorderType::Thick);
        }

        f.render_widget(groups_block, chunk[0]);
    }

     fn create_and_render_item_list<B: Backend>(f: &mut Frame<B>, app: &mut App, chunk: &Vec<Rect>, frame_size: &Rect) {
        let area = centered_rect(85, 85, chunk[0]);
        let mut items_list: Vec<ListItem> = Vec::new();

        let height = ListItem::new("Hello").style(Style::default()).height();
        let max_lines : usize = (frame_size.height as usize / (2 * height)) as usize;
        let selected_group = app.data_manager.selected_group;
        let showing_start_item = if selected_group > max_lines { selected_group - max_lines } else { 0 };

        for i in showing_start_item..app.data_manager.get_group_items().len() {
            let mut line = String::new();
            let group_name = app.data_manager.get_group_items()[i].name.as_str();
            line.push_str(group_name);

            if i == app.data_manager.selected_group {
                line = format!("{}{}", app.data_manager.config.group.get("icon").unwrap(), "  ").to_string();
                line.push_str(group_name);
                items_list.push(ListItem::new(line).style(Style::default().fg(app.data_manager.config.get_color("group", "selected_color")).remove_modifier(Modifier::BOLD)));
                continue;
            }

            line = format!("{}{}", app.data_manager.config.group.get("icon").unwrap(), "  ").to_string();
            line.push_str(group_name);
            items_list.push(ListItem::new(line).style(Style::default().fg(app.data_manager.config.get_color("group", "non_selected_color")).remove_modifier(Modifier::BOLD)));
        }

        let items = List::new(items_list)
            .block(Block::default().borders(Borders::NONE))
            .highlight_style(
                Style::default()
                    .fg(Color::LightGreen)
                    .add_modifier(Modifier::BOLD),
            );

        f.render_widget(items, area);
    }

    fn create_and_render_edit_mode<B: Backend>(f: &mut Frame<B>, app: &mut App, chunk: &Vec<Rect>) {
        let title : String = if app.group_layout.layout_common.input_mode == InputMode::Add { "Add group".to_string() } else { "Edit group".to_string() };
        <GroupLayout as LayoutCommonTrait>::render_common_input_mode(f, &mut app.group_layout.layout_common, title.as_str(), chunk);
    }
}