How to Create Qt Applications with Metro Style
January 25, 2012 by Sami Makkonen | Comments
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
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.6 Now!
Download the latest release here: www.qt.io/download.
Qt 6.6. is a feature release with focus on improving UX capabilities including responsive UI technology and the Qt Graph module.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.