rust_kanban/ui/rendering/view/
signup.rs

1use crate::{
2    app::{
3        state::{AppStatus, Focus, KeyBindingEnum},
4        App,
5    },
6    constants::HIDDEN_PASSWORD_SYMBOL,
7    ui::{
8        rendering::{
9            common::{
10                draw_crab_pattern, draw_title, render_blank_styled_canvas_with_margin,
11                render_close_button,
12            },
13            utils::{
14                calculate_viewport_corrected_cursor_position, centered_rect_with_length,
15                check_if_active_and_get_style, get_mouse_focusable_field_style,
16            },
17            view::Signup,
18        },
19        Renderable,
20    },
21};
22use ratatui::{
23    layout::{Alignment, Constraint, Direction, Layout},
24    text::{Line, Span},
25    widgets::{Block, BorderType, Borders, Clear, Paragraph},
26    Frame,
27};
28
29impl Renderable for Signup {
30    fn render(rect: &mut Frame, app: &mut App, is_active: bool) {
31        if is_active {
32            if app.state.focus == Focus::EmailIDField
33                || app.state.focus == Focus::PasswordField
34                || app.state.focus == Focus::ConfirmPasswordField
35            {
36                if app.state.app_status != AppStatus::UserInput {
37                    app.state.app_status = AppStatus::UserInput;
38                }
39            } else if app.state.app_status != AppStatus::Initialized {
40                app.state.app_status = AppStatus::Initialized;
41            }
42        }
43
44        let main_chunks = Layout::default()
45            .direction(Direction::Vertical)
46            .constraints([Constraint::Length(3), Constraint::Fill(1)].as_ref())
47            .split(rect.area());
48
49        let chunks = Layout::default()
50            .direction(Direction::Horizontal)
51            .constraints([
52                Constraint::Fill(1),
53                Constraint::Length(2),
54                Constraint::Length(50),
55            ])
56            .split(main_chunks[1]);
57
58        let info_box = centered_rect_with_length(30, 10, chunks[0]);
59
60        let info_chunks = Layout::default()
61            .direction(Direction::Vertical)
62            .constraints(
63                [
64                    Constraint::Length(1),
65                    Constraint::Length(1),
66                    Constraint::Length(3),
67                ]
68                .as_ref(),
69            )
70            .margin(1)
71            .split(info_box);
72
73        let form_chunks = Layout::default()
74            .direction(Direction::Vertical)
75            .constraints([
76                Constraint::Length((chunks[2].height - 15) / 2),
77                Constraint::Length(3),
78                Constraint::Length(3),
79                Constraint::Length(3),
80                Constraint::Length(3),
81                Constraint::Length(3),
82                Constraint::Length((chunks[2].height - 15) / 2),
83            ])
84            .margin(1)
85            .split(chunks[2]);
86
87        let show_password_chunks = Layout::default()
88            .direction(Direction::Horizontal)
89            .constraints([
90                Constraint::Length(form_chunks[3].width - 7),
91                Constraint::Length(5),
92            ])
93            .margin(1)
94            .split(form_chunks[4]);
95
96        let submit_button_chunks = Layout::default()
97            .direction(Direction::Horizontal)
98            .constraints([
99                Constraint::Length((form_chunks[4].width - 12) / 2),
100                Constraint::Length(12),
101                Constraint::Length((form_chunks[4].width - 12) / 2),
102            ])
103            .split(form_chunks[5]);
104
105        let email_id_field_style = get_mouse_focusable_field_style(
106            app,
107            Focus::EmailIDField,
108            &form_chunks[1],
109            is_active,
110            true,
111        );
112
113        let password_field_style = get_mouse_focusable_field_style(
114            app,
115            Focus::PasswordField,
116            &form_chunks[2],
117            is_active,
118            true,
119        );
120
121        let confirm_password_field_style = get_mouse_focusable_field_style(
122            app,
123            Focus::ConfirmPasswordField,
124            &form_chunks[3],
125            is_active,
126            true,
127        );
128
129        let show_password_checkbox_style = get_mouse_focusable_field_style(
130            app,
131            Focus::ExtraFocus,
132            &show_password_chunks[1],
133            is_active,
134            false,
135        );
136
137        let submit_button_style = get_mouse_focusable_field_style(
138            app,
139            Focus::SubmitButton,
140            &submit_button_chunks[1],
141            is_active,
142            false,
143        );
144
145        let general_style = check_if_active_and_get_style(
146            is_active,
147            app.current_theme.inactive_text_style,
148            app.current_theme.general_style,
149        );
150        let help_key_style = check_if_active_and_get_style(
151            is_active,
152            app.current_theme.inactive_text_style,
153            app.current_theme.help_key_style,
154        );
155        let help_text_style = check_if_active_and_get_style(
156            is_active,
157            app.current_theme.inactive_text_style,
158            app.current_theme.help_text_style,
159        );
160
161        let crab_paragraph = draw_crab_pattern(
162            chunks[0],
163            app.current_theme.inactive_text_style,
164            is_active,
165            app.config.disable_animations,
166        );
167
168        let info_border = Block::default()
169            .borders(Borders::ALL)
170            .border_type(BorderType::Rounded)
171            .border_style(general_style);
172
173        let info_paragraph = Paragraph::new("Sign Up")
174            .style(general_style)
175            .block(Block::default())
176            .alignment(Alignment::Center);
177
178        let accept_key = app
179            .get_first_keybinding(KeyBindingEnum::Accept)
180            .unwrap_or("".to_string());
181        let next_focus_key = app
182            .get_first_keybinding(KeyBindingEnum::NextFocus)
183            .unwrap_or("".to_string());
184        let prv_focus_key = app
185            .get_first_keybinding(KeyBindingEnum::PrvFocus)
186            .unwrap_or("".to_string());
187
188        let help_spans = vec![
189            Span::styled("Press ", help_text_style),
190            Span::styled(next_focus_key, help_key_style),
191            Span::styled(" or ", help_text_style),
192            Span::styled(prv_focus_key, help_key_style),
193            Span::styled(" to change focus. Press ", help_text_style),
194            Span::styled(accept_key, help_key_style),
195            Span::styled(" to submit.", help_text_style),
196        ];
197
198        let help_paragraph = Paragraph::new(Line::from(help_spans))
199            .style(general_style)
200            .block(Block::default())
201            .alignment(Alignment::Center)
202            .wrap(ratatui::widgets::Wrap { trim: true });
203
204        let separator = Block::default()
205            .borders(Borders::ALL)
206            .border_type(BorderType::Rounded)
207            .border_style(general_style);
208
209        let email_id_block = Block::default()
210            .style(email_id_field_style)
211            .borders(Borders::ALL)
212            .border_type(BorderType::Rounded);
213
214        let password_block = Block::default()
215            .style(password_field_style)
216            .borders(Borders::ALL)
217            .border_type(BorderType::Rounded);
218
219        let confirm_password_block = Block::default()
220            .style(confirm_password_field_style)
221            .borders(Borders::ALL)
222            .border_type(BorderType::Rounded);
223
224        app.state
225            .text_buffers
226            .email_id
227            .set_placeholder_text("Email ID");
228
229        app.state.text_buffers.email_id.set_block(email_id_block);
230
231        app.state
232            .text_buffers
233            .password
234            .set_placeholder_text("Password");
235
236        app.state.text_buffers.password.set_block(password_block);
237
238        app.state
239            .text_buffers
240            .confirm_password
241            .set_placeholder_text("Confirm Password");
242
243        app.state
244            .text_buffers
245            .confirm_password
246            .set_block(confirm_password_block);
247
248        let show_password_paragraph = Paragraph::new("Show Password")
249            .style(general_style)
250            .block(Block::default())
251            .alignment(Alignment::Right);
252
253        let show_password_checkbox_value = if app.state.show_password {
254            "[X]"
255        } else {
256            "[ ]"
257        };
258
259        let show_password_checkbox_paragraph = Paragraph::new(show_password_checkbox_value)
260            .style(show_password_checkbox_style)
261            .block(Block::default())
262            .alignment(Alignment::Center);
263
264        let submit_button = Paragraph::new("Submit")
265            .style(submit_button_style)
266            .block(
267                Block::default()
268                    .borders(Borders::ALL)
269                    .border_type(BorderType::Rounded),
270            )
271            .alignment(Alignment::Center);
272
273        rect.render_widget(draw_title(app, main_chunks[0], is_active), main_chunks[0]);
274        rect.render_widget(crab_paragraph, chunks[0]);
275        rect.render_widget(Clear, info_box);
276        render_blank_styled_canvas_with_margin(rect, app, info_box, is_active, -1);
277        rect.render_widget(info_border, info_box);
278        rect.render_widget(info_paragraph, info_chunks[0]);
279        rect.render_widget(help_paragraph, info_chunks[2]);
280        rect.render_widget(separator, chunks[1]);
281        rect.render_widget(app.state.text_buffers.email_id.widget(), form_chunks[1]);
282
283        if app.state.show_password {
284            rect.render_widget(app.state.text_buffers.password.widget(), form_chunks[2]);
285            rect.render_widget(
286                app.state.text_buffers.confirm_password.widget(),
287                form_chunks[3],
288            );
289        } else {
290            if app.state.text_buffers.password.is_empty() {
291                rect.render_widget(app.state.text_buffers.password.widget(), form_chunks[2]);
292            } else {
293                let hidden_text = HIDDEN_PASSWORD_SYMBOL
294                    .to_string()
295                    .repeat(app.state.text_buffers.password.get_joined_lines().len());
296                let hidden_paragraph = Paragraph::new(hidden_text)
297                    .style(password_field_style)
298                    .block(
299                        Block::default()
300                            .borders(Borders::ALL)
301                            .border_type(BorderType::Rounded),
302                    );
303                rect.render_widget(hidden_paragraph, form_chunks[2]);
304            }
305            if app.state.text_buffers.confirm_password.is_empty() {
306                rect.render_widget(
307                    app.state.text_buffers.confirm_password.widget(),
308                    form_chunks[3],
309                );
310            } else {
311                // TODO: Use the textbox function to hide it
312                let hidden_text = HIDDEN_PASSWORD_SYMBOL.to_string().repeat(
313                    app.state
314                        .text_buffers
315                        .confirm_password
316                        .get_joined_lines()
317                        .len(),
318                );
319                let hidden_paragraph = Paragraph::new(hidden_text)
320                    .style(confirm_password_field_style)
321                    .block(
322                        Block::default()
323                            .borders(Borders::ALL)
324                            .border_type(BorderType::Rounded),
325                    );
326                rect.render_widget(hidden_paragraph, form_chunks[3]);
327            }
328        }
329
330        rect.render_widget(show_password_paragraph, show_password_chunks[0]);
331        rect.render_widget(show_password_checkbox_paragraph, show_password_chunks[1]);
332        rect.render_widget(submit_button, submit_button_chunks[1]);
333        if app.config.enable_mouse_support {
334            render_close_button(rect, app, is_active)
335        }
336
337        if app.state.app_status == AppStatus::UserInput {
338            match app.state.focus {
339                Focus::EmailIDField => {
340                    let (x_pos, y_pos) = calculate_viewport_corrected_cursor_position(
341                        &app.state.text_buffers.email_id,
342                        &app.config.show_line_numbers,
343                        &form_chunks[1],
344                    );
345                    rect.set_cursor_position((x_pos, y_pos));
346                }
347                Focus::PasswordField => {
348                    let (x_pos, y_pos) = calculate_viewport_corrected_cursor_position(
349                        &app.state.text_buffers.password,
350                        &app.config.show_line_numbers,
351                        &form_chunks[2],
352                    );
353                    rect.set_cursor_position((x_pos, y_pos));
354                }
355                Focus::ConfirmPasswordField => {
356                    let (x_pos, y_pos) = calculate_viewport_corrected_cursor_position(
357                        &app.state.text_buffers.confirm_password,
358                        &app.config.show_line_numbers,
359                        &form_chunks[3],
360                    );
361                    rect.set_cursor_position((x_pos, y_pos));
362                }
363                _ => {}
364            }
365        }
366    }
367}