Как получить инстанцированный компонент делегата из GridView или ListView в QML

Моя головоломка, сформулированная в общем виде, заключается в следующем: посредством некоторого действия вне GridView я хочу выяснить координаты определенного элемента делегата в GridView, основываясь только на определенном элементе модели или индексе, выбранном ранее.

У меня есть GridView с рядом элементов в модели. Делегат GridView создает эскизное представление каждого элемента. При нажатии на него появляется подробное полноэкранное представление элемента. Я хотел бы получить красивый переход, который показывает, как миниатюра расширяется из своего места в GridView, а когда подробное представление удаляется, сжимается обратно в GridView на место.

Сложность в том, что подробное представление само является делегатом ListView, поэтому вы можете переходить между подробными представлениями по одному экрану за раз. Это означает, что решение, которое просто изменяет размер элемента делегата GridView или что-то в этом роде, не будет работать. Кроме того, поскольку вы можете перейти к любому элементу ListView, возврат к GridView должен быть основан только на информации, доступной в модели или индексе элемента модели (например, я не могу хранить координаты MouseArea, используемой для запуска детального представления или что-то в этом роде).

Анимация расширения довольно проста, поскольку элемент делегата имеет MouseArea для обработчика клика, который знает свое собственное размещение, так что это можно передать функции, которая запускает анимацию. Я не могу понять обратную задачу: как из элемента модели/индекса в ListView определить координаты связанного элемента в GridView?

Я не могу найти в документации ничего, что позволило бы получить доступ к экземпляру элемента делегата из экземпляра элемента модели или даже индекса. В GridView есть indexAt(), который возвращает индекс на основе координат. Думаю, я мог бы обойтись и обратным способом, но, похоже, его не существует.

Вот более конкретный пример. Прошу прощения за длину; это самый короткий пример кода, который я смог придумать и который точно описывает мою проблему:

import QtQuick 1.1

Item {
    id: window
    width: 400
    height: 200

    state: "summary"
    states: [
        State { name: "summary"; },
        State { name: "details"; }
    ]
    transitions: [
        Transition { from: "summary"; to: "details";
            SequentialAnimation {
                PropertyAction { target: animationRect; property: "visible"; value: true; }
                ParallelAnimation {
                    NumberAnimation { target: animationRect; properties: "x,y"; to: 0; duration: 200; }
                    NumberAnimation { target: animationRect; property: "width"; to: 400; duration: 200; }
                    NumberAnimation { target: animationRect; property: "height"; to: 200; duration: 200; }
                }
                PropertyAction { target: detailsView; property: "visible"; value: true; }
                PropertyAction { target: summaryView; property: "visible"; value: false; }
                PropertyAction { target: animationRect; property: "visible"; value: false; }
            }
        },
        Transition { from: "details"; to: "summary";
            SequentialAnimation {
                PropertyAction { target: summaryView; property: "visible"; value: true; }

                // How to animate animationRect back down to the correct item?

                PropertyAction { target: detailsView; property: "visible"; value: false; }
            }
        }
    ]

    Rectangle {
        id: animationRect
        z: 1
        color: "gray"
        visible: false

        function positionOverSummary(summaryRect) {
            x = summaryRect.x; y = summaryRect.y;
            width = summaryRect.width; height = summaryRect.height;
        }
    }

    ListModel {
        id: data
        ListElement { summary: "Item 1"; description: "Lorem ipsum..."; }
        ListElement { summary: "Item 2"; description: "Blah blah..."; }
        ListElement { summary: "Item 3"; description: "Hurf burf..."; }
    }

    GridView {
        id: summaryView
        anchors.fill: parent
        cellWidth: 100
        cellHeight: 100

        model: data
        delegate: Rectangle {
            color: "lightgray"
            width: 95; height: 95;

            Text { text: summary; }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    var delegateRect = mapToItem(window, x, y);
                    delegateRect.width = width; delegateRect.height = height;

                    animationRect.positionOverSummary(delegateRect);
                    detailsView.positionViewAtIndex(index, ListView.Beginning);
                    window.state = "details";
                }
            }
        }
    }

    ListView {
        id: detailsView
        anchors.fill: parent
        visible: false
        orientation: ListView.Horizontal
        snapMode: ListView.SnapOneItem

        model: data
        delegate: Rectangle {
            color: "gray"
            width: 400; height: 200;

            Column {
                Text { text: summary; }
                Text { text: description; }
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    // How do I get the coordinates to where animationRect should return?

                    summaryView.positionViewAtIndex(index, GridView.Visible);
                    window.state = "summary";
                }
            }
        }
    }
}

Есть идеи? Возможно, я просто иду неправильным путем. Если то, что я пытаюсь сделать конкретно, невозможно, может быть, есть какой-то другой способ, которым я должен это спроектировать? Спасибо!


Edit: Некоторые идеи, которые у меня возникли (ни одна из них, на мой взгляд, не осуществима):

  • Храните список всех созданных элементов делегата, используя Component.onCompleted и Component.onDestruction. Чтобы быть полезным, это должна быть карта элемента модели или индекс => элемент делегата. Проблема в том, что в документации basic types (особенно в variant) говорится, что такую карту невозможно создать в чистом QML. Так что, похоже, это означает создание такой карты как класса C++ и использование ее в QML с onCompleted/onDestruction в компоненте делегата для поддержания ее в актуальном состоянии. Это кажется немного опасным и тяжелым для того, что должно быть простым.

  • Это сообщение в списке рассылки, кажется, указывает на то, что свойство contentItem Flickable можно использовать для перечисления элементов делегата. Затем я нашел этот пост, в котором это названо плохой практикой. Я все еще изучаю этот вопрос, но я скептически отношусь к тому, что это будет законным решением. Кажется, что это слишком халтурно, чтобы работать надежно.

Это все, что у меня есть на данный момент.

17
задан feedc0de 28 February 2018 в 16:32
поделиться