I'm trying to realize something similiar to QScrollArea
(in widgets world) with the help of Qml.
I decided to probe Flickable
plus QQuickPaintedItem
based item (named Drawer in my case):
Flickable {
...
onContentXChanged(): {
drawer.update()
}
Drawer {
id: drawer
...
}
Drawer's render target is set to FrameBufferObject
. Its paint function looks like this:
void Drawer::paint(QPainter *painter)
{
// Some function to compute rect which is needed to be redrawn
QRect updateRect = computeUpdateRect();
// How to shift contents of Frame buffer, e.g. to right, and draw only updateRect in this space?
}
Imagine how we do scrolling in QScrollArea
widget, e.g. to left: all entry of viewport is shifted to right and the only small rect in left is redrawn.
I want to do the same with Flickable
+QQuickPaintedItem
. But I can't understand some things:
How can I manipulate Frame Buffer object inside QQuickPaintedItem
?
Maybe there is some more right way to implement QScrollArea
in QML?
By the way, is double buffering enabled by default in QQuickPaintedItem
?
For implementing with Flickable
:
To provide more info about task: I have a very big "picture". So I cannot load it whole into memory, but I have to navigate through it with something like viewport.
A scroll area or a flickable are used when you want to encapsulate a larger content in a smaller area and navigate around it. And in your case that is... not the case. You are not practically using a scroll area as your image is never larger than the scroll area size, you just want to fake it, which is actually quite easy:
#include <QQuickPaintedItem>
#include <QImage>
#include <QPainter>
class PImage : public QQuickPaintedItem {
Q_OBJECT
public:
PImage(QQuickItem * p = 0) : QQuickPaintedItem(p), xpos(0), ypos(0) {}
void paint(QPainter *painter) {
if (!source.isNull()) painter->drawImage(QRect(0, 0, width(), height()), source, QRect(xpos, ypos, width(), height()));
else painter->fillRect(QRect(0, 0, width(), height()), Qt::black);
}
public slots:
bool load(QString path) {
source = QImage(path);
return !source.isNull();
}
void moveBy(int x, int y) {
int ox, oy;
// don't go outside the image
ox = x + xpos + width() <= source.width() ? x + xpos : source.width() - width();
oy = y + ypos + height() <= source.height() ? y + ypos : source.height() - height();
if (ox < 0) ox = 0;
if (oy < 0) oy = 0;
if (ox != xpos || oy != ypos) {
xpos = ox;
ypos = oy;
update();
}
}
private:
QImage source;
int xpos, ypos;
};
And on the QML side:
PImage {
width: 300
height: 300
Component.onCompleted: load("d:/img.jpg") // a big image
MouseArea {
property int ix
property int iy
anchors.fill: parent
onPressed: {
ix = mouseX
iy = mouseY
}
onPositionChanged: {
parent.moveBy(ix - mouseX, iy - mouseY)
ix = mouseX
iy = mouseY
}
}
}
This is just a quick basic example, there is a lot of room to polish and improve. Also note that if the source rect is different size than the target rect, you can easily achieve zooming in or out. You can hook it to a flickable to get the "kinetic scrolling" instead of the mouse area.