feat: add in-memory project management

This commit is contained in:
fy59 2026-08-06 14:41:25 +02:00
parent dcf9f09c88
commit 59efdddef9
6 changed files with 285 additions and 40 deletions

View file

@ -5,6 +5,7 @@
void lardon3d_layout_draw( void lardon3d_layout_draw(
const Lardon3DAppState *state, const Lardon3DAppState *state,
const char *project_name_input,
int rows, int rows,
int cols int cols
); );

View file

@ -0,0 +1,15 @@
#ifndef LARDON3D_PROJECT_H
#define LARDON3D_PROJECT_H
#include <stdbool.h>
#include <lardon3d/app_state.h>
bool lardon3d_project_set_name(
Lardon3DAppState *state,
const char *name
);
void lardon3d_project_close(Lardon3DAppState *state);
#endif

View file

@ -27,6 +27,7 @@ executable(
'src/app_state.c', 'src/app_state.c',
'src/tui.c', 'src/tui.c',
'src/layout.c', 'src/layout.c',
'src/project.c',
], ],
include_directories: include_directories('include'), include_directories: include_directories('include'),
dependencies: [ncursesw], dependencies: [ncursesw],

View file

@ -64,6 +64,7 @@ screen_texts(
case LARDON3D_SCREEN_PROJECTS: case LARDON3D_SCREEN_PROJECTS:
*title = "Projets"; *title = "Projets";
*content = "Gestion des projets"; *content = "Gestion des projets";
*footer = "N Nouveau projet C Fermer le projet ESC Accueil Q Quit";
break; break;
case LARDON3D_SCREEN_IMPORT: case LARDON3D_SCREEN_IMPORT:
*title = "Import"; *title = "Import";
@ -87,7 +88,39 @@ screen_texts(
} }
static void 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 *title;
const char *content; 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(1, (columns - title_length) / 2, title_length, title);
draw_text(3, 2, columns - 4, "Projet"); draw_text(3, 2, columns - 4, "Projet");
draw_text(4, 4, columns - 6, project); draw_text(4, 4, columns - 6, project);
draw_text( if (state->screen == LARDON3D_SCREEN_PROJECTS) {
(3 + journal_row) / 2, draw_project_screen(project_name_input, columns);
(columns - content_length) / 2, } else {
content_length, draw_text(
content (3 + journal_row) / 2,
); (columns - content_length) / 2,
content_length,
content
);
}
draw_text(journal_row + 1, 2, columns - 4, "Journal"); draw_text(journal_row + 1, 2, columns - 4, "Journal");
draw_text(journal_row + 2, 4, columns - 6, state->status_message); draw_text(journal_row + 2, 4, columns - 6, state->status_message);
draw_text(rows - 2, 2, columns - 4, footer); draw_text(rows - 2, 2, columns - 4, footer);
@ -117,6 +154,7 @@ draw_content(const Lardon3DAppState *state, int rows, int columns)
void void
lardon3d_layout_draw( lardon3d_layout_draw(
const Lardon3DAppState *state, const Lardon3DAppState *state,
const char *project_name_input,
int rows, int rows,
int columns int columns
) )
@ -127,7 +165,7 @@ lardon3d_layout_draw(
draw_too_small(rows, columns); draw_too_small(rows, columns);
} else { } else {
draw_frame(rows, columns); draw_frame(rows, columns);
draw_content(state, rows, columns); draw_content(state, project_name_input, rows, columns);
} }
(void)refresh(); (void)refresh();

87
src/project.c Normal file
View file

@ -0,0 +1,87 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <lardon3d/project.h>
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é."
);
}

167
src/tui.c
View file

@ -1,16 +1,143 @@
#include <ctype.h>
#include <limits.h>
#include <ncurses.h> #include <ncurses.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#include <lardon3d/layout.h> #include <lardon3d/layout.h>
#include <lardon3d/project.h>
#include <lardon3d/tui.h> #include <lardon3d/tui.h>
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 static void
redraw(const Lardon3DAppState *state) redraw(const Lardon3DAppState *state, const ProjectInput *input)
{ {
int rows; int rows;
int columns; int columns;
getmaxyx(stdscr, rows, 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 bool
@ -36,45 +163,21 @@ lardon3d_tui_run(Lardon3DAppState *state)
return false; return false;
} }
redraw(state); ProjectInput input = {0};
redraw(state, &input);
while (state->running) { while (state->running) {
int key = getch(); int key = getch();
if (key == 'q' || key == 'Q') {
state->running = false;
continue;
}
if (key == ERR) { if (key == ERR) {
return false; return false;
} }
bool should_redraw = true; bool should_redraw = input.active
switch (key) { ? handle_project_input(state, &input, key)
case KEY_F(1): : handle_normal_input(state, &input, key);
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;
}
if (should_redraw) { if (should_redraw) {
redraw(state); redraw(state, &input);
} }
} }