refactor(gui): use GtkStack in workspace
This commit is contained in:
parent
ecac36fd8d
commit
33014a5316
3 changed files with 373 additions and 67 deletions
289
docs/tickets/closed/TICKET-016.md
Normal file
289
docs/tickets/closed/TICKET-016.md
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
# Ticket #016
|
||||||
|
|
||||||
|
## Titre
|
||||||
|
|
||||||
|
Structurer le `Workspace` avec `GtkStack`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
Remplacer la gestion manuelle de visibilité des pages du `Workspace` par un
|
||||||
|
`GtkStack`.
|
||||||
|
|
||||||
|
Le `Workspace` doit pouvoir afficher proprement plusieurs pages internes sans
|
||||||
|
modifier son API publique actuelle.
|
||||||
|
|
||||||
|
Ce ticket prépare l'ajout futur de nouvelles vues :
|
||||||
|
|
||||||
|
- aperçu texte ;
|
||||||
|
- aperçu image ;
|
||||||
|
- aperçu PDF ;
|
||||||
|
- chronologie ;
|
||||||
|
- fiches d'entités ;
|
||||||
|
- rapports.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```text
|
||||||
|
Workspace
|
||||||
|
└── GtkStack
|
||||||
|
├── WelcomePage
|
||||||
|
└── NodeInformationPage
|
||||||
|
```
|
||||||
|
|
||||||
|
Plus tard :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Workspace
|
||||||
|
└── GtkStack
|
||||||
|
├── WelcomePage
|
||||||
|
├── NodeInformationPage
|
||||||
|
├── TextPreviewPage
|
||||||
|
├── ImagePreviewPage
|
||||||
|
├── PdfPreviewPage
|
||||||
|
└── EntityPage
|
||||||
|
```
|
||||||
|
|
||||||
|
Le `Workspace` reste responsable de son affichage interne.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Responsabilités
|
||||||
|
|
||||||
|
Le module `Workspace` doit :
|
||||||
|
|
||||||
|
- créer un `GtkStack` comme widget racine interne ;
|
||||||
|
- ajouter une page d'accueil ;
|
||||||
|
- ajouter une page d'informations sur le nœud sélectionné ;
|
||||||
|
- afficher la page d'accueil lorsqu'aucun nœud n'est sélectionné ;
|
||||||
|
- afficher la page d'informations lorsqu'un nœud est sélectionné ;
|
||||||
|
- conserver l'API publique existante.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API publique conservée
|
||||||
|
|
||||||
|
Aucune nouvelle fonction publique n'est requise.
|
||||||
|
|
||||||
|
L'API reste :
|
||||||
|
|
||||||
|
```c
|
||||||
|
Workspace *workspace_new(void);
|
||||||
|
|
||||||
|
GtkWidget *workspace_get_widget(
|
||||||
|
const Workspace *workspace
|
||||||
|
);
|
||||||
|
|
||||||
|
void workspace_set_selected_node(
|
||||||
|
Workspace *workspace,
|
||||||
|
const InvestigationNode *node
|
||||||
|
);
|
||||||
|
|
||||||
|
void workspace_free(
|
||||||
|
Workspace *workspace
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fichiers concernés
|
||||||
|
|
||||||
|
```text
|
||||||
|
src/widgets/workspace.c
|
||||||
|
```
|
||||||
|
|
||||||
|
Éventuellement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
include/widgets/workspace.h
|
||||||
|
```
|
||||||
|
|
||||||
|
uniquement pour mettre à jour la documentation.
|
||||||
|
|
||||||
|
Aucun autre module ne doit être modifié.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structure interne attendue
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct Workspace
|
||||||
|
{
|
||||||
|
GtkWidget *root_widget;
|
||||||
|
GtkWidget *stack;
|
||||||
|
|
||||||
|
GtkWidget *welcome_page;
|
||||||
|
GtkWidget *welcome_title_label;
|
||||||
|
GtkWidget *welcome_status_label;
|
||||||
|
GtkWidget *welcome_instruction_label;
|
||||||
|
|
||||||
|
GtkWidget *node_page;
|
||||||
|
GtkWidget *node_name_label;
|
||||||
|
GtkWidget *node_type_label;
|
||||||
|
GtkWidget *node_parent_label;
|
||||||
|
GtkWidget *node_children_label;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Les noms exacts peuvent varier, mais doivent rester explicites.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pages attendues
|
||||||
|
|
||||||
|
### Page d'accueil
|
||||||
|
|
||||||
|
Nom interne :
|
||||||
|
|
||||||
|
```text
|
||||||
|
welcome
|
||||||
|
```
|
||||||
|
|
||||||
|
Contenu :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Labfy Investigation
|
||||||
|
|
||||||
|
Aucune enquête ouverte
|
||||||
|
|
||||||
|
Sélectionnez ou créez une enquête
|
||||||
|
```
|
||||||
|
|
||||||
|
### Page d'informations
|
||||||
|
|
||||||
|
Nom interne :
|
||||||
|
|
||||||
|
```text
|
||||||
|
node-information
|
||||||
|
```
|
||||||
|
|
||||||
|
Contenu pour un dossier :
|
||||||
|
|
||||||
|
```text
|
||||||
|
00_BaseDeDonnees
|
||||||
|
|
||||||
|
Type : dossier
|
||||||
|
Parent : Template
|
||||||
|
Enfants : 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Contenu pour un fichier :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Enquete.sqlite
|
||||||
|
|
||||||
|
Type : fichier
|
||||||
|
Parent : 00_BaseDeDonnees
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comportement attendu
|
||||||
|
|
||||||
|
Lorsque :
|
||||||
|
|
||||||
|
```c
|
||||||
|
workspace_set_selected_node(workspace, NULL);
|
||||||
|
```
|
||||||
|
|
||||||
|
le `GtkStack` doit afficher :
|
||||||
|
|
||||||
|
```text
|
||||||
|
welcome
|
||||||
|
```
|
||||||
|
|
||||||
|
Lorsqu'un nœud valide est transmis :
|
||||||
|
|
||||||
|
```c
|
||||||
|
workspace_set_selected_node(workspace, node);
|
||||||
|
```
|
||||||
|
|
||||||
|
le `GtkStack` doit afficher :
|
||||||
|
|
||||||
|
```text
|
||||||
|
node-information
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hors périmètre
|
||||||
|
|
||||||
|
Ce ticket ne doit pas :
|
||||||
|
|
||||||
|
- ouvrir un fichier ;
|
||||||
|
- afficher son contenu ;
|
||||||
|
- ajouter une barre de navigation ;
|
||||||
|
- ajouter une animation personnalisée ;
|
||||||
|
- afficher plusieurs nœuds simultanément ;
|
||||||
|
- modifier le Core ;
|
||||||
|
- communiquer avec SQLite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contraintes techniques
|
||||||
|
|
||||||
|
- GTK4 uniquement ;
|
||||||
|
- utiliser `GtkStack` ;
|
||||||
|
- aucune gestion manuelle de visibilité entre les pages ;
|
||||||
|
- aucun état global ;
|
||||||
|
- aucun changement de propriété des nœuds ;
|
||||||
|
- documentation cohérente ;
|
||||||
|
- compilation sans warning ;
|
||||||
|
- aucun `Gtk-CRITICAL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gestion de la propriété
|
||||||
|
|
||||||
|
Le `Workspace` possède uniquement sa structure d'encapsulation.
|
||||||
|
|
||||||
|
Les widgets sont gérés par l'arbre GTK une fois intégrés dans la fenêtre.
|
||||||
|
|
||||||
|
Le nœud reçu reste la propriété du `InvestigationTreeModel`.
|
||||||
|
|
||||||
|
Le `Workspace` ne doit jamais appeler :
|
||||||
|
|
||||||
|
```c
|
||||||
|
investigation_node_free(node);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critères d'acceptation
|
||||||
|
|
||||||
|
- [ ] Le projet compile sans warning.
|
||||||
|
- [ ] `make test` reste entièrement valide.
|
||||||
|
- [ ] `GtkStack` est utilisé.
|
||||||
|
- [ ] La page d'accueil apparaît sans sélection.
|
||||||
|
- [ ] La page d'informations apparaît après sélection.
|
||||||
|
- [ ] Le passage d'une page à l'autre fonctionne.
|
||||||
|
- [ ] Aucun appel manuel à `gtk_widget_set_visible()` n'est utilisé pour changer de page.
|
||||||
|
- [ ] L'API publique de `Workspace` reste stable.
|
||||||
|
- [ ] Aucun autre module n'est modifié.
|
||||||
|
- [ ] Aucun `Gtk-CRITICAL`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tests manuels
|
||||||
|
|
||||||
|
1. Lancer l'application.
|
||||||
|
2. Ouvrir une enquête.
|
||||||
|
3. Vérifier que la page d'accueil est affichée sans sélection.
|
||||||
|
4. Sélectionner un dossier.
|
||||||
|
5. Vérifier l'affichage de la page d'informations.
|
||||||
|
6. Sélectionner un fichier.
|
||||||
|
7. Vérifier la mise à jour de la même page.
|
||||||
|
8. Changer d'enquête.
|
||||||
|
9. Vérifier le retour à la page d'accueil.
|
||||||
|
10. Lancer `make test`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit attendu
|
||||||
|
|
||||||
|
```text
|
||||||
|
refactor(gui): use GtkStack in workspace
|
||||||
|
```
|
||||||
Binary file not shown.
|
|
@ -7,28 +7,33 @@
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
#define WORKSPACE_PAGE_WELCOME "welcome"
|
||||||
|
#define WORKSPACE_PAGE_NODE_INFORMATION "node-information"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @struct Workspace
|
* @struct Workspace
|
||||||
* @brief Représentation interne de la zone de travail.
|
* @brief Représentation interne de la zone de travail.
|
||||||
*
|
|
||||||
* Cette structure reste privée au module. Les autres fichiers utilisent
|
|
||||||
* uniquement le type opaque déclaré dans workspace.h.
|
|
||||||
*/
|
*/
|
||||||
struct Workspace
|
struct Workspace
|
||||||
{
|
{
|
||||||
GtkWidget *root_widget;
|
GtkWidget *root_widget;
|
||||||
GtkWidget *welcome_box;
|
GtkWidget *stack;
|
||||||
GtkWidget *title_label;
|
|
||||||
GtkWidget *status_label;
|
|
||||||
GtkWidget *instruction_label;
|
|
||||||
|
|
||||||
GtkWidget *node_box;
|
GtkWidget *welcome_page;
|
||||||
|
GtkWidget *welcome_title_label;
|
||||||
|
GtkWidget *welcome_status_label;
|
||||||
|
GtkWidget *welcome_instruction_label;
|
||||||
|
|
||||||
|
GtkWidget *node_page;
|
||||||
GtkWidget *node_name_label;
|
GtkWidget *node_name_label;
|
||||||
GtkWidget *node_type_label;
|
GtkWidget *node_type_label;
|
||||||
GtkWidget *node_parent_label;
|
GtkWidget *node_parent_label;
|
||||||
GtkWidget *node_children_label;
|
GtkWidget *node_children_label;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Affiche la page d'accueil.
|
||||||
|
*/
|
||||||
static void workspace_show_welcome(
|
static void workspace_show_welcome(
|
||||||
Workspace *workspace
|
Workspace *workspace
|
||||||
)
|
)
|
||||||
|
|
@ -38,14 +43,9 @@ static void workspace_show_welcome(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_widget_set_visible(
|
gtk_stack_set_visible_child_name(
|
||||||
workspace->welcome_box,
|
GTK_STACK(workspace->stack),
|
||||||
TRUE
|
WORKSPACE_PAGE_WELCOME
|
||||||
);
|
|
||||||
|
|
||||||
gtk_widget_set_visible(
|
|
||||||
workspace->node_box,
|
|
||||||
FALSE
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,76 +76,107 @@ Workspace *workspace_new(void)
|
||||||
TRUE
|
TRUE
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace->welcome_box = gtk_box_new(
|
workspace->stack = gtk_stack_new();
|
||||||
|
|
||||||
|
if (workspace->stack == NULL)
|
||||||
|
{
|
||||||
|
workspace_free(workspace);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_set_hexpand(
|
||||||
|
workspace->stack,
|
||||||
|
TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_widget_set_vexpand(
|
||||||
|
workspace->stack,
|
||||||
|
TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_box_append(
|
||||||
|
GTK_BOX(workspace->root_widget),
|
||||||
|
workspace->stack
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Page d'accueil.
|
||||||
|
*/
|
||||||
|
workspace->welcome_page = gtk_box_new(
|
||||||
GTK_ORIENTATION_VERTICAL,
|
GTK_ORIENTATION_VERTICAL,
|
||||||
12
|
12
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workspace->welcome_box == NULL)
|
if (workspace->welcome_page == NULL)
|
||||||
{
|
{
|
||||||
workspace_free(workspace);
|
workspace_free(workspace);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Le conteneur d'accueil est centré horizontalement et verticalement
|
|
||||||
* dans l'espace disponible.
|
|
||||||
*/
|
|
||||||
gtk_widget_set_halign(
|
gtk_widget_set_halign(
|
||||||
workspace->welcome_box,
|
workspace->welcome_page,
|
||||||
GTK_ALIGN_CENTER
|
GTK_ALIGN_CENTER
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_widget_set_valign(
|
gtk_widget_set_valign(
|
||||||
workspace->welcome_box,
|
workspace->welcome_page,
|
||||||
GTK_ALIGN_CENTER
|
GTK_ALIGN_CENTER
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace->title_label = gtk_label_new(
|
workspace->welcome_title_label = gtk_label_new(
|
||||||
"Labfy Investigation"
|
"Labfy Investigation"
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace->status_label = gtk_label_new(
|
workspace->welcome_status_label = gtk_label_new(
|
||||||
"Aucune enquête ouverte"
|
"Aucune enquête ouverte"
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace->instruction_label = gtk_label_new(
|
workspace->welcome_instruction_label = gtk_label_new(
|
||||||
"Sélectionnez ou créez une enquête"
|
"Sélectionnez ou créez une enquête"
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->welcome_box),
|
GTK_BOX(workspace->welcome_page),
|
||||||
workspace->title_label
|
workspace->welcome_title_label
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->welcome_box),
|
GTK_BOX(workspace->welcome_page),
|
||||||
workspace->status_label
|
workspace->welcome_status_label
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->welcome_box),
|
GTK_BOX(workspace->welcome_page),
|
||||||
workspace->instruction_label
|
workspace->welcome_instruction_label
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace->node_box = gtk_box_new(
|
gtk_stack_add_named(
|
||||||
GTK_ORIENTATION_VERTICAL,
|
GTK_STACK(workspace->stack),
|
||||||
12
|
workspace->welcome_page,
|
||||||
|
WORKSPACE_PAGE_WELCOME
|
||||||
);
|
);
|
||||||
|
|
||||||
if (workspace->node_box == NULL)
|
/*
|
||||||
|
* Page d'informations sur le nœud.
|
||||||
|
*/
|
||||||
|
workspace->node_page = gtk_box_new(
|
||||||
|
GTK_ORIENTATION_VERTICAL,
|
||||||
|
12
|
||||||
|
);
|
||||||
|
|
||||||
|
if (workspace->node_page == NULL)
|
||||||
{
|
{
|
||||||
workspace_free(workspace);
|
workspace_free(workspace);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_widget_set_halign(
|
gtk_widget_set_halign(
|
||||||
workspace->node_box,
|
workspace->node_page,
|
||||||
GTK_ALIGN_CENTER
|
GTK_ALIGN_CENTER
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_widget_set_valign(
|
gtk_widget_set_valign(
|
||||||
workspace->node_box,
|
workspace->node_page,
|
||||||
GTK_ALIGN_CENTER
|
GTK_ALIGN_CENTER
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -155,33 +186,29 @@ Workspace *workspace_new(void)
|
||||||
workspace->node_children_label = gtk_label_new(NULL);
|
workspace->node_children_label = gtk_label_new(NULL);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->node_box),
|
GTK_BOX(workspace->node_page),
|
||||||
workspace->node_name_label
|
workspace->node_name_label
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->node_box),
|
GTK_BOX(workspace->node_page),
|
||||||
workspace->node_type_label
|
workspace->node_type_label
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->node_box),
|
GTK_BOX(workspace->node_page),
|
||||||
workspace->node_parent_label
|
workspace->node_parent_label
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_box_append(
|
||||||
GTK_BOX(workspace->node_box),
|
GTK_BOX(workspace->node_page),
|
||||||
workspace->node_children_label
|
workspace->node_children_label
|
||||||
);
|
);
|
||||||
|
|
||||||
gtk_box_append(
|
gtk_stack_add_named(
|
||||||
GTK_BOX(workspace->root_widget),
|
GTK_STACK(workspace->stack),
|
||||||
workspace->welcome_box
|
workspace->node_page,
|
||||||
);
|
WORKSPACE_PAGE_NODE_INFORMATION
|
||||||
|
|
||||||
gtk_box_append(
|
|
||||||
GTK_BOX(workspace->root_widget),
|
|
||||||
workspace->node_box
|
|
||||||
);
|
);
|
||||||
|
|
||||||
workspace_show_welcome(workspace);
|
workspace_show_welcome(workspace);
|
||||||
|
|
@ -212,7 +239,7 @@ void workspace_set_selected_node(
|
||||||
InvestigationNodeType node_type;
|
InvestigationNodeType node_type;
|
||||||
size_t children_count = 0;
|
size_t children_count = 0;
|
||||||
|
|
||||||
char *type_text = NULL;
|
const char *type_text = NULL;
|
||||||
char *parent_text = NULL;
|
char *parent_text = NULL;
|
||||||
char *children_text = NULL;
|
char *children_text = NULL;
|
||||||
|
|
||||||
|
|
@ -238,11 +265,11 @@ void workspace_set_selected_node(
|
||||||
|
|
||||||
if (node_type == INVESTIGATION_NODE_DIRECTORY)
|
if (node_type == INVESTIGATION_NODE_DIRECTORY)
|
||||||
{
|
{
|
||||||
type_text = g_strdup("Type : dossier");
|
type_text = "Type : dossier";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
type_text = g_strdup("Type : fichier");
|
type_text = "Type : fichier";
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_label_set_text(
|
gtk_label_set_text(
|
||||||
|
|
@ -302,19 +329,13 @@ void workspace_set_selected_node(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_widget_set_visible(
|
gtk_stack_set_visible_child_name(
|
||||||
workspace->welcome_box,
|
GTK_STACK(workspace->stack),
|
||||||
FALSE
|
WORKSPACE_PAGE_NODE_INFORMATION
|
||||||
);
|
|
||||||
|
|
||||||
gtk_widget_set_visible(
|
|
||||||
workspace->node_box,
|
|
||||||
TRUE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
g_free(children_text);
|
g_free(children_text);
|
||||||
g_free(parent_text);
|
g_free(parent_text);
|
||||||
g_free(type_text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void workspace_free(Workspace *workspace)
|
void workspace_free(Workspace *workspace)
|
||||||
|
|
@ -324,9 +345,5 @@ void workspace_free(Workspace *workspace)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Les widgets GTK sont intégrés dans l'arbre de widgets de MainWindow.
|
|
||||||
* GTK gère donc leur destruction.
|
|
||||||
*/
|
|
||||||
g_free(workspace);
|
g_free(workspace);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue