Créer des applications avec un style Metro avec Qt

| Sami Makkonen | Digia Qt |

Comme déjà montré, les applications Qt Commercial en C++ et Qt Quick sont utilisables sans aucun problème ni modification sur la préversion pour développeurs de Windows 8. Ici, on montrera, avec plus de détails, comment des applications Qt peuvent adopter un style Metro. Les exemples seront des applications Qt normales dont le style aura été adapté pour ressembler à des applications Metro.

Commentez

- Titre : Créer des applications avec un style Metro avec Qt

- Auteurs : Sami Makkonen, Digia Qt

- Parution : 22 février 2012

- Mise à jour : 22 mars 2012

- Licence : Copyright ® 2012 Sami Makkonen. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. L'article original

- II. Introduction

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

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

- V. Vidéo et code

- VI. Remerciements

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 :

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

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

// 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.

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

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

...
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

...
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

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.

Animation en format "youtube" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://www.youtube.com/v/cTEMkDZxpaw

VI - Remerciements

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