How to style a QML scroll bar with an image provider

From Qt Wiki
Revision as of 10:28, 24 February 2015 by Maintenance script (talk | contribs)
Jump to navigation Jump to search




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.

<br />import Qt 4.7<br />import style 1.0

Item {<br /> width: 300<br /> height: 300

ListView {<br /> id: myList<br /> anchors.fill: parent<br /> model: 100<br /> delegate: Text {<br /> text: &quot;item &quot; + index<br /> }<br /> }<br /> ScrollBar {<br /> target: myList<br /> }<br />}<br />

ScrollBar.qml

This "ScrollBar is originally by Gregory Schlomoff&quot;:http://bitbucket.org/gregschlom/qmlscrollbar/src/tip/ScrollBar.qml. 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&quot;, 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.

<br />/*<br /> 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<br /> of this software and associated documentation files (the &quot;Software&amp;quot;), to deal<br /> in the Software without restriction, including without limitation the rights<br /> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br /> copies of the Software, and to permit persons to whom the Software is<br /> furnished to do so, subject to the following conditions:

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

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

With modifications:<br /> Copyright © 2010 Bradley Smith &lt;bradley at baysmith.com&amp;gt;

Original: http://bitbucket.org/gregschlom/qmlscrollbar/src/tip/ScrollBar.qml<br />'''/
<br />/'''<br /> Usage:

Flickable {<br /> id: myFlickable<br /> …<br /> }<br /> ScrollBar {<br /> target: myFlickable<br /> }<br />'''/
<br />import Qt 4.7<br />import style 1.0
<br />Item {<br /> property variant target
<br /> width: upArrow.width<br /> anchors.top: target.top<br /> anchors.bottom: target.bottom<br /> anchors.margins: 1<br /> anchors.rightMargin: 2<br /> anchors.bottomMargin: 2<br /> anchors.right: target.right<br /> visible: (track.height == slider.height) ? false : true
<br /> Image {<br /> id: groove<br /> width: parent.width<br /> anchors.top: parent.top<br /> anchors.bottom: parent.bottom<br /> source: &quot;image://style/scrollBar/addPage&amp;quot;<br /> }
<br /> Item {<br /> anchors.fill: parent
<br /> Image {<br /> id: upArrow<br /> source: &quot;image://style/scrollBar/subLine&amp;quot;<br /> z: 1<br /> anchors.top: parent.top<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> MouseArea {<br /> anchors.fill: parent<br /> onPressed: {<br /> timer.scrollAmount = <s>10<br /> timer.running = true;<br /> }<br /> onReleased: {<br /> timer.running = false;<br /> }<br /> }<br /> }
<br /> Timer {<br /> property int scrollAmount
<br /> id: timer<br /> repeat: true<br /> interval: 20<br /> onTriggered: {<br /> target.contentY = Math.max(0, Math.min(target.contentY + scrollAmount,<br /> target.contentHeight</s> target.height));<br /> }<br /> }
<br /> Item {<br /> id: track<br /> anchors.top: upArrow.bottom<br /> anchors.topMargin: 1<br /> anchors.bottom: downArrow.top<br /> width: parent.width
<br /> MouseArea {<br /> anchors.fill: parent<br /> onPressed: {<br /> timer.scrollAmount = target.height''' (mouseY &lt; slider.y ? <s>1 : 1)<br /> timer.running = true;<br /> }<br /> onReleased: {<br /> timer.running = false;<br /> }<br /> }
<br /> BorderImage {<br /> id:slider
<br /> source: &quot;image://style/scrollBar/slider&amp;quot;<br /> border.left: 3<br /> border.top: 3<br /> border.right: 3<br /> border.bottom: 3<br /> horizontalTileMode: BorderImage.Round<br /> verticalTileMode: BorderImage.Round<br /> width: parent.width<br /> Image {<br /> anchors.centerIn: parent<br /> source: &quot;image://style/scrollBar/sliderDecor&amp;quot;<br /> }
<br /> height: Math.min(target.height / target.contentHeight * track.height, track.height)<br /> y: target.visibleArea.yPosition * track.height
<br /> MouseArea {<br /> anchors.fill: parent<br /> drag.target: parent<br /> drag.axis: Drag.YAxis<br /> drag.minimumY: 0<br /> drag.maximumY: track.height</s> height

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

qmldir

This defines the plugin name and resources for it.

<br />plugin style<br />ScrollBar 1.0 ScrollBar.qml<br />

StylePlugin.h

<br />#ifndef STYLE_PLUGIN_H<br />#define STYLE_PLUGIN_H

#include &lt;QtDeclarative/QDeclarativeExtensionPlugin&amp;gt;

class StylePlugin : public QDeclarativeExtensionPlugin<br />{<br /> Q_OBJECT

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

#endif // STYLE_PLUGIN_H

StylePlugin.cpp

<br />#include &quot;StylePlugin.h&amp;quot;<br />#include &quot;StyleImageProvider.h&amp;quot;

#include &lt;QtDeclarative/qdeclarative.h&amp;gt;<br />#include &lt;QtDeclarative/QDeclarativeEngine&amp;gt;

void StylePlugin::registerTypes(const char *uri)<br />{<br />}

void StylePlugin::initializeEngine(QDeclarativeEngine *engine, const char *uri)<br />{<br /> Q_UNUSED(uri);<br /> engine-&gt;addImageProvider(&quot;style&amp;quot;, new StyleImageProvider);<br />}

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.

<br />#ifndef STYLEIMAGEPROVIDER_H<br />#define STYLEIMAGEPROVIDER_H

#include &lt;QtDeclarative/QDeclarativeEngine&amp;gt;<br />#include &lt;QtDeclarative/QDeclarativeImageProvider&amp;gt;

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

};

#endif // STYLEIMAGEPROVIDER_H<br />

StyleImageProvider.cpp

<br />#include &quot;StyleImageProvider.h&amp;quot;

#include &lt;QtGui/QApplication&amp;gt;<br />#include &lt;QtGui/QPainter&amp;gt;<br />#include &lt;QtGui/QStyleOptionSlider&amp;gt;

namespace<br />{<br /> QMap&amp;lt;QString, QStyle::ComplexControl&amp;gt; complexControlMap;<br /> QMap&amp;lt;QString, QStyle::SubControl&amp;gt; subControlMap;<br />}

StyleImageProvider::StyleImageProvider()<br /> : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap)<br />{<br /> complexControlMap[&quot;scrollBar&amp;quot;] = QStyle::CC_ScrollBar;

subControlMap[&quot;none&amp;quot;] = QStyle::SC_None;<br /> subControlMap[&quot;addLine&amp;quot;] = QStyle::SC_ScrollBarAddLine;<br /> subControlMap[&quot;subLine&amp;quot;] = QStyle::SC_ScrollBarSubLine;<br /> subControlMap[&quot;addPage&amp;quot;] = QStyle::SC_ScrollBarAddPage;<br /> subControlMap[&quot;subPage&amp;quot;] = QStyle::SC_ScrollBarSubPage;<br /> subControlMap[&quot;first&amp;quot;] = QStyle::SC_ScrollBarFirst;<br /> subControlMap[&quot;last&amp;quot;] = QStyle::SC_ScrollBarLast;<br /> subControlMap[&quot;slider&amp;quot;] = QStyle::SC_ScrollBarSlider;<br /> subControlMap[&quot;sliderDecor&amp;quot;] = QStyle::SC_ScrollBarSlider;<br /> subControlMap[&quot;groove&amp;quot;] = QStyle::SC_ScrollBarGroove;<br />}

QPixmap StyleImageProvider::requestPixmap(const QString &amp;id, QSize '''size, const QSize &amp;requestedSize)<br />{<br /> // Parse the request into complex and sub control parts<br /> QStyle::ComplexControl complexControl = complexControlMap[id.section('/', 0, 0)];<br /> QString subControlName = id.section('/', 1, 1);<br /> QStyle::SubControl subControl = subControlMap[subControlName];<br /> QString state = id.section('/', 2, 2);
<br /> // Manually tailored parameters for Windows XP<br /> int width = 16;<br /> int height = 50;
<br /> // Setup the style options<br /> QStyleOptionComplex''' option = 0;<br /> if (complexControl  QStyle::CC_ScrollBar)
    &amp;#123;
        QStyleOptionSlider *sliderOption = new QStyleOptionSlider;
        option = sliderOption;
        if (subControlName  &quot;sliderDecor&amp;quot;)<br /> // Manually tailored parameter for Windows XP<br /> height = 72;<br /> sliderOption-&gt;orientation = Qt::Vertical;<br /> sliderOption-&gt;state = QStyle::State_Enabled;<br /> if (state  &amp;quot;hover&amp;quot;)
            sliderOption-&amp;gt;state |= QStyle::State_MouseOver;
        else if (state  &quot;pressed&amp;quot;)<br /> sliderOption-&gt;state |= QStyle::State_Sunken;<br /> else if (state == &quot;disabled&amp;quot;)<br /> sliderOption-&gt;state ^= QStyle::State_Enabled;<br /> sliderOption-&gt;minimum = 1;<br /> sliderOption-&gt;maximum = 100;<br /> sliderOption-&gt;sliderPosition = 50;<br /> sliderOption-&gt;sliderValue = 50;<br /> sliderOption-&gt;singleStep = 1;<br /> sliderOption-&gt;pageStep = 100;<br /> }<br /> if (!option)<br /> // Unsupported control<br /> return QPixmap();

option-&gt;subControls = subControl;<br /> option-&gt;activeSubControls = subControl;

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

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

// Copy just the sub control from the painted image<br /> QPixmap sliderPixmap;<br /> if (subControlName == &quot;sliderDecor&amp;quot;)<br /> sliderPixmap = pixmap.copy(subControlRect.adjusted(3, 3, –3, –3));<br /> else<br /> sliderPixmap = pixmap.copy(subControlRect);

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

return sliderPixmap;<br />}<br />

style.pro

<br />TEMPLATE = lib<br />TARGET = style<br />QT ''= declarative<br />CONFIG''= qt plugin<br />DESTDIR = style

TARGET = $$qtLibraryTarget($$TARGET)

SOURCES ''=  StylePlugin.cpp  StyleImageProvider.cpp
<br />HEADERS''=  StylePlugin.h  StyleImageProvider.h

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

copy_qmldir.target = $$OUT_PWD/$$DESTDIR/qmldir<br />copy_qmldir.depends = $$PWD/qmldir<br />copy_qmldir.commands = $(COPY_FILE) quot;$$replace(copy_qmldir.depends, /, $$QMAKE_DIR_SEP)quot; quot;$$replace(copy_qmldir.target, /, $$QMAKE_DIR_SEP)quot;<br />copy_ScrollBar_qml.target = $$OUT_PWD/$$DESTDIR/ScrollBar.qml<br />copy_ScrollBar_qml.depends = $$PWD/ScrollBar.qml<br />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;<br />QMAKE_EXTRA_TARGETS ''= copy_qmldir copy_ScrollBar_qml<br />PRE_TARGETDEPS''= $$copy_qmldir.target $$copy_ScrollBar_qml.target<br />