Bonjour à toutes et à tous.

Bon nombre d'entre-vous pose souvent cette question. Je voudrais avoir une table avec chaque case entourée, comme dans un tableur. Ayant un peu de temps devant moi je me suis mis à la tâche pour essayer de vous donner une solution élégante à ce "douloureux problème" .

Tout d'abord il est bon de préciser que la solution que je vais vous donner ne fonctionne que pour Gtk+ v3.0 et ultérieures. En effet elle utilise cairo. Ceci étant dit, passons aux choses sérieuses.

Ce qu'il faut comprendre aujourd'hui avec Gtk+v3.0 est que tout widget utilise cairo pour "se dessiner". Nous allons utiliser simplement cette propriété pour ajouter notre petite touche personnelle.

Pour l'exemple j'ai donc créé une fenêtre principale dans laquelle j'insère un GtkGrid. Jusqu'ici rien de bien nouveau. Ensuite à l'aide de deux boucles imbriquées j'insère dans le GtkGrid 9 GtkLabel initialisés avec les coordonnées de chacun d'eux histoire d'avoir un affichage clair. Tout le monde arrive à faire ce genre de chose sans trop de problème. Mais comment maintenant ajouter un cadre autour de chaque case ?

La solution est finalement simple. Puisqu'un widget utilise cairo il déclenche donc le signal "draw". Certains d'entre-vous utilise ce signal de manière courante avec les GtkDrawingArea pour pouvoir dessiner. Nous allons en faire de même pour chaque GtkLabel.
J'assigne un callback pour chaque widget enfant inséré dans le GtkGrid attaché au signal "draw". Dans ce callback je récupère naturellement le contexte graphique (cairo_t) avec lequel je peux dessiner directement sur le widget.

Dans le code exemple çi-dessous j'ai volontairement agrémenté un peu le code. Chaque case est entourée par un trait discontinue de couleur différente histoire que chacun puisse voir comment on peut modifier à son gré l'apparence finale du widget.

J'arrête là le charabia et vous livre le code en pâture. À vos commentaires...

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-  */
/*
 * main.c
 * Copyright (C) 2013 Gerald Dumas <gerald.dumas@laposte.net>
 *
 * tableur is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * tableur is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include <config.h>
#include <gtk/gtk.h>
 
gboolean draw_child_widget (GtkWidget *ChildWidget, cairo_t *cr, GdkRGBA *color)
{
	/* Dans cette fonction nous accédons au context cairo de chaque widget enfant inséré dans le GtkGrid.
	 * Il nous suffit maintenant de dessiner ce que l'on veut dessus.
	 * La couleur de chaque encadrement est récupérée dans le pointeur utilisateur.
	 */
	gdouble dash[2];
	gint width = gtk_widget_get_allocated_width (ChildWidget);
	gint height = gtk_widget_get_allocated_height (ChildWidget);
 
	dash[0]=5;
	dash[1]=10;
	cairo_set_dash (cr, dash, 1, 0); // Affectation du trait discontinue déclaré dans le tableau dash
	cairo_set_line_width (cr, 1); // Epaisseur du trait
	cairo_set_source_rgb (cr, color->red, color->green, color->blue); // Affectation de la couleur récupérée en tant que donnée utilisateur
 
	/*Dessin d'un trait autour du widget*/
	cairo_move_to (cr, 0, 0);
	cairo_line_to (cr, width, 0);
	cairo_line_to (cr, width, height);
	cairo_line_to (cr, 0, height);
	cairo_line_to (cr, 0, 0);
	cairo_stroke (cr);
 
	return FALSE;
}
 
int
main (int argc, char *argv[])
{
	GtkWidget *MainWindow=NULL;
	GtkWidget *Grid = NULL;
	GtkWidget *ChildWidget = NULL;
	int i, j, k=0;
	gchar *CaseNumber = NULL;
 
	/* Déclaration de trois couleurs primaires pour exemple. */
	GdkRGBA color [3];
	color[0].red=1; color[0].green=0; color[0].blue=0;
	color[1].red=0; color[1].green=1; color[1].blue=0;
	color[2].red=0; color[2].green=0; color[2].blue=1;
 
	gtk_init (&argc, &argv);
 
	/*Création d'une fenêtre principale*/
	MainWindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
	/*Création d'une grille dont les cases sont homogènes*/
	Grid = gtk_grid_new ();
	gtk_grid_set_row_homogeneous (GTK_GRID(Grid), TRUE);
	gtk_grid_set_column_homogeneous (GTK_GRID(Grid), TRUE);
	gtk_container_add (GTK_CONTAINER(MainWindow), Grid);
 
	for (j=0; j<3; j++)
		for (i=0; i<3; i++)
		{
			/*Initialisation d'un GtkLabel comme widget enfant*/
			CaseNumber = g_strdup_printf ("(%d,%d)", i,j);
			ChildWidget = gtk_label_new (CaseNumber);
			g_free (CaseNumber);
 
			/*On affecte une fonction callback pour chaque widget enfant accrochée au signal "draw"*/
			g_signal_connect (G_OBJECT (ChildWidget), "draw", (GCallback)draw_child_widget, &color[k]);
 
			/*Finalement on insère le widget enfant dans la grille*/
			gtk_grid_attach (GTK_GRID(Grid), ChildWidget, i, j, 1, 1);
 
			// Pour que la couleur affectée soit cyclique
			if (k<2) k++; else k=0;
		}
 
	g_signal_connect (G_OBJECT (MainWindow), "destroy", (GCallback)gtk_main_quit, NULL);
	g_signal_connect (G_OBJECT (MainWindow), "delete-event", (GCallback)gtk_main_quit, NULL);
 
	gtk_widget_show_all (MainWindow);
 
	gtk_main ();
 
	return 0;
}