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>
|
||||
|
||||
#define WORKSPACE_PAGE_WELCOME "welcome"
|
||||
#define WORKSPACE_PAGE_NODE_INFORMATION "node-information"
|
||||
|
||||
/**
|
||||
* @struct Workspace
|
||||
* @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
|
||||
{
|
||||
GtkWidget *root_widget;
|
||||
GtkWidget *welcome_box;
|
||||
GtkWidget *title_label;
|
||||
GtkWidget *status_label;
|
||||
GtkWidget *instruction_label;
|
||||
GtkWidget *stack;
|
||||
|
||||
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_type_label;
|
||||
GtkWidget *node_parent_label;
|
||||
GtkWidget *node_children_label;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Affiche la page d'accueil.
|
||||
*/
|
||||
static void workspace_show_welcome(
|
||||
Workspace *workspace
|
||||
)
|
||||
|
|
@ -38,14 +43,9 @@ static void workspace_show_welcome(
|
|||
return;
|
||||
}
|
||||
|
||||
gtk_widget_set_visible(
|
||||
workspace->welcome_box,
|
||||
TRUE
|
||||
);
|
||||
|
||||
gtk_widget_set_visible(
|
||||
workspace->node_box,
|
||||
FALSE
|
||||
gtk_stack_set_visible_child_name(
|
||||
GTK_STACK(workspace->stack),
|
||||
WORKSPACE_PAGE_WELCOME
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -76,76 +76,107 @@ Workspace *workspace_new(void)
|
|||
TRUE
|
||||
);
|
||||
|
||||
workspace->welcome_box = gtk_box_new(
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
12
|
||||
);
|
||||
workspace->stack = gtk_stack_new();
|
||||
|
||||
if (workspace->welcome_box == NULL)
|
||||
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
|
||||
);
|
||||
|
||||
/*
|
||||
* Le conteneur d'accueil est centré horizontalement et verticalement
|
||||
* dans l'espace disponible.
|
||||
* Page d'accueil.
|
||||
*/
|
||||
workspace->welcome_page = gtk_box_new(
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
12
|
||||
);
|
||||
|
||||
if (workspace->welcome_page == NULL)
|
||||
{
|
||||
workspace_free(workspace);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gtk_widget_set_halign(
|
||||
workspace->welcome_box,
|
||||
workspace->welcome_page,
|
||||
GTK_ALIGN_CENTER
|
||||
);
|
||||
|
||||
gtk_widget_set_valign(
|
||||
workspace->welcome_box,
|
||||
workspace->welcome_page,
|
||||
GTK_ALIGN_CENTER
|
||||
);
|
||||
|
||||
workspace->title_label = gtk_label_new(
|
||||
workspace->welcome_title_label = gtk_label_new(
|
||||
"Labfy Investigation"
|
||||
);
|
||||
|
||||
workspace->status_label = gtk_label_new(
|
||||
workspace->welcome_status_label = gtk_label_new(
|
||||
"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"
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->welcome_box),
|
||||
workspace->title_label
|
||||
GTK_BOX(workspace->welcome_page),
|
||||
workspace->welcome_title_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->welcome_box),
|
||||
workspace->status_label
|
||||
GTK_BOX(workspace->welcome_page),
|
||||
workspace->welcome_status_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->welcome_box),
|
||||
workspace->instruction_label
|
||||
GTK_BOX(workspace->welcome_page),
|
||||
workspace->welcome_instruction_label
|
||||
);
|
||||
|
||||
workspace->node_box = gtk_box_new(
|
||||
gtk_stack_add_named(
|
||||
GTK_STACK(workspace->stack),
|
||||
workspace->welcome_page,
|
||||
WORKSPACE_PAGE_WELCOME
|
||||
);
|
||||
|
||||
/*
|
||||
* Page d'informations sur le nœud.
|
||||
*/
|
||||
workspace->node_page = gtk_box_new(
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
12
|
||||
);
|
||||
|
||||
if (workspace->node_box == NULL)
|
||||
if (workspace->node_page == NULL)
|
||||
{
|
||||
workspace_free(workspace);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gtk_widget_set_halign(
|
||||
workspace->node_box,
|
||||
workspace->node_page,
|
||||
GTK_ALIGN_CENTER
|
||||
);
|
||||
|
||||
gtk_widget_set_valign(
|
||||
workspace->node_box,
|
||||
workspace->node_page,
|
||||
GTK_ALIGN_CENTER
|
||||
);
|
||||
|
||||
|
|
@ -155,33 +186,29 @@ Workspace *workspace_new(void)
|
|||
workspace->node_children_label = gtk_label_new(NULL);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->node_box),
|
||||
GTK_BOX(workspace->node_page),
|
||||
workspace->node_name_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->node_box),
|
||||
GTK_BOX(workspace->node_page),
|
||||
workspace->node_type_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->node_box),
|
||||
GTK_BOX(workspace->node_page),
|
||||
workspace->node_parent_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->node_box),
|
||||
GTK_BOX(workspace->node_page),
|
||||
workspace->node_children_label
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->root_widget),
|
||||
workspace->welcome_box
|
||||
);
|
||||
|
||||
gtk_box_append(
|
||||
GTK_BOX(workspace->root_widget),
|
||||
workspace->node_box
|
||||
gtk_stack_add_named(
|
||||
GTK_STACK(workspace->stack),
|
||||
workspace->node_page,
|
||||
WORKSPACE_PAGE_NODE_INFORMATION
|
||||
);
|
||||
|
||||
workspace_show_welcome(workspace);
|
||||
|
|
@ -212,7 +239,7 @@ void workspace_set_selected_node(
|
|||
InvestigationNodeType node_type;
|
||||
size_t children_count = 0;
|
||||
|
||||
char *type_text = NULL;
|
||||
const char *type_text = NULL;
|
||||
char *parent_text = NULL;
|
||||
char *children_text = NULL;
|
||||
|
||||
|
|
@ -238,11 +265,11 @@ void workspace_set_selected_node(
|
|||
|
||||
if (node_type == INVESTIGATION_NODE_DIRECTORY)
|
||||
{
|
||||
type_text = g_strdup("Type : dossier");
|
||||
type_text = "Type : dossier";
|
||||
}
|
||||
else
|
||||
{
|
||||
type_text = g_strdup("Type : fichier");
|
||||
type_text = "Type : fichier";
|
||||
}
|
||||
|
||||
gtk_label_set_text(
|
||||
|
|
@ -302,19 +329,13 @@ void workspace_set_selected_node(
|
|||
);
|
||||
}
|
||||
|
||||
gtk_widget_set_visible(
|
||||
workspace->welcome_box,
|
||||
FALSE
|
||||
);
|
||||
|
||||
gtk_widget_set_visible(
|
||||
workspace->node_box,
|
||||
TRUE
|
||||
gtk_stack_set_visible_child_name(
|
||||
GTK_STACK(workspace->stack),
|
||||
WORKSPACE_PAGE_NODE_INFORMATION
|
||||
);
|
||||
|
||||
g_free(children_text);
|
||||
g_free(parent_text);
|
||||
g_free(type_text);
|
||||
}
|
||||
|
||||
void workspace_free(Workspace *workspace)
|
||||
|
|
@ -324,9 +345,5 @@ void workspace_free(Workspace *workspace)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Les widgets GTK sont intégrés dans l'arbre de widgets de MainWindow.
|
||||
* GTK gère donc leur destruction.
|
||||
*/
|
||||
g_free(workspace);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue