How to style a QML scroll bar with an image provider

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.

  1. import Qt 4.7
  2. import style 1.0
  3.  
  4. Item {
  5.     width: 300
  6.     height: 300
  7.  
  8.     ListView {
  9.         id: myList
  10.         anchors.fill: parent
  11.         model: 100
  12.         delegate: Text {
  13.             text: "item " + index
  14.         }
  15.     }
  16.     ScrollBar {
  17.         target: myList
  18.     }
  19. }

ScrollBar.qml

This ScrollBar is originally by Gregory Schlomoff [bitbucket.org]. 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.

  1. /*
  2.         ScrollBar component for QML Flickable
  3.  
  4.         Copyright (c) 2010 Gregory Schlomoff - gregory.schlomoff at gmail.com
  5.  
  6.         This code is released under the MIT license
  7.  
  8.         Permission is hereby granted, free of charge, to any person obtaining a copy
  9.         of this software and associated documentation files (the "Software"), to deal
  10.         in the Software without restriction, including without limitation the rights
  11.         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12.         copies of the Software, and to permit persons to whom the Software is
  13.         furnished to do so, subject to the following conditions:
  14.  
  15.         The above copyright notice and this permission notice shall be included in
  16.         all copies or substantial portions of the Software.
  17.  
  18.         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19.         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20.         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21.         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22.         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23.         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24.         THE SOFTWARE.
  25.  
  26. With modifications:
  27.         Copyright (c) 2010 Bradley Smith <bradley at baysmith.com>
  28.  
  29. Original: http://bitbucket.org/gregschlom/qmlscrollbar/src/tip/ScrollBar.qml
  30. */
  31.  
  32. /*
  33.         Usage:
  34.  
  35.         Flickable {
  36.           id: myFlickable
  37.           ...
  38.         }
  39.         ScrollBar {
  40.           target: myFlickable
  41.         }
  42. */
  43.  
  44. import Qt 4.7
  45. import style 1.0
  46.  
  47. Item {
  48.     property variant target
  49.  
  50.     width: upArrow.width
  51.     anchors.top: target.top
  52.     anchors.bottom: target.bottom
  53.     anchors.margins: 1
  54.     anchors.rightMargin: 2
  55.     anchors.bottomMargin: 2
  56.     anchors.right: target.right
  57.     visible: (track.height == slider.height) ? false : true
  58.  
  59.     Image {
  60.         id: groove
  61.         width: parent.width
  62.         anchors.top: parent.top
  63.         anchors.bottom: parent.bottom
  64.         source: "image://style/scrollBar/addPage"
  65.     }
  66.  
  67.     Item {
  68.         anchors.fill: parent
  69.  
  70.         Image {
  71.             id: upArrow
  72.             source: "image://style/scrollBar/subLine"
  73.             z: 1
  74.             anchors.top: parent.top
  75.             anchors.horizontalCenter: parent.horizontalCenter
  76.             MouseArea {
  77.                 anchors.fill: parent
  78.                 onPressed: {
  79.                     timer.scrollAmount = -10
  80.                     timer.running = true;
  81.                 }
  82.                 onReleased: {
  83.                     timer.running = false;
  84.                 }
  85.             }
  86.         }
  87.  
  88.         Timer {
  89.             property int scrollAmount
  90.  
  91.             id: timer
  92.             repeat: true
  93.             interval: 20
  94.             onTriggered: {
  95.                 target.contentY = Math.max(0, Math.min(target.contentY + scrollAmount,
  96.                                                        target.contentHeight - target.height));
  97.             }
  98.         }
  99.  
  100.         Item {
  101.             id: track
  102.             anchors.top: upArrow.bottom
  103.             anchors.topMargin: 1
  104.             anchors.bottom: downArrow.top
  105.             width: parent.width
  106.  
  107.             MouseArea {
  108.                 anchors.fill: parent
  109.                 onPressed: {
  110.                     timer.scrollAmount = target.height * (mouseY < slider.y ? -1 : 1)
  111.                     timer.running = true;
  112.                 }
  113.                 onReleased: {
  114.                     timer.running = false;
  115.                 }
  116.             }
  117.  
  118.             BorderImage {
  119.                 id:slider
  120.  
  121.                 source: "image://style/scrollBar/slider"
  122.                 border.left: 3
  123.                 border.top: 3
  124.                 border.right: 3
  125.                 border.bottom: 3
  126.                 horizontalTileMode: BorderImage.Round
  127.                 verticalTileMode: BorderImage.Round
  128.                 width: parent.width
  129.                 Image {
  130.                     anchors.centerIn: parent
  131.                     source: "image://style/scrollBar/sliderDecor"
  132.                 }
  133.  
  134.                 height: Math.min(target.height / target.contentHeight * track.height, track.height)
  135.                 y: target.visibleArea.yPosition * track.height
  136.  
  137.                 MouseArea {
  138.                     anchors.fill: parent
  139.                     drag.target: parent
  140.                     drag.axis: Drag.YAxis
  141.                     drag.minimumY: 0
  142.                     drag.maximumY: track.height - height
  143.  
  144.                     onPositionChanged: {
  145.                         if (pressedButtons == Qt.LeftButton) {
  146.                             target.contentY = slider.y * target.contentHeight / track.height
  147.                         }
  148.                     }
  149.                 }
  150.             }
  151.         }
  152.         Image {
  153.             id: downArrow
  154.             source: "image://style/scrollBar/addLine"
  155.             width: parent.width
  156.             z: 1
  157.             anchors.bottom: parent.bottom
  158.             anchors.horizontalCenter: parent.horizontalCenter
  159.             MouseArea {
  160.                 anchors.fill: parent
  161.                 onPressed: {
  162.                     timer.scrollAmount = 10
  163.                     timer.running = true;
  164.                 }
  165.                 onReleased: {
  166.                     timer.running = false;
  167.                 }
  168.             }
  169.         }
  170.     }
  171. }

qmldir

This defines the plugin name and resources for it.

  1. plugin style
  2. ScrollBar 1.0 ScrollBar.qml

StylePlugin.h

  1. #ifndef STYLE_PLUGIN_H
  2. #define STYLE_PLUGIN_H
  3.  
  4. #include <QtDeclarative/QDeclarativeExtensionPlugin>
  5.  
  6. class StylePlugin : public QDeclarativeExtensionPlugin
  7. {
  8.     Q_OBJECT
  9.  
  10. public:
  11.     void registerTypes(const char *uri);
  12.     void initializeEngine(QDeclarativeEngine *engine, const char *uri);
  13. };
  14.  
  15. #endif // STYLE_PLUGIN_H

StylePlugin.cpp

  1. #include "StylePlugin.h"
  2. #include "StyleImageProvider.h"
  3.  
  4. #include <QtDeclarative/qdeclarative.h>
  5. #include <QtDeclarative/QDeclarativeEngine>
  6.  
  7. void StylePlugin::registerTypes(const char *uri)
  8. {
  9. }
  10.  
  11. void StylePlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri)
  12. {
  13.     Q_UNUSED(uri);
  14.     engine->addImageProvider("style", new StyleImageProvider);
  15. }
  16.  
  17. 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.

  1. #ifndef STYLEIMAGEPROVIDER_H
  2. #define STYLEIMAGEPROVIDER_H
  3.  
  4. #include <QtDeclarative/QDeclarativeEngine>
  5. #include <QtDeclarative/QDeclarativeImageProvider>
  6.  
  7. class StyleImageProvider : public QDeclarativeImageProvider
  8. {
  9. public:
  10.     StyleImageProvider();
  11.     QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
  12.  
  13. };
  14.  
  15. #endif // STYLEIMAGEPROVIDER_H

StyleImageProvider.cpp

  1. #include "StyleImageProvider.h"
  2.  
  3. #include <QtGui/QApplication>
  4. #include <QtGui/QPainter>
  5. #include <QtGui/QStyleOptionSlider>
  6.  
  7. namespace
  8. {
  9.     QMap<QString, QStyle::ComplexControl> complexControlMap;
  10.     QMap<QString, QStyle::SubControl> subControlMap;
  11. }
  12.  
  13. StyleImageProvider::StyleImageProvider()
  14.     : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap)
  15. {
  16.     complexControlMap["scrollBar"] = QStyle::CC_ScrollBar;
  17.  
  18.     subControlMap["none"] = QStyle::SC_None;
  19.     subControlMap["addLine"] = QStyle::SC_ScrollBarAddLine;
  20.     subControlMap["subLine"] = QStyle::SC_ScrollBarSubLine;
  21.     subControlMap["addPage"] = QStyle::SC_ScrollBarAddPage;
  22.     subControlMap["subPage"] = QStyle::SC_ScrollBarSubPage;
  23.     subControlMap["first"] = QStyle::SC_ScrollBarFirst;
  24.     subControlMap["last"] = QStyle::SC_ScrollBarLast;
  25.     subControlMap["slider"] = QStyle::SC_ScrollBarSlider;
  26.     subControlMap["sliderDecor"] = QStyle::SC_ScrollBarSlider;
  27.     subControlMap["groove"] = QStyle::SC_ScrollBarGroove;
  28. }
  29.  
  30. QPixmap StyleImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
  31. {
  32.     // Parse the request into complex and sub control parts
  33.     QStyle::ComplexControl complexControl = complexControlMap[id.section('/', 0, 0)];
  34.     QString subControlName = id.section('/', 1, 1);
  35.     QStyle::SubControl subControl = subControlMap[subControlName];
  36.     QString state = id.section('/', 2, 2);
  37.  
  38.     // Manually tailored parameters for Windows XP
  39.     int width = 16;
  40.     int height = 50;
  41.  
  42.     // Setup the style options
  43.     QStyleOptionComplex* option = 0;
  44.     if (complexControl == QStyle::CC_ScrollBar)
  45.     {
  46.         QStyleOptionSlider *sliderOption = new QStyleOptionSlider;
  47.         option = sliderOption;
  48.         if (subControlName == "sliderDecor")
  49.             // Manually tailored parameter for Windows XP
  50.             height = 72;
  51.         sliderOption->orientation = Qt::Vertical;
  52.         sliderOption->state = QStyle::State_Enabled;
  53.         if (state == "hover")
  54.             sliderOption->state |= QStyle::State_MouseOver;
  55.         else if (state == "pressed")
  56.             sliderOption->state |= QStyle::State_Sunken;
  57.         else if (state == "disabled")
  58.             sliderOption->state ^= QStyle::State_Enabled;
  59.         sliderOption->minimum = 1;
  60.         sliderOption->maximum = 100;
  61.         sliderOption->sliderPosition = 50;
  62.         sliderOption->sliderValue = 50;
  63.         sliderOption->singleStep = 1;
  64.         sliderOption->pageStep = 100;
  65.     }
  66.     if (!option)
  67.         // Unsupported control
  68.         return QPixmap();
  69.  
  70.     option->subControls = subControl;
  71.     option->activeSubControls = subControl;
  72.  
  73.     // Create a pixmap of the correct size
  74.     QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width,
  75.                    requestedSize.height() > 0 ? requestedSize.height() : height);
  76.     option->rect = QRect(QPoint(), pixmap.size());
  77.     delete option;
  78.  
  79.     // Paint the control
  80.     QPainter painter(&pixmap);
  81.     QRect subControlRect = qApp->style()->subControlRect(complexControl, option, subControl);
  82.     qApp->style()->drawComplexControl(complexControl, option, &painter);
  83.     painter.end();
  84.  
  85.     // Copy just the sub control from the painted image
  86.     QPixmap sliderPixmap;
  87.     if (subControlName == "sliderDecor")
  88.         sliderPixmap = pixmap.copy(subControlRect.adjusted(3, 3, -3, -3));
  89.     else
  90.         sliderPixmap = pixmap.copy(subControlRect);
  91.  
  92.     // Return the size of the pixmap created
  93.     if (size)
  94.         *size = sliderPixmap.size();
  95.  
  96.     return sliderPixmap;
  97. }

style.pro

  1. TEMPLATE = lib
  2. TARGET = style
  3. QT += declarative
  4. CONFIG += qt plugin
  5. DESTDIR = style
  6.  
  7. TARGET = $$qtLibraryTarget($$TARGET)
  8.  
  9. SOURCES += \
  10.     StylePlugin.cpp \
  11.     StyleImageProvider.cpp
  12.  
  13. HEADERS += \
  14.     StylePlugin.h \
  15.     StyleImageProvider.h
  16.  
  17. OTHER_FILES = qmldir test.qml ScrollBar.qml main.qml
  18.  
  19. copy_qmldir.target = $$OUT_PWD/$$DESTDIR/qmldir
  20. copy_qmldir.depends = $$PWD/qmldir
  21. copy_qmldir.commands = $(COPY_FILE) \"$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)\"
  22. copy_ScrollBar_qml.target = $$OUT_PWD/$$DESTDIR/ScrollBar.qml
  23. copy_ScrollBar_qml.depends = $$PWD/ScrollBar.qml
  24. copy_ScrollBar_qml.commands = $(COPY_FILE) \"$$replace(copy_ScrollBar_qml.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_ScrollBar_qml.target, /, $$QMAKE_DIR_SEP)\"
  25. QMAKE_EXTRA_TARGETS += copy_qmldir copy_ScrollBar_qml
  26. PRE_TARGETDEPS += $$copy_qmldir.target $$copy_ScrollBar_qml.target

Categories:

  • HowTo
  • snippets
  •