2

I have a ChartView item declared in QML and I need a rubberband-like zoom functionality. This can be achieved with semi-transparent rectangle and MouseArea item. The problem is with one rectangle it's only possible to select area from top-left to bottom-right due to the fact that Rectangle item with negative dim-s is either invisible or disabled. Although it's possible to apply transform to Rectangle

transform: Scale { origin.x: 0; origin.y: 0; xScale: -1}

I failed to find how to manipulate xScale/yScale properties from the outside.

Right now I draw 4 rectangles, one per each quadrant, with correct xScale/yScale and dims (code in the end).

So I wonder is there more elegant/easy solution to the problem?

ChartView {
    id: chartViewTop
    ...

    Rectangle{
        id: rubberBandRec1
        border.color: "black"
        border.width: 1
        opacity: 0.3
        visible: false
        transform: Scale { origin.x: 0; origin.y: 0; yScale: -1}
    }

    Rectangle{
        id: rubberBandRec2
        border.color: "black"
        border.width: 1
        opacity: 0.3
        visible: false
        transform: Scale { origin.x: 0; origin.y: 0; yScale: -1; xScale: -1}
    }

    Rectangle{
        id: rubberBandRec3
        border.color: "black"
        border.width: 1
        opacity: 0.3
        visible: false
        transform: Scale { origin.x: 0; origin.y: 0; xScale: -1}
    }

    Rectangle{
        id: rubberBandRec4
        border.color: "black"
        border.width: 1
        opacity: 0.3
        visible: false
    }

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        onPressed: {
            rubberBandRec1.x = mouseX; rubberBandRec1.y = mouseY; rubberBandRec1.visible = true;
            rubberBandRec2.x = mouseX; rubberBandRec2.y = mouseY; rubberBandRec2.visible = true;
            rubberBandRec3.x = mouseX; rubberBandRec3.y = mouseY; rubberBandRec3.visible = true;
            rubberBandRec4.x = mouseX; rubberBandRec4.y = mouseY; rubberBandRec4.visible = true;
        }
        onMouseXChanged: {
            rubberBandRec1.width = mouseX - rubberBandRec1.x;
            rubberBandRec2.width = rubberBandRec2.x-mouseX;
            rubberBandRec3.width = rubberBandRec3.x-mouseX;
            rubberBandRec4.width = mouseX - rubberBandRec4.x;
        }
        onMouseYChanged: {
            rubberBandRec1.height = rubberBandRec1.y - mouseY;
            rubberBandRec2.height = rubberBandRec2.y - mouseY;
            rubberBandRec3.height = mouseY - rubberBandRec3.y;
            rubberBandRec4.height = mouseY - rubberBandRec4.y;
        }
        onReleased: {
            var x = rubberBandRec4.x-(rubberBandRec4.width<0)*Math.abs(rubberBandRec4.width);
            var y = rubberBandRec4.y-(rubberBandRec4.height<0)*Math.abs(rubberBandRec4.height);

            if (Math.abs(rubberBandRec4.width*rubberBandRec4.height)>100)
                chartViewTop.zoomIn(Qt.rect(x, y, Math.abs(rubberBandRec4.width),
                                            Math.abs(rubberBandRec4.height)));
            rubberBandRec1.visible = false;
            rubberBandRec2.visible = false;
            rubberBandRec3.visible = false;
            rubberBandRec4.visible = false;
        }
    }
}
z3dd
  • 177
  • 1
  • 11

1 Answers1

6

Set external properties for the scaling, and then just change these in the onMouseXChanged and onMouseYChanged events as follows. This appears to be working for me:

property int xScaleZoom: 0
property int yScaleZoom: 0

Rectangle{
    id: recZoom
    border.color: "steelblue"
    border.width: 1
    color: "steelblue"
    opacity: 0.3
    visible: false
    transform: Scale { origin.x: 0; origin.y: 0; xScale: xScaleZoom; yScale: yScaleZoom}
}
MouseArea {
    anchors.fill: parent
    hoverEnabled: true
    onPressed: {
        recZoom.x = mouseX;
        recZoom.y = mouseY;
        recZoom.visible = true;
    }
    onMouseXChanged: {
        if (mouseX - recZoom.x >= 0) {
            xScaleZoom = 1;
            recZoom.width = mouseX - recZoom.x;
        } else {
            xScaleZoom = -1;
            recZoom.width = recZoom.x - mouseX;
        }
    }
    onMouseYChanged: {
        if (mouseY - recZoom.y >= 0) {
            yScaleZoom = 1;
            recZoom.height = mouseY - recZoom.y;
        } else {
            yScaleZoom = -1;
            recZoom.height = recZoom.y - mouseY;
        }
    }
    onReleased: {
        var x = (mouseX >= recZoom.x) ? recZoom.x : mouseX
        var y = (mouseY >= recZoom.y) ? recZoom.y : mouseY
        chartView.zoomIn(Qt.rect(x, y, recZoom.width, recZoom.height));
        recZoom.visible = false;
    }
}
Todd Hay
  • 76
  • 1
  • 3
  • Thanks, it works. I've tried setting x/yScaleZoom as recZoom's property before but for some reason transform couldn't see them, why is that? – z3dd Feb 03 '17 at 08:38
  • I wonder, was it that transform couldn't see them or the MouseArea couldn't see them? If they were properties set directly within recZoom then I'm guessing that MouseArea would not know about xScaleZoom or yScaleZoom – Todd Hay Feb 04 '17 at 01:48
  • I'll check on Monday to be sure, but IDE pointed me to `transform: Scale { ... }` and I also logged the value of xScaleZoom in onMouseXChanged. – z3dd Feb 04 '17 at 05:20