1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use ratatui::{
    backend::Backend,
    layout::{Alignment, Constraint, Direction, Layout},
    prelude::Rect,
    style::{Color, Modifier, Style},
    text::Text,
    widgets::{Block, Borders, Clear, List, ListItem, Paragraph},
    Frame,
};

use crate::app::App;

/// Renders the user interface widgets.
pub fn render<B: Backend>(app: &mut App, frame: &mut Frame) {
    // This is where you add new widgets.
    // See the following resources:
    // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
    // - https://github.com/tui-rs-revival/ratatui/tree/master/examples
    let chunks = Layout::default()
        .direction(Direction::Horizontal)
        .margin(1)
        .constraints(
            [
                Constraint::Percentage(10),
                Constraint::Percentage(20),
                Constraint::Percentage(70),
            ]
            .as_ref(),
        )
        .split(frame.size());
    let books_chunk = chunks[0];
    let pages_chunk = chunks[1];
    let page_content_chunk = chunks[2];

    let books_block = Block::default()
        .title("Books")
        .title_alignment(Alignment::Center)
        .borders(Borders::ALL);

    let items: Vec<ListItem> = app
        .get_books()
        .items
        .iter()
        .map(|i| ListItem::new(i.name.to_string()).style(Style::default().fg(Color::White)))
        .collect();

    // Create a List from all list items and highlight the currently selected one
    let items = List::new(items).block(books_block).highlight_style(
        Style::default()
            .bg(Color::LightGreen)
            .fg(Color::Black)
            .add_modifier(Modifier::BOLD),
    );

    frame.render_stateful_widget(items, books_chunk, &mut app.books.state);

    let pages_block = Block::default()
        .title("Pages")
        .title_alignment(Alignment::Center)
        .borders(Borders::ALL);

    let items: Vec<ListItem> = app
        .get_pages()
        .items
        .iter()
        .map(|i| {
            let _id = i.id.to_string();
            let _summary = i.summary.to_string();
            ListItem::new(format!("({}) {}...", _id, _summary))
                .style(Style::default().fg(Color::White))
        })
        .collect();

    // Create a List from all list items and highlight the currently selected one
    let items = List::new(items).block(pages_block).highlight_style(
        Style::default()
            .bg(Color::Yellow)
            .fg(Color::Black)
            .add_modifier(Modifier::BOLD),
    );

    frame.render_stateful_widget(items, pages_chunk, &mut app.pages.state);

    let content_block = Block::default()
        .title("Content")
        .title_alignment(Alignment::Center)
        .borders(Borders::ALL);
    let page_content = app.get_page_content();
    let paragraph = Paragraph::new(page_content.content)
        .style(Style::default().fg(Color::Gray))
        .block(content_block);
    frame.render_widget(paragraph, page_content_chunk);

    if app.show_popup {
        let input = Paragraph::new(Text::from(app.popup_content.as_str()))
            .style(Style::default().fg(Color::White))
            .block(Block::default().borders(Borders::ALL).title("Rename Book"));
        let area = centered_rect(60, 20, frame.size());
        frame.render_widget(Clear, area); // Clear the background
        frame.render_widget(input, area);
    }
}

/// helper function to create a centered rect using up certain percentage of the available rect `r`
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints(
            [
                Constraint::Percentage((100 - percent_y) / 2),
                Constraint::Percentage(percent_y),
                Constraint::Percentage((100 - percent_y) / 2),
            ]
            .as_ref(),
        )
        .split(r);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints(
            [
                Constraint::Percentage((100 - percent_x) / 2),
                Constraint::Percentage(percent_x),
                Constraint::Percentage((100 - percent_x) / 2),
            ]
            .as_ref(),
        )
        .split(popup_layout[1])[1]
}