How to style a QML scroll bar with an image provider

From Qt Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine.
Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean.

En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh

This is not a recommended way to style QML elements, but it does provide an example of creating a QML plugin and creating an image provider. In this example, some parameters have been tailored for Windows XP.

main.qml

This is a simple UI to have something which needs a scroll bar. The ScrollBar element is in the ScrollBar.qml below. It is connected to a ListView element by setting the target property to the id of the list view. The list view is filled with 100 items to give it something to scroll.

import Qt 4.7
import style 1.0

Item {
 width: 300
 height: 300

ListView {
 id: myList
 anchors.fill: parent
 model: 100
 delegate: Text {
 text: "item " + index
 }
 }
 ScrollBar {
 target: myList
 }
}

ScrollBar.qml

This ScrollBar is originally by Gregory Schlomoff. It was modified from the original by changing the source property for Image and BorderImage elements from a image file reference to an image provider reference of the form "image://style/control/subControl", where control and subControl are replaced with names of those QStyle parts. What values those can be are defined in the StyleImageProvider.cpp below. In this example, the only valid values are scrollBar and the subControls of a scroll bar.

/*
 ScrollBar component for QML Flickable

Copyright © 2010 Gregory Schlomoff - gregory.schlomoff at gmail.com

This code is released under the MIT license

Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.

With modifications:
 Copyright © 2010 Bradley Smith <bradley at baysmith.com>

Original: http://bitbucket.org/gregschlom/qmlscrollbar/src/tip/ScrollBar.qml
*/

/'''
 Usage:

Flickable {
 id: myFlickable
 
 }
 ScrollBar {
 target: myFlickable
 }
*/

import Qt 4.7
import style 1.0

Item {
 property variant target

 width: upArrow.width
 anchors.top: target.top
 anchors.bottom: target.bottom
 anchors.margins: 1
 anchors.rightMargin: 2
 anchors.bottomMargin: 2
 anchors.right: target.right
 visible: (track.height == slider.height) ? false : true

 Image {
 id: groove
 width: parent.width
 anchors.top: parent.top
 anchors.bottom: parent.bottom
 source: "image://style/scrollBar/addPage"
 }

 Item {
 anchors.fill: parent

 Image {
 id: upArrow
 source: "image://style/scrollBar/subLine"
 z: 1
 anchors.top: parent.top
 anchors.horizontalCenter: parent.horizontalCenter
 MouseArea {
 anchors.fill: parent
 onPressed: {
 timer.scrollAmount = -10
 timer.running = true;
 }
 onReleased: {
 timer.running = false;
 }
 }
 }

 Timer {
 property int scrollAmount

 id: timer
 repeat: true
 interval: 20
 onTriggered: {
 target.contentY = Math.max(0, Math.min(target.contentY + scrollAmount,
 target.contentHeight- target.height));
 }
 }

 Item {
 id: track
 anchors.top: upArrow.bottom
 anchors.topMargin: 1
 anchors.bottom: downArrow.top
 width: parent.width

 MouseArea {
 anchors.fill: parent
 onPressed: {
 timer.scrollAmount = target.height''' (mouseY < slider.y ? -1 : 1)
 timer.running = true;
 }
 onReleased: {
 timer.running = false;
 }
 }

 BorderImage {
 id:slider

 source: "image://style/scrollBar/slider"
 border.left: 3
 border.top: 3
 border.right: 3
 border.bottom: 3
 horizontalTileMode: BorderImage.Round
 verticalTileMode: BorderImage.Round
 width: parent.width
 Image {
 anchors.centerIn: parent
 source: "image://style/scrollBar/sliderDecor"
 }

 height: Math.min(target.height / target.contentHeight * track.height, track.height)
 y: target.visibleArea.yPosition * track.height

 MouseArea {
 anchors.fill: parent
 drag.target: parent
 drag.axis: Drag.YAxis
 drag.minimumY: 0
 drag.maximumY: track.height- height

onPositionChanged: {
 if (pressedButtons == Qt.LeftButton) {
 target.contentY = slider.y * target.contentHeight / track.height
 }
 }
 }
 }
 }
 Image {
 id: downArrow
 source: "image://style/scrollBar/addLine"
 width: parent.width
 z: 1
 anchors.bottom: parent.bottom
 anchors.horizontalCenter: parent.horizontalCenter
 MouseArea {
 anchors.fill: parent
 onPressed: {
 timer.scrollAmount = 10
 timer.running = true;
 }
 onReleased: {
 timer.running = false;
 }
 }
 }
 }
}

qmldir

This defines the plugin name and resources for it.

plugin style
ScrollBar 1.0 ScrollBar.qml

StylePlugin.h

#ifndef STYLE_PLUGIN_H
#define STYLE_PLUGIN_H

#include <QDeclarativeExtensionPlugin>

class StylePlugin : public QDeclarativeExtensionPlugin
{
 Q_OBJECT

public:
 void registerTypes(const char *uri);
 void initializeEngine(QDeclarativeEngine *engine, const char *uri);
};

#endif // STYLE_PLUGIN_H

StylePlugin.cpp

#include "StylePlugin.h"
#include "StyleImageProvider.h"

#include <qdeclarative.h>
#include <QDeclarativeEngine>

void StylePlugin::registerTypes(const char *uri)
{
}

void StylePlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri)
{
 Q_UNUSED(uri);
 engine->addImageProvider("style", new StyleImageProvider);
}

Q_EXPORT_PLUGIN2(style, StylePlugin)

StyleImageProvider.h

The StyleImageProvider class parses the requested pixmap's id for the control and subControl (and an optional state, hover, pressed, etc. which is not used in this example). A QStyleOptionComplex is created and the QStyle from the application is used to paint into a pixmap. The pixmap is cropped to an appropriate size and returned.

#ifndef STYLEIMAGEPROVIDER_H
#define STYLEIMAGEPROVIDER_H

#include <QDeclarativeEngine>
#include <QDeclarativeImageProvider>

class StyleImageProvider : public QDeclarativeImageProvider
{
public:
 StyleImageProvider();
 QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);

};

#endif // STYLEIMAGEPROVIDER_H

StyleImageProvider.cpp

#include "StyleImageProvider.h"

#include <QApplication>
#include <QPainter>
#include <QStyleOptionSlider>

namespace
{
 QMap<QString, QStyle::ComplexControl> complexControlMap;
 QMap<QString, QStyle::SubControl> subControlMap;
}

StyleImageProvider::StyleImageProvider()
 : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap)
{
 complexControlMap["scrollBar"] = QStyle::CC_ScrollBar;

subControlMap["none"] = QStyle::SC_None;
 subControlMap["addLine"] = QStyle::SC_ScrollBarAddLine;
 subControlMap["subLine"] = QStyle::SC_ScrollBarSubLine;
 subControlMap["addPage"] = QStyle::SC_ScrollBarAddPage;
 subControlMap["subPage"] = QStyle::SC_ScrollBarSubPage;
 subControlMap["first"] = QStyle::SC_ScrollBarFirst;
 subControlMap["last"] = QStyle::SC_ScrollBarLast;
 subControlMap["slider"] = QStyle::SC_ScrollBarSlider;
 subControlMap["sliderDecor"] = QStyle::SC_ScrollBarSlider;
 subControlMap["groove"] = QStyle::SC_ScrollBarGroove;
}

QPixmap StyleImageProvider::requestPixmap(const QString &id, QSize '''size, const QSize &requestedSize)
{
 // Parse the request into complex and sub control parts
 QStyle::ComplexControl complexControl = complexControlMap[id.section('/', 0, 0)];
 QString subControlName = id.section('/', 1, 1);
 QStyle::SubControl subControl = subControlMap[subControlName];
 QString state = id.section('/', 2, 2);

 // Manually tailored parameters for Windows XP
 int width = 16;
 int height = 50;

 // Setup the style options
 QStyleOptionComplex''' option = 0;
 if (complexControl  QStyle::CC_ScrollBar)
    {
        QStyleOptionSlider *sliderOption = new QStyleOptionSlider;
        option = sliderOption;
        if (subControlName  "sliderDecor")
 // Manually tailored parameter for Windows XP
 height = 72;
 sliderOption->orientation = Qt::Vertical;
 sliderOption->state = QStyle::State_Enabled;
 if (state  "hover")
            sliderOption->state |= QStyle::State_MouseOver;
        else if (state  "pressed")
 sliderOption->state |= QStyle::State_Sunken;
 else if (state == "disabled")
 sliderOption->state ^= QStyle::State_Enabled;
 sliderOption->minimum = 1;
 sliderOption->maximum = 100;
 sliderOption->sliderPosition = 50;
 sliderOption->sliderValue = 50;
 sliderOption->singleStep = 1;
 sliderOption->pageStep = 100;
 }
 if (!option)
 // Unsupported control
 return QPixmap();

option->subControls = subControl;
 option->activeSubControls = subControl;

// Create a pixmap of the correct size
 QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width,
 requestedSize.height() > 0 ? requestedSize.height() : height);
 option->rect = QRect(QPoint(), pixmap.size());
 delete option;

// Paint the control
 QPainter painter(&pixmap);
 QRect subControlRect = qApp->style()->subControlRect(complexControl, option, subControl);
 qApp->style()->drawComplexControl(complexControl, option, &painter);
 painter.end();

// Copy just the sub control from the painted image
 QPixmap sliderPixmap;
 if (subControlName == "sliderDecor")
 sliderPixmap = pixmap.copy(subControlRect.adjusted(3, 3, 3, 3));
 else
 sliderPixmap = pixmap.copy(subControlRect);

// Return the size of the pixmap created
 if (size)
 *size = sliderPixmap.size();

return sliderPixmap;
}

style.pro

TEMPLATE = lib
TARGET = style
QT ''= declarative
CONFIG''= qt plugin
DESTDIR = style

TARGET = $$qtLibraryTarget($$TARGET)

SOURCES ''=  StylePlugin.cpp  StyleImageProvider.cpp

HEADERS''=  StylePlugin.h  StyleImageProvider.h

OTHER_FILES = qmldir test.qml ScrollBar.qml main.qml

copy_qmldir.target = $$OUT_PWD/$$DESTDIR/qmldir
copy_qmldir.depends = $$PWD/qmldir
copy_qmldir.commands = $(COPY_FILE) quot;$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)quot; quot;$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)quot;
copy_ScrollBar_qml.target = $$OUT_PWD/$$DESTDIR/ScrollBar.qml
copy_ScrollBar_qml.depends = $$PWD/ScrollBar.qml
copy_ScrollBar_qml.commands = $(COPY_FILE) quot;$$replace(copy_ScrollBar_qml.depends, /, $$QMAKE_DIR_SEP)quot; quot;$$replace(copy_ScrollBar_qml.target, /, $$QMAKE_DIR_SEP)quot;
QMAKE_EXTRA_TARGETS ''= copy_qmldir copy_ScrollBar_qml
PRE_TARGETDEPS''= $$copy_qmldir.target $$copy_ScrollBar_qml.target