diff --git a/include/lardon3d/layout.h b/include/lardon3d/layout.h index b97e3e5..d8b2a35 100644 --- a/include/lardon3d/layout.h +++ b/include/lardon3d/layout.h @@ -5,6 +5,7 @@ void lardon3d_layout_draw( const Lardon3DAppState *state, + const char *project_name_input, int rows, int cols ); diff --git a/include/lardon3d/project.h b/include/lardon3d/project.h new file mode 100644 index 0000000..6be226a --- /dev/null +++ b/include/lardon3d/project.h @@ -0,0 +1,15 @@ +#ifndef LARDON3D_PROJECT_H +#define LARDON3D_PROJECT_H + +#include + +#include + +bool lardon3d_project_set_name( + Lardon3DAppState *state, + const char *name +); + +void lardon3d_project_close(Lardon3DAppState *state); + +#endif diff --git a/meson.build b/meson.build index e811e94..f9a80c4 100644 --- a/meson.build +++ b/meson.build @@ -27,6 +27,7 @@ executable( 'src/app_state.c', 'src/tui.c', 'src/layout.c', + 'src/project.c', ], include_directories: include_directories('include'), dependencies: [ncursesw], diff --git a/src/layout.c b/src/layout.c index b925b32..6e932f4 100644 --- a/src/layout.c +++ b/src/layout.c @@ -64,6 +64,7 @@ screen_texts( case LARDON3D_SCREEN_PROJECTS: *title = "Projets"; *content = "Gestion des projets"; + *footer = "N Nouveau projet C Fermer le projet ESC Accueil Q Quit"; break; case LARDON3D_SCREEN_IMPORT: *title = "Import"; @@ -87,7 +88,39 @@ screen_texts( } static void -draw_content(const Lardon3DAppState *state, int rows, int columns) +draw_project_screen(const char *project_name_input, int columns) +{ + draw_text(6, 4, columns - 6, "N : Nouveau projet"); + draw_text(7, 4, columns - 6, "C : Fermer le projet"); + draw_text(8, 4, columns - 6, "ESC : Accueil"); + draw_text(9, 4, columns - 6, "Q : Quitter"); + + if (!project_name_input) { + return; + } + + draw_text(10, 4, columns - 6, "Nom du nouveau projet :"); + (void)mvaddch(11, 2, '['); + (void)mvaddch(11, columns - 3, ']'); + + int available = columns - 8; + size_t length = strlen(project_name_input); + const char *visible = project_name_input; + if (length > (size_t)available) { + visible += length - (size_t)available; + length = (size_t)available; + } + draw_text(11, 4, available, visible); + (void)move(11, 4 + (int)length); +} + +static void +draw_content( + const Lardon3DAppState *state, + const char *project_name_input, + int rows, + int columns +) { const char *title; const char *content; @@ -103,12 +136,16 @@ draw_content(const Lardon3DAppState *state, int rows, int columns) draw_text(1, (columns - title_length) / 2, title_length, title); draw_text(3, 2, columns - 4, "Projet"); draw_text(4, 4, columns - 6, project); - draw_text( - (3 + journal_row) / 2, - (columns - content_length) / 2, - content_length, - content - ); + if (state->screen == LARDON3D_SCREEN_PROJECTS) { + draw_project_screen(project_name_input, columns); + } else { + draw_text( + (3 + journal_row) / 2, + (columns - content_length) / 2, + content_length, + content + ); + } draw_text(journal_row + 1, 2, columns - 4, "Journal"); draw_text(journal_row + 2, 4, columns - 6, state->status_message); draw_text(rows - 2, 2, columns - 4, footer); @@ -117,6 +154,7 @@ draw_content(const Lardon3DAppState *state, int rows, int columns) void lardon3d_layout_draw( const Lardon3DAppState *state, + const char *project_name_input, int rows, int columns ) @@ -127,7 +165,7 @@ lardon3d_layout_draw( draw_too_small(rows, columns); } else { draw_frame(rows, columns); - draw_content(state, rows, columns); + draw_content(state, project_name_input, rows, columns); } (void)refresh(); diff --git a/src/project.c b/src/project.c new file mode 100644 index 0000000..a018dad --- /dev/null +++ b/src/project.c @@ -0,0 +1,87 @@ +#include +#include +#include + +#include + +bool +lardon3d_project_set_name(Lardon3DAppState *state, const char *name) +{ + if (!state) { + return false; + } + + if (!name) { + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Erreur : le nom du projet est vide." + ); + return false; + } + + const char *start = name; + while (*start && isspace((unsigned char)*start)) { + ++start; + } + + const char *end = name + strlen(name); + while (end > start && isspace((unsigned char)end[-1])) { + --end; + } + + size_t length = (size_t)(end - start); + if (length == 0) { + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Erreur : le nom du projet est vide." + ); + return false; + } + + if (length >= sizeof(state->project_name)) { + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Erreur : le nom du projet est trop long." + ); + return false; + } + + (void)memcpy(state->project_name, start, length); + state->project_name[length] = '\0'; + state->project_loaded = true; + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Projet créé : %s", + state->project_name + ); + return true; +} + +void +lardon3d_project_close(Lardon3DAppState *state) +{ + if (!state) { + return; + } + + if (!state->project_loaded) { + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Aucun projet à fermer." + ); + return; + } + + state->project_loaded = false; + state->project_name[0] = '\0'; + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Projet fermé." + ); +} diff --git a/src/tui.c b/src/tui.c index b57ffbe..abf93a0 100644 --- a/src/tui.c +++ b/src/tui.c @@ -1,16 +1,143 @@ +#include +#include #include #include +#include #include +#include #include +enum { + PROJECT_INPUT_CAPACITY = 256, + MINIMUM_ROWS = 20, + MINIMUM_COLUMNS = 72, +}; + +typedef struct { + bool active; + char text[PROJECT_INPUT_CAPACITY]; + size_t length; +} ProjectInput; + static void -redraw(const Lardon3DAppState *state) +redraw(const Lardon3DAppState *state, const ProjectInput *input) { int rows; int columns; getmaxyx(stdscr, rows, columns); - lardon3d_layout_draw(state, rows, columns); + + const char *text = input->active ? input->text : NULL; + lardon3d_layout_draw(state, text, rows, columns); + (void)curs_set( + input->active && rows >= MINIMUM_ROWS && columns >= MINIMUM_COLUMNS + ? 1 + : 0 + ); +} + +static bool +handle_project_input( + Lardon3DAppState *state, + ProjectInput *input, + int key +) +{ + if (key == KEY_RESIZE) { + return true; + } + + if (key == 27) { + input->active = false; + (void)snprintf( + state->status_message, + sizeof(state->status_message), + "Création du projet annulée." + ); + return true; + } + + if (key == '\n' || key == '\r' || key == KEY_ENTER) { + (void)lardon3d_project_set_name(state, input->text); + input->active = false; + return true; + } + + if (key == KEY_BACKSPACE || key == 127 || key == '\b') { + if (input->length > 0) { + --input->length; + input->text[input->length] = '\0'; + } + return true; + } + + if (key >= 0 && key <= UCHAR_MAX && isprint((unsigned char)key)) { + if (input->length + 1 >= sizeof(input->text)) { + (void)lardon3d_project_set_name(state, input->text); + input->active = false; + return true; + } + + input->text[input->length] = (char)key; + ++input->length; + input->text[input->length] = '\0'; + return true; + } + + return false; +} + +static bool +handle_normal_input( + Lardon3DAppState *state, + ProjectInput *input, + int key +) +{ + if (key == 'q' || key == 'Q') { + state->running = false; + return false; + } + + switch (key) { + case KEY_F(1): + state->screen = LARDON3D_SCREEN_HELP; + return true; + case KEY_F(2): + state->screen = LARDON3D_SCREEN_PROJECTS; + return true; + case KEY_F(3): + state->screen = LARDON3D_SCREEN_IMPORT; + return true; + case KEY_F(4): + state->screen = LARDON3D_SCREEN_VIEWER; + return true; + case 27: + state->screen = LARDON3D_SCREEN_HOME; + return true; + case KEY_RESIZE: + return true; + case 'n': + case 'N': + if (state->screen == LARDON3D_SCREEN_PROJECTS) { + *input = (ProjectInput) { + .active = true, + .text = "", + .length = 0, + }; + return true; + } + return false; + case 'c': + case 'C': + if (state->screen == LARDON3D_SCREEN_PROJECTS) { + lardon3d_project_close(state); + return true; + } + return false; + default: + return false; + } } bool @@ -36,45 +163,21 @@ lardon3d_tui_run(Lardon3DAppState *state) return false; } - redraw(state); + ProjectInput input = {0}; + redraw(state, &input); while (state->running) { int key = getch(); - - if (key == 'q' || key == 'Q') { - state->running = false; - continue; - } if (key == ERR) { return false; } - bool should_redraw = true; - switch (key) { - case KEY_F(1): - state->screen = LARDON3D_SCREEN_HELP; - break; - case KEY_F(2): - state->screen = LARDON3D_SCREEN_PROJECTS; - break; - case KEY_F(3): - state->screen = LARDON3D_SCREEN_IMPORT; - break; - case KEY_F(4): - state->screen = LARDON3D_SCREEN_VIEWER; - break; - case 27: - state->screen = LARDON3D_SCREEN_HOME; - break; - case KEY_RESIZE: - break; - default: - should_redraw = false; - break; - } + bool should_redraw = input.active + ? handle_project_input(state, &input, key) + : handle_normal_input(state, &input, key); if (should_redraw) { - redraw(state); + redraw(state, &input); } }