I. L'article original

Cet article est une adaptation en langue française de How to Create Qt Applications with Metro Style, de Sami Makkonen.

II. Introduction

Metro est le nom donné au nouveau langage de design d'interfaces utilisateur créé par Microsoft. Actuellement, Metro est utilisé comme base pour les Windows Phone 7 et, plus tard, pour l'interface de Windows 8. Pour récupérer l'essence du style Metro, on va créer un style Qt en utilisant plusieurs principes généraux de style et de design.

Comme expliqué dans le guide des principes généraux de design de Microsoft, le style Metro se focalise sur le contenu. Metro est prévu pour être simple et léger, libre d'éléments au look chromé, où le contenu est l'interface. Les principes-clés de design sont la cohérence, la typographie et le mouvement. Pour apporter une structure cohérente à l'interface, tous les items sont séparés en utilisant des marges cohérentes et les items sont généralement alignés à gauche. Metro utilise extensivement le texte et la typographie. Le texte brut est utilisé non seulement pour montrer le contenu, mais aussi comme marqueur indiquant la voie vers plus de contenu. Le type de police est Segoe (en fonction des plateformes) - la famille et la taille de police sont cohérentes en fonction de l'usage (certaines tailles seront réservées aux titres, par exemple). Metro ne supporte pas la rognure de texte ou les affaiblissements. En lieu et place, tout le texte qui ne tient pas sur l'écran est mis sur la droite, par-delà l'écran. Comme mentionné, l'un des éléments-clés de Metro est le mouvement : des animations pour les actions de l'utilisateur (feedback visuel de l'appui sur un bouton ou transition de vue). De même, on veut des transitions cohérentes, qui donnent le même genre de feedback pour les mêmes types d'actions.

Ces quelques principes-clés esquissés, on montrera comment Qt Commercial peut être utilisé pour rendre l'essence du style Metro par le biais de deux applications simples : une vue en grille avec QML et une application de contrôle de pivot en C++.

III. Créer une vue en grille avec QML et un style Metro

Cet exemple de vue en grille montre comment les composants QML peuvent être utilisés pour créer des applications au style Metro, semblables à l'écran de démarrage en tuiles de Windows 8 :

Image non disponible

Cet écran de démarrage consiste en un en-tête et une grille en dessous. La vue en grille est alignée sur la gauche et déborde du côté droit. La grille peut être déplacée sur la gauche ou la droite, les applications peuvent être démarrées en touchant les tuiles. Pour suivre les principes de conception édictés dans l'introduction, on définit quelques paramètres de style pour les composants de l'interface :

main.qml
Sélectionnez
property int topBarSize: 144 
property int barSize: 120 
property int tileMargin: 12 
property int tileHeaderFontSize: 11 
property int tileDateFontSize: 10 
property int appHeaderFontSize: 36 
property string appBackground: "#262626" 
property string tileBackground: "#86bc24" 
property string textColor: "white" 
property string uiFont: "Segoe UI"

Ces propriétés définissent la structure, les couleurs et les polices pour l'interface. Comme on peut le voir, topBarSize, barSize et tileMargin ont une valeur de douze pixels, des incréments d'autant de pixels pour rendre la disposition cohérente. Le type de police est défini à Segoe UI. Une fois le style défini, on s'occupe de la structure de l'interface. On ne va pas passer en revue tout le code, seulement les éléments importants.

main.qml
Sélectionnez
// Barre supérieure
Rectangle { 
    id: topBar 
    anchors.left: leftBar.right 
    anchors.top: parent.top 
    height: topBarSize 
    color: appBackground 
    Text { 
        ... 
        text: qsTr("Qt Commercial Blog") 
        font.family: uiFont; 
        font.pointSize: appHeaderFontSize; 
        color: textColor 
    } 
} 
 
// Barre de gauche
Rectangle { 
    id: leftBar 
    anchors.left: parent.left 
    anchors.top: parent.top 
    width: barSize 
    height: parent.height 
    color: appBackground 
    ... 
} 
 
// Vue en grille
GridView { 
    id: grid 
    anchors.left: leftBar.right 
    anchors.top: topBar.bottom 
    flow: GridView.TopToBottom 
    width: parent.width - leftBar.width 
    height: parent.height - topBar.height - bottomBar.height 
    cellHeight: parseInt(grid.height / 3) 
    cellWidth: parseInt(cellHeight * 1.5) 
    clip: false 
    model: feedModel 
    delegate: RssDelegate {} 
    ... 
} 
 
// Barre du bas
Rectangle { 
    id: bottomBar 
    anchors.top: grid.bottom 
    anchors.left: leftBar.right 
    width: parent.width - leftBar.width 
    height: barSize 
    color: appBackground 
    ... 
} 

La structure est assez simple : elle est constituée d'une barre supérieure, d'une barre à gauche, d'une barre en bas et de la vue en grille centrée, avec les paramètres de style déjà définis. Le composant GridView est standard en QML et affiche ses items en grille, de manière horizontale. Les tailles sont définies avec les propriétés cellHeight et cellWidth. La rognure est désactivée, on peut donc déplacer la grille vers la gauche ou la droite "sur" l'écran. Cette vue supporte aussi le déplacement cinétique avec une courbe de lissage appropriée par défaut. Le contenu de la vue est fourni par le flux RSS Qt Commercial, récupéré par XmlListModel.

C'est tout. L'application QML en grille est stylée à la mode Metro. On peut en vérifier le résultat dans la démonstration en fin d'article.

IV. Créer un contrôle de pivot avec Qt en C++

Dans ce second exemple, on va montrer comment implémenter un contrôle de pivot avec un style Metro à l'aide de Qt en C++. Il s'agit d'une version Metro de l'habituel contrôle d'onglets avec quelques exceptions. On peut définir plusieurs pages pour la même fenêtre ; chaque page consiste en un certain type d'informations ou d'autres contrôles. Les items de l'en-tête du pivot glissent sur l'écran vers la droite et l'item sélectionné est toujours déplacé sur la gauche de l'écran.

Image non disponible

Pour les briques de base de l'application, on a décidé d'utiliser les classes QGraphicsView et QGraphicsTextItem. Comme dans l'exemple précédent, on a défini quelques paramètres pour l'interface.

Style.h
Sélectionnez
const int componentMargin = 24; 
const int bodyTextSize = 24; 
const int headerTextSize = 48; 
const QFont headerFont = QFont("Segoe UI", headerTextSize); 
const QFont bodyFont = QFont("Segoe UI", bodyTextSize); 
const QColor uiTextColor = Qt::white; 
const QString backgroundStyle = "background-color: rgba(26,26,26)"; 
const int animationTime = 400;

componentMargin définit une marge cohérente pour tous les composants de l'interface, tant horizontalement que verticalement. La police sera Segoe UI. Le bout de code ci-après montre la création des items de l'en-tête du pivot.

tabview.cpp
Sélectionnez
... 
const int itemCount = 6; 
... 
QGraphicsTextItem *tmp; 
... 
for(int i = 0; i < itemCount; ++i) { 
    tmp = new QGraphicsTextItem(); 
    text = "loremIpsum"; 
    text = text.append(QString("%1").arg(i+1)); 
    tmp->setPlainText(text); 
    tmp->setFont(headerFont); 
    tmp->adjustSize(); 
    tmp->setDefaultTextColor(uiTextColor); 
    // sous le texte de l'en-tête
    tmp->setPos(xPos,(componentMargin * 2 + bodyTextSize)); 
    // calculer la position de l'item suivant : c'est la somme de la position actuelle, 
	// de la largeur de l'item et de la marge. 
    xPos = xPos + tmp->textWidth() + componentMargin; 
    ... 
    mHeaderItems.append(tmp); 
    scene()->addItem(tmp); 
}

Six éléments de l'en-tête du contrôle sont créés. Pour chaque en-tête, on doit définir du contenu. Dans ce cas, le contenu sera du texte, on utilise donc QGraphicsTextItem pour l'afficher. Puisque QGraphicsTextItem est utilisé dans l'en-tête et les items du contenu, la création de ces items sera semblable à l'instanciation de l'en-tête et de son contenu. Les seules différences seront la police (bodyFont) et la position (déterminée par rapport aux items de l'en-tête).

Pour apporter du mouvement aux interfaces par le biais d'animations, on utilise la classe QPropertyAnimation. Le bout de code suivant montre la création de l'item d'animation.

tabview.cpp
Sélectionnez
... 
QPropertyAnimation *anim; 
anim = new QPropertyAnimation(tmp, "pos"); 
anim->setDuration(animationTime); 
anim->setPropertyName("pos"); 
anim->setEasingCurve(QEasingCurve::OutCirc); 
mGroupAnimHeader->addAnimation(anim); 
mHeaderAnimations.append(anim); 
... 
mHeaderItems.append(tmp); 
scene()->addItem(tmp);

Le style d'interface Metro recommande l'utilisation de deux types de courbes d'assouplissement : logarithmique pour l'entrée à l'écran et exponentielle pour la sortie. Dans ce cas, QEasingCurve::OutCirc est utilisé pour les deux. Ce type de courbe est fort semblable à une courbe logarithmique pour l'animation. Le bout de code suivant montre l'utilisation des animations.

tabview.cpp
Sélectionnez
void TabView::mouseMoveEvent(QMouseEvent *event) 
{ 
    int xDif = event->pos().x() - mMouseXPosition; 
    ... 
    if(xDif < 0) { 
        ... 
        startContentAnimation(ESweepLeft); 
        startHeaderAnimation(ESweepLeft); 
    } else if(xDif > 0) { 
        ... 
        startContentAnimation(ESweepRight); 
        startHeaderAnimation(ESweepRight); 
    } 
    mMouseXPosition = event->pos().x(); 
}

void TabView::startContentAnimation(int direction) 
{ 
    QPropertyAnimation *anim; 
    QGraphicsTextItem *text; 
    // init animation items 
    for(int i = 0; i < itemCount; ++i) { 
        text = mContentItems.at(i); 
        anim = mContentAnimations.at(i); 
        QPointF start = text->pos(); 
        QPointF end = start; 
        if(direction == ESweepLeft) 
            end.setX( end.x() - text->textWidth() - componentMargin); 
        else 
            end.setX( end.x() + text->textWidth() + componentMargin); 
        anim->setStartValue(start); 
        anim->setEndValue(end); 
    } 
    mGroupAnimContent->start(); 
}

L'animation est démarrée pour le pivot et les items du contenu quand l'écran est balayé vers la gauche ou la droite. Ce mouvement est détecté dans la fonction mouseMoveEvent et chaque fois qu'une nouvelle animation débute ou se termine, les points étant calculés à l'aide de sa direction. L'opacité des items est définie à 0.6 en cas de non-mise en évidence. Pour les éléments mis en évidence, l'opacité est de 1.0 avec utilisation d'une courbe d'assouplissement linéaire pendant les animations.

En résumé, on a maintenant un contrôle de pivot construit à l'aide de la classe QGraphicsTextItem avec une transition entre les vues avec la classe QPropertyAnimation.

V. Vidéo et code

Le résultat de ces péripéties est résumé dans cette vidéo. Le code est d'ailleurs disponible.



VI. Remerciements

Un tout grand merci à Alexandre Laurent pour sa relecture attentive, ainsi qu'à djibril et Claude Leloup pour leur relecture orthographique !