Konstantin Utkin
Konstantin Utkin

Reputation: 508

QML: Using ScrollView leads to incorrect displaying of elements

I'm using Qt 5.2.1 for windows (Qt creator 3.0.1)

I have a custom QML component, it works fine when I'm loading in into rectangle:

import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
    id: mainRectangle
    anchors.fill: parent
        Loader {
            anchors.top: parent.top;
            anchors.left: parent.left;
            anchors.right: parent.right;
            id: ld01;
            onLoaded: {
                ld01.visible = true;
                anchors.top = parent.top;
            }
        }
        Loader {
            anchors.top: ld01.bottom;
            anchors.left: parent.left;
            anchors.right: parent.right;
            id: ld02;
            onLoaded: {
                anchors.top = ld01.bottom;
                ld02.visible = true;
            }
        }
        Component.onCompleted: {
            ld01.setSource("View_item2.qml");
            ld02.setSource("View_item2.qml");
        }
 }

But when I'm trying to put it all inside a ScrollView, elements of my component are moved somewhere. What kind of trick I should implement for correct use of ScrollView?

ScrollView {
    id: mainTabLayout
    anchors.fill: parent
    anchors.margins: 4
    //here I put a code from above (except imports, of course)
}

Component code is below:

import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.0

Rectangle {
    id: slv_layout
    objectName: "itemColumnLayout"
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.margins: 1
    property int minimal_height: 200
    height: 400
    color: "green"
    MouseArea {
        property bool is_pressed: false
        property int initial_y: 0
        property int proposed_y: 0
        id: resizeStick
        enabled: true
        anchors.bottom: parent.bottom
        height: 10
        width: parent.width
        hoverEnabled: true
        onEntered: {
            cursorShape = Qt.SizeVerCursor;
        }
        onPressed: {
            is_pressed = true;
            initial_y = mouseY;
        }
        onReleased: {
            is_pressed = false;
        }
        onMouseYChanged: {
            if (is_pressed) {
                proposed_y = slv_layout.height + mouseY - initial_y;
                if (proposed_y >= slv_layout.minimal_height) {
                    slv_layout.height += (mouseY - initial_y);
                    initial_y = mouseY;
                }
            }
        }
    }

    Text {
        id: slvTitle
        text: "device name"
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.margins: 2
    }
    Rectangle {
        anchors.top: slvTitle.bottom
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        anchors.right: parent.right
        anchors.topMargin: 2
        color: "blue"
        Button {
            id: slv_butt_run;
            objectName: "slv_butt_run"
            width: 60
            height: width
            text: "Run"
            anchors.top: parent.top
            anchors.left: parent.left
            anchors.margins: 2
        }
        Button {
            id: slv_butt_settings;
            objectName: "slv_butt_settings"
            width: 60
            height: width
            text: "Settings"
            anchors.top: parent.top
            anchors.left: slv_butt_run.right
            anchors.margins: 2
        }
        Button {
            id: slv_butt_stop;
            objectName: "slv_butt_stop"
            width: 60
            height: width
            text: "Stop"
            anchors.top: slv_butt_run.bottom
            anchors.left: parent.left
            anchors.margins: 2
        }
        Button {
            id: slv_butt_expand;
            objectName: "slv_butt_expand"
            width: 60
            height: width
            text: "Expand"
            anchors.top: slv_butt_settings.bottom
            anchors.left: slv_butt_stop.right
            anchors.margins: 2
        }
        TextArea {
            id: slv_log_area
            anchors.left: slv_butt_expand.right
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.right: parent.right
            anchors.margins: 3
        }
    }
}

How it looks when all is ok: How it looks when all is ok How it looks when not ok: How it looks when not ok

Upvotes: 4

Views: 7307

Answers (2)

Konstantin Utkin
Konstantin Utkin

Reputation: 508

Actually, I still don't know, why code works as described above. But I have found acceptable method to solve task other way.

Looks like "put a needle into egg, egg into duck, duck into rabbit": ScrollView must contain a ListView component which has a corresponding ListModel and a custom component should act as delegate. Only with ListModel I've got correct automatic scrolling and relative emplacement support.

ScrollView {
    id: id_scrollView
    anchors.fill: parent
    objectName: "ScrollView"
    frameVisible: true
    highlightOnFocus: true

    style: ScrollViewStyle {
        transientScrollBars: true
        handle: Item {
            implicitWidth: 14
            implicitHeight: 26
            Rectangle {
                color: "#424246"
                anchors.fill: parent
                anchors.topMargin: 6
                anchors.leftMargin: 4
                anchors.rightMargin: 4
                anchors.bottomMargin: 6
            }
        }
        scrollBarBackground: Item {
            implicitWidth: 14
            implicitHeight: 26
        }
    }

ListView {

    id: id_listView
    objectName: "ListView"
    anchors.top: parent.top
    anchors.left: parent.left
    anchors.right: parent.right
    anchors.rightMargin: 11
    flickableDirection: Flickable.VerticalFlick
    boundsBehavior: Flickable.StopAtBounds
    delegate: view_component
    model: id_listModel

    ListModel {
        id :id_listModel
        objectName: "ListModel"
    }

    //delegate: View_item2.Item
    Component {
        id: view_component
        View_item2 {
            objectName: name
        }
    }

}

Upvotes: 4

mcchu
mcchu

Reputation: 3369

According to the ScrollView documentation,

A ScrollView can be used either to replace a Flickable or decorate an existing Flickable. ... The width and height of the child item will be used to define the size of the content area.

A ScrollView needs to know two width-height pairs: the first one is the width and height used to display the region, and the second one is the width and height of the content. If the area of the content is larger than the display area, the display area will add a scroll bar on it.

In your example:

ScrollView {
    id: mainTabLayout
    anchors.fill: parent
    //other properties

    Rectangle {
        id: mainRectangle
        anchors.fill: parent
        //...
    }
}

The width and height of the content is bound to the display area, making the two areas be in the same size. The width and height of display area is the one in mainTabLayout, which is bound to it's parent; and the width and height of the content is the one in mainRectangle, which is bound to it's parent, mainTabLayout. Therefore the ScrollView cannot work correctly since ScrollView expects the two values are different, not bound together.

To solve your problem, you can explicitly assign width and height to mainRectangle. Do not bind the width and height of mainRectangle to it's parent using anchors.fill:parent.

ScrollView {
    id: mainTabLayout
    anchors.fill: parent
    //other properties

    Rectangle {
        id: mainRectangle

        width: 800; height: 800  //not binding to parent.width & height

        //...
    }
}

And this can work correctly.

Upvotes: 3

Related Questions