How to style a QML scroll bar with an image provider: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
[[Category:HowTo]]<br />[[Category:snippets]]<br />[[Category:Developing_with_Qt::Qt Quick]]<br />[[Category:Developing_with_Qt::Qt Quick::QML]]
[[Category:HowTo]]
[[Category:snippets]]
[[Category:Developing_with_Qt::Qt Quick]]
[[Category:Developing_with_Qt::Qt Quick::QML]]


= How to style a QML scroll bar with an image provider =
= 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<br />creating a QML plugin and creating an image provider. In this example, some parameters<br />have been tailored for Windows XP.
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 ===
=== main.qml ===
Line 9: Line 14:
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.
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.


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


Item {<br /> width: 300<br /> height: 300
Item {
width: 300
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 /></code>
ListView {
id: myList
anchors.fill: parent
model: 100
delegate: Text {
text: "item " + index
}
}
ScrollBar {
target: myList
}
}
</code>


=== ScrollBar.qml ===
=== ScrollBar.qml ===


This &quot;ScrollBar is originally by Gregory Schlomoff&amp;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 &quot;image://style/control/subControl&amp;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.
This "ScrollBar is originally by Gregory Schlomoff":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", 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.


<code><br />/*<br /> ScrollBar component for QML Flickable
<code>
/*
ScrollBar component for QML Flickable


Copyright © 2010 Gregory Schlomoff - gregory.schlomoff at gmail.com
Copyright © 2010 Gregory Schlomoff - gregory.schlomoff at gmail.com
Line 25: Line 48:
This code is released under the MIT license
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:
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.


The above copyright notice and this permission notice shall be included in<br /> all copies or substantial portions of the Software.
With modifications:
Copyright © 2010 Bradley Smith <bradley at baysmith.com>


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.
Original: http://bitbucket.org/gregschlom/qmlscrollbar/src/tip/ScrollBar.qml
*/


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


Original: http://bitbucket.org/gregschlom/qmlscrollbar/src/tip/ScrollBar.qml<br />'''/
Flickable {
<br />/'''<br /> Usage:
id: myFlickable
}
ScrollBar {
target: myFlickable
}
*/


Flickable {<br /> id: myFlickable<br /> …<br /> }<br /> ScrollBar {<br /> target: myFlickable<br /> }<br />'''/
import Qt 4.7
<br />import Qt 4.7<br />import style 1.0
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 /></code>
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;
}
}
}
}
}
</code>


=== qmldir ===
=== qmldir ===
Line 58: Line 218:
This defines the plugin name and resources for it.
This defines the plugin name and resources for it.


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


=== StylePlugin.h ===
=== StylePlugin.h ===


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


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


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


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


#endif // STYLE_PLUGIN_H
#endif // STYLE_PLUGIN_H
Line 76: Line 246:
=== StylePlugin.cpp ===
=== StylePlugin.cpp ===


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


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


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


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


Q_EXPORT_PLUGIN2(style, StylePlugin)
Q_EXPORT_PLUGIN2(style, StylePlugin)
Line 92: Line 271:
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.
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.


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


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


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


};
};


#endif // STYLEIMAGEPROVIDER_H<br /></code>
#endif // STYLEIMAGEPROVIDER_H
</code>


=== StyleImageProvider.cpp ===
=== StyleImageProvider.cpp ===


<code><br />#include &quot;StyleImageProvider.h&amp;quot;
<code>
#include "StyleImageProvider.h"
 
#include <QtGui/QApplication>
#include <QtGui/QPainter>
#include <QtGui/QStyleOptionSlider>
 
namespace
{
QMap<QString, QStyle::ComplexControl> complexControlMap;
QMap<QString, QStyle::SubControl> subControlMap;
}


#include &lt;QtGui/QApplication&amp;gt;<br />#include &lt;QtGui/QPainter&amp;gt;<br />#include &lt;QtGui/QStyleOptionSlider&amp;gt;
StyleImageProvider::StyleImageProvider()
: QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap)
{
complexControlMap["scrollBar"] = QStyle::CC_ScrollBar;


namespace<br />{<br /> QMap&amp;lt;QString, QStyle::ComplexControl&amp;gt; complexControlMap;<br /> QMap&amp;lt;QString, QStyle::SubControl&amp;gt; subControlMap;<br />}
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;
}


StyleImageProvider::StyleImageProvider()<br /> : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap)<br />{<br /> complexControlMap[&quot;scrollBar&amp;quot;] = QStyle::CC_ScrollBar;
QPixmap StyleImageProvider::requestPixmap(const QString &amp;id, QSize '''size, const QSize &amp;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);


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 />}
// Manually tailored parameters for Windows XP
int width = 16;
int height = 50;


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);
// Setup the style options
<br /> // Manually tailored parameters for Windows XP<br /> int width = 16;<br /> int height = 50;
QStyleOptionComplex''' option = 0;
<br /> // Setup the style options<br /> QStyleOptionComplex''' option = 0;<br /> if (complexControl  QStyle::CC_ScrollBar)
if (complexControl  QStyle::CC_ScrollBar)
     &amp;#123;
     {
         QStyleOptionSlider *sliderOption = new QStyleOptionSlider;
         QStyleOptionSlider *sliderOption = new QStyleOptionSlider;
         option = sliderOption;
         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;)
         if (subControlName  "sliderDecor")
             sliderOption-&amp;gt;state |= QStyle::State_MouseOver;
// Manually tailored parameter for Windows XP
         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();
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-&gt;subControls = subControl;<br /> option-&gt;activeSubControls = subControl;
option->subControls = subControl;
option->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;
// 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<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();
// Paint the control
QPainter painter(&amp;pixmap);
QRect subControlRect = qApp->style()->subControlRect(complexControl, option, subControl);
qApp->style()->drawComplexControl(complexControl, option, &amp;painter);
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);
// 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<br /> if (size)<br /> *size = sliderPixmap.size();
// Return the size of the pixmap created
if (size)
*size = sliderPixmap.size();


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


=== style.pro ===
=== style.pro ===


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


TARGET = $$qtLibraryTarget($$TARGET)
TARGET = $$qtLibraryTarget($$TARGET)


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


OTHER_FILES = qmldir test.qml ScrollBar.qml main.qml
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 /></code>
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
</code>

Revision as of 10:48, 25 February 2015


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.

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":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", 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 <QtDeclarative/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 <QtDeclarative/qdeclarative.h>
#include <QtDeclarative/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 <QtDeclarative/QDeclarativeEngine>
#include <QtDeclarative/QDeclarativeImageProvider>

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

};

#endif // STYLEIMAGEPROVIDER_H

StyleImageProvider.cpp

#include "StyleImageProvider.h"

#include <QtGui/QApplication>
#include <QtGui/QPainter>
#include <QtGui/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 &amp;id, QSize '''size, const QSize &amp;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(&amp;pixmap);
 QRect subControlRect = qApp->style()->subControlRect(complexControl, option, subControl);
 qApp->style()->drawComplexControl(complexControl, option, &amp;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