I. L'article original

Cet article est une adaptation en langue française de How to tile widgets in a multiple document interface application, de Titta Heikkala.

II. Nouvelles actions

Tout d'abord, on ajoute les actions qui appelleront les fonctions de repositionnement. Ainsi, on ajoute une action pour chaque fonction dans la classe MainWindow en tant qu'attribut privé :

 
Sélectionnez
QAction *tileVerticalAct;
QAction *tileHorizontalAct;

On ajoute aussi un slot privé pour chaque fonction de réarrangement :

 
Sélectionnez
void tileSubWindowsVertically();
void tileSubWindowsHorizontally();

Il suffit ensuite d'initialiser les actions et de les connecter aux slots adéquats. Le code suivant dans la méthode createActions() initialise ces actions pour l'application :

 
Sélectionnez
tileVerticalAct = new QAction(tr("Tile Vertically"), this);
tileVerticalAct->setStatusTip(tr("Tile the windows vertically"));
connect(tileVerticalAct, SIGNAL(triggered()), this, SLOT(tileSubWindowsVertically()));

tileHorizontalAct = new QAction(tr("Tile Horizontally"), this);
tileHorizontalAct->setStatusTip(tr("Tile the windows horizontally"));
connect(tileHorizontalAct, SIGNAL(triggered()), this, SLOT(tileSubWindowsHorizontally()));

Ensuite, on ajoute ces actions dans le menu de la fenêtre. Ceci peut se faire dans la méthode updateWindowMenu() où les autres actions seront aussi ajoutées au menu :

 
Sélectionnez
windowMenu->addAction(tileVerticalAct);
windowMenu->addAction(tileHorizontalAct);

III. Réarrangement des fenêtres

Maintenant que l'application est prête à réarranger les sous-fenêtres, on regarde de plus près la méthode tileSubWindowsVertically(). Pour commencer, on vérifie l'existence de sous-fenêtres dans l'aire MDI :

 
Sélectionnez
if (mdiArea->subWindowList().isEmpty())
    return;

Ensuite, on crée un rectangle qui définit la taille idéale de chacune des sous-fenêtres. Lors d'un réarrangement vertical, la largeur d'une sous-fenêtre sera la largeur de l'aire MDI. Sa hauteur sera fonction de la hauteur de l'aire MDI et du nombre de sous-fenêtres ouvertes. En gros, il suffit de diviser la hauteur de l'aire MDI par le nombre de sous-fenêtres qui sont ouvertes. Ce rectangle peut alors être utilisé pour définir la taille de chaque sous-fenêtre dans la zone MDI.

 
Sélectionnez
QPoint position(0, 0);

foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
    QRect rect(0, 0, mdiArea->width(), 
               mdiArea->height() / mdiArea->subWindowList().count());
    window->setGeometry(rect);
    window->move(position);
    position.setY(position.y() + window->height());
}

Après avoir modifié la dimension de chaque sous-fenêtre, on modifie aussi leur position pour que les fenêtres soient empilées verticalement. Les sous-fenêtres sont empilées de haut en bas, selon leur indice. Par défaut, cet ordre est celui de l'insertion de la sous-fenêtre dans la fenêtre principale. La première sous-fenêtre est déplacée dans le coin haut gauche de la fenêtre principale. Pendant ce temps, la position est ajustée de façon à ce que la fenêtre en cours de traitement se place juste en dessous de la fenêtre précédemment traitée. C'est la seule chose requise pour arranger les fenêtres verticalement.

Image non disponible

L'arrangement horizontal des sous-fenêtres sera presque identique ; il faut simplement ajuster la position sur l'axe des abscisses des sous-fenêtres. La hauteur des sous-fenêtres sera celle de la fenêtre principale. Les widgets seront positionnés de gauche à droite selon les index des sous-fenêtres, de manière croissante.

 
Sélectionnez
void MainWindow::tileSubWindowsHorizontally()
{
    if (mdiArea->subWindowList().isEmpty())
        return;
    QPoint position(0, 0);
    foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
        QRect rect(0, 0, mdiArea->width() / mdiArea->subWindowList().count(), 
                   mdiArea->height());
        window->setGeometry(rect);
        window->move(position);
        position.setX(position.x() + window->width());
    }
}

IV. Conclusions

Alors, pas si compliqué finalement ? Vous pouvez essayer ce code (sources).

V. Remerciements

Un tout grand merci à Thibaut Cuvelier et à zoom61 pour leur relecture attentive.