How to Create Qt Applications with Metro Style

As the Deploying on Windows 8 Tablets with Qt Commercial blog post demonstrated, the Qt Commercial C++ and Qt Quick applications ran nicely without any problems or modifications on Windows 8 Developer Preview. With this post we are going to show in more detail how Qt applications can be styled to follow the Metro style. The examples are normal Qt applications styled to look similar to Metro application.

Metro is the name of the new user interface design language created by Microsoft. Currently Metro is used as a basis for the Windows Phone 7 and later on will serve as the basis for the Windows 8 UI. To reach the essence of the Metro style, we are going to style the Qt applications by using general Metro UI styling and design principles.

As said in Microsoft's general design principles guide, Metro style focuses strongly on content. Metro is designed to be simple and light, free of chrome elements, where the content is the interface. Key design principles along with previous ones are consistency, typography and motion. To bring consistent structure to UI, all the UI items are separated from each other's by using consistent margins and items are usually aligned evenly to the left. Metro relies heavily on text and typography. Plain text is used not only to show the content but as a marker leading to more content. Font type is from (depending on the platform) Segoe - font family and font sizes are consistent depending on the usage (certain size for headers etc.). Metro doesn’t support text clipping or fading; instead every text unfitting to the screen is flushed to the right, over the screen. As mentioned, one of the key elements of Metro is the motion. By motion they mean animations to user actions, like visual feedback of pushing a button or view transition. Transitions should also be consistent, giving the same kind of feedback for the same kind of actions.

So there we go. We have few key elements of the Metro design outlined. To demonstrate how Qt Commercial can be used for catching the Metro style essence, we created two simple applications: grid view application with QML and pivot control application with Qt C++.

Creating Metro styled grid view with QML

The grid view example demonstrates how the QML components can be used to create Metro styled grid application, similar to the Windows 8 developer preview live tile starting screen.

The live tile starting screen consists of header and grid view below it. Grid view is aligned to the left and it flows over the right side. The grid can be kinetically panned left or right and the application can be started by tapping the tiles. To follow the design principles stated in previous chapter, simple style settings were defined for the UI components.

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"
  • file: main.qml

The style settings define the structure, colors and fonts for the application UI. As you can see the topBarSize, barSize and tileMargin are 12px or 12px increments to make the layout consistent. The font type is set to Segoe UI. As the style is defined, the UI structure needs to be defined. Note that we are not going through all the source code, only the essential lines for the UI.

// Top bar
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
    }
}
// left bar
Rectangle {
    id: leftBar
    anchors.left: parent.left
    anchors.top: parent.top
    width: barSize
    height: parent.height
    color: appBackground
    ...
}
// Grid view
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 {}
    ...
}
// bottom bar
Rectangle {
    id: bottomBar
    anchors.top: grid.bottom
    anchors.left: leftBar.right
    width: parent.width - leftBar.width
    height: barSize
    color: appBackground
    ...
}

 

  • file: main.qml

 

The UI structure is quite simple. It consists of a top bar, left bar, bottom bar and the grid view center styled with defined style settings. The GridView is a standard QML component for displaying items in the grid layout. GridView allows items to be laid out horizontally and item sizes can be defined by using cellHeight- and cellWidth – properties. Clipping is set to false, which allows the grid to be moved left or right “over” the screen. GridView supports also kinetic panning with appropriate easing by default. The content for the grid view is from the Qt RSS feed fetched by using XmlListModel.

That’s it. The QML grid application is styled to follow Metro. Check the result from the demo at the end of this post.

Creating pivot control with Qt C++

With the second example we are going to show how to implement and style Metro pivot control with Qt C++. The pivot control is a Metro version of the normal tab control with some exceptions. It allows multiple pages to be defined for the same window and each page consists of a certain type of information or other controls. Pivot header items flow over the screen to the right and the selected item is always moved to the left of the screen.

For the building blocks of the application we decided to use the QGraphicsView and QGraphicsTextItem classes. Similar to the previous example, simple style settings were defined for the UI.

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;
  • file: Style.h

The componentMargin defines consistent margin for all the UI components horizontally and vertically. The font is set to Segoe UI. The following code snippet demonstrates the creation of pivot header items.

...
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);
    // below header text
    tmp->setPos(xPos,(componentMargin * 2 + bodyTextSize));
    // calculate the position for the next item.
    xPos = xPos + tmp->textWidth() + componentMargin;
    ...
    mHeaderItems.append(tmp);
    scene()->addItem(tmp);
}
  • file: tabview.cpp

As the snippets show, 6 pivot control header items are created. For each header item there needs to be content. In this case the content is text so the QGraphicsTextItem was also used for showing it. Since the QGraphicsTextItem is used in header and content items the creation of content items is similar to the creation of header items. The difference is that the bodyFont is used as a font and position is set to be below header items.

For bringing the motion to the UI via animations, the QPropertyAnimation class was used. The following code snippet demonstrates the creation of the animation item.

...
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);
  • file: TabView.cpp

Metro UI style recommends using two types of ease for the animations; logarithmic for the object entering to the screen and exponential for the object leaving the screen. In this case the Qt's QEasingCurve::OutCirc is used for both. It provides logarithmic-like ease to the animation. The following code snippet demonstrates the usage of animations.

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();
}
  • file: TabView.cpp

The animation is started for the pivot and content items when the screen is swiped left or right. Sweep is detected in the mouseMoveEvent-function and each time a new animation starts and ends, points are calculated depending on the direction of sweep. Pivot items opacity is set to 0.6, if not highlighted. Highlighted item's opacity is set to 1.0 by using linear easing during the animations.

To sum up, we now have a pivot control constructed by using the QGraphicsTextItem - classes and also a nice transition effect between views by using QPropertyAnimation - class.

Check out the results in the following video.

http://www.youtube.com/watch?v=cTEMkDZxpaw


Comments