rust_kanban/ui/rendering/view/
new_card_form.rs1use crate::{
2 app::{
3 state::{AppStatus, Focus, KeyBindingEnum},
4 App,
5 },
6 ui::{
7 rendering::{
8 common::render_close_button,
9 utils::{
10 calculate_viewport_corrected_cursor_position, check_if_active_and_get_style,
11 get_mouse_focusable_field_style,
12 },
13 view::NewCardForm,
14 },
15 PopUp, Renderable,
16 },
17};
18use ratatui::{
19 layout::{Alignment, Constraint, Direction, Layout},
20 text::{Line, Span},
21 widgets::{Block, BorderType, Borders, Paragraph},
22 Frame,
23};
24
25impl Renderable for NewCardForm {
26 fn render(rect: &mut Frame, app: &mut App, is_active: bool) {
27 let chunks = Layout::default()
28 .direction(Direction::Vertical)
29 .constraints(
30 [
31 Constraint::Length(3),
32 Constraint::Length(5),
33 Constraint::Fill(1),
34 Constraint::Length(3),
35 Constraint::Length(4),
36 Constraint::Length(3),
37 ]
38 .as_ref(),
39 )
40 .split(rect.area());
41
42 let card_due_date = app
43 .widgets
44 .date_time_picker
45 .get_date_time_as_string(app.config.date_time_format);
46
47 if app.state.z_stack.last() == Some(&PopUp::DateTimePicker) {
48 if app.widgets.date_time_picker.anchor.is_none() {
49 app.widgets.date_time_picker.anchor = Some((
50 chunks[3].x + card_due_date.len() as u16 + 2,
51 chunks[3].y + 3,
52 )); }
54 app.widgets.date_time_picker.current_viewport = Some(rect.area());
55 }
56
57 let general_style = check_if_active_and_get_style(
58 is_active,
59 app.current_theme.inactive_text_style,
60 app.current_theme.general_style,
61 );
62 let name_style =
63 get_mouse_focusable_field_style(app, Focus::CardName, &chunks[1], is_active, false);
64 let description_style = get_mouse_focusable_field_style(
65 app,
66 Focus::CardDescription,
67 &chunks[2],
68 is_active,
69 false,
70 );
71 let due_date_style =
72 get_mouse_focusable_field_style(app, Focus::CardDueDate, &chunks[3], is_active, false);
73 let help_key_style = check_if_active_and_get_style(
74 is_active,
75 app.current_theme.inactive_text_style,
76 app.current_theme.help_key_style,
77 );
78 let help_text_style = check_if_active_and_get_style(
79 is_active,
80 app.current_theme.inactive_text_style,
81 app.current_theme.help_text_style,
82 );
83 let submit_style =
84 get_mouse_focusable_field_style(app, Focus::SubmitButton, &chunks[5], is_active, false);
85
86 let title_paragraph = Paragraph::new("Create a new Card")
87 .alignment(Alignment::Center)
88 .block(
89 Block::default()
90 .borders(Borders::ALL)
91 .border_type(BorderType::Rounded)
92 .style(general_style),
93 );
94 rect.render_widget(title_paragraph, chunks[0]);
95
96 let card_name_block = Block::default()
97 .borders(Borders::ALL)
98 .style(name_style)
99 .border_type(BorderType::Rounded)
100 .title("Card Name (required)");
101 app.state.text_buffers.card_name.set_block(card_name_block);
102 rect.render_widget(app.state.text_buffers.card_name.widget(), chunks[1]);
103 let description_length = app.state.text_buffers.card_description.get_num_lines();
104 let description_block = Block::default()
105 .title(format!("Description ({} line(s))", description_length))
106 .borders(Borders::ALL)
107 .border_type(BorderType::Rounded)
108 .border_style(description_style);
109
110 if app.config.show_line_numbers {
111 app.state
112 .text_buffers
113 .card_description
114 .set_line_number_style(general_style)
115 } else {
116 app.state.text_buffers.card_description.remove_line_number()
117 }
118 app.state
119 .text_buffers
120 .card_description
121 .set_block(description_block.clone());
122 rect.render_widget(app.state.text_buffers.card_description.widget(), chunks[2]);
123
124 let card_due_date = app
125 .widgets
126 .date_time_picker
127 .get_date_time_as_string(app.config.date_time_format);
128 let card_due_date_paragraph = Paragraph::new(card_due_date).block(
129 Block::default()
130 .title("Due Date")
131 .borders(Borders::ALL)
132 .style(due_date_style)
133 .border_type(BorderType::Rounded),
134 );
135 rect.render_widget(card_due_date_paragraph, chunks[3]);
136
137 let input_mode_key = app
138 .get_first_keybinding(KeyBindingEnum::TakeUserInput)
139 .unwrap_or("".to_string());
140 let next_focus_key = app
141 .get_first_keybinding(KeyBindingEnum::NextFocus)
142 .unwrap_or("".to_string());
143 let prv_focus_key = app
144 .get_first_keybinding(KeyBindingEnum::PrvFocus)
145 .unwrap_or("".to_string());
146 let accept_key = app
147 .get_first_keybinding(KeyBindingEnum::Accept)
148 .unwrap_or("".to_string());
149 let cancel_key = app
150 .get_first_keybinding(KeyBindingEnum::GoToPreviousViewOrCancel)
151 .unwrap_or("".to_string());
152 let stop_user_input_key = app
153 .get_first_keybinding(KeyBindingEnum::StopUserInput)
154 .unwrap_or("".to_string());
155
156 let help_spans = Line::from(vec![
157 Span::styled("Press ", help_text_style),
158 Span::styled(input_mode_key, help_key_style),
159 Span::styled(" or ", help_text_style),
160 Span::styled(accept_key.clone(), help_key_style),
161 Span::styled(" to start typing. Press ", help_text_style),
162 Span::styled(stop_user_input_key, help_key_style),
163 Span::styled(" to stop typing. Press ", help_text_style),
164 Span::styled(next_focus_key, help_key_style),
165 Span::styled(" or ", help_text_style),
166 Span::styled(prv_focus_key, help_key_style),
167 Span::styled(" to switch focus. Press ", help_text_style),
168 Span::styled(accept_key, help_key_style),
169 Span::styled(" to submit. Press ", help_text_style),
170 Span::styled(cancel_key, help_key_style),
171 Span::styled(" to cancel", help_text_style),
172 ]);
173
174 let help_paragraph = Paragraph::new(help_spans)
175 .alignment(Alignment::Center)
176 .block(
177 Block::default()
178 .borders(Borders::ALL)
179 .border_type(BorderType::Rounded)
180 .border_style(general_style),
181 )
182 .wrap(ratatui::widgets::Wrap { trim: true });
183 rect.render_widget(help_paragraph, chunks[4]);
184
185 let submit_button = Paragraph::new("Submit").alignment(Alignment::Center).block(
186 Block::default()
187 .borders(Borders::ALL)
188 .style(submit_style)
189 .border_type(BorderType::Rounded),
190 );
191 rect.render_widget(submit_button, chunks[5]);
192
193 if app.state.app_status == AppStatus::UserInput {
194 match app.state.focus {
195 Focus::CardName => {
196 let (x_pos, y_pos) = calculate_viewport_corrected_cursor_position(
197 &app.state.text_buffers.card_name,
198 &app.config.show_line_numbers,
199 &chunks[1],
200 );
201 rect.set_cursor_position((x_pos, y_pos));
202 }
203 Focus::CardDescription => {
204 let (x_pos, y_pos) = calculate_viewport_corrected_cursor_position(
205 &app.state.text_buffers.card_description,
206 &app.config.show_line_numbers,
207 &chunks[2],
208 );
209 rect.set_cursor_position((x_pos, y_pos));
210 }
211 _ => {}
212 }
213 }
214
215 if app.config.enable_mouse_support {
216 render_close_button(rect, app, is_active);
217 }
218 }
219}