feat: add in-memory project management
This commit is contained in:
parent
dcf9f09c88
commit
59efdddef9
6 changed files with 285 additions and 40 deletions
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
|
||||||
15
include/lardon3d/project.h
Normal file
15
include/lardon3d/project.h
Normal 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
|
||||||
|
|
@ -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],
|
||||||
|
|
|
||||||
42
src/layout.c
42
src/layout.c
|
|
@ -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);
|
||||||
|
if (state->screen == LARDON3D_SCREEN_PROJECTS) {
|
||||||
|
draw_project_screen(project_name_input, columns);
|
||||||
|
} else {
|
||||||
draw_text(
|
draw_text(
|
||||||
(3 + journal_row) / 2,
|
(3 + journal_row) / 2,
|
||||||
(columns - content_length) / 2,
|
(columns - content_length) / 2,
|
||||||
content_length,
|
content_length,
|
||||||
content
|
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
87
src/project.c
Normal 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
167
src/tui.c
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue