[vlc-devel] [PATCH 10/32] QML: reimplement ExpandGridView starting from a Flickable

Adrien Maglo magsoft at videolan.org
Wed Jun 12 14:30:13 CEST 2019


This fixes the resizing issues of the previous implementation based on
two GridViews.
---
 .../qt/qml/mediacenter/MusicAlbumsDisplay.qml |  97 ++--
 .../MusicAlbumsGridExpandDelegate.qml         |  20 +-
 modules/gui/qt/qml/utils/ExpandGridView.qml   | 525 ++++++++++--------
 .../gui/qt/qml/utils/KeyNavigableListView.qml |   1 +
 .../qt/qml/utils/KeyNavigableTableView.qml    |   3 +
 5 files changed, 369 insertions(+), 277 deletions(-)

diff --git a/modules/gui/qt/qml/mediacenter/MusicAlbumsDisplay.qml b/modules/gui/qt/qml/mediacenter/MusicAlbumsDisplay.qml
index c421e241f7..c99ffa4634 100644
--- a/modules/gui/qt/qml/mediacenter/MusicAlbumsDisplay.qml
+++ b/modules/gui/qt/qml/mediacenter/MusicAlbumsDisplay.qml
@@ -48,46 +48,10 @@ Utils.NavigableFocusScope {
             ml: medialib
         }
 
+
         delegate: Package {
             id: element
 
-            Utils.GridItem {
-                Package.name: "gridTop"
-                image: model.cover || VLCStyle.noArtAlbum
-                title: model.title || qsTr("Unknown title")
-                subtitle: model.main_artist || qsTr("Unknown artist")
-                selected: element.DelegateModel.inSelected || view.currentItem.currentIndex === index
-                shiftX: view.currentItem.shiftX(model.index)
-
-                onItemClicked : {
-                    view._switchExpandItem( index )
-                    delegateModel.updateSelection( modifier , view.currentItem.currentIndex, index)
-                    view.currentItem.currentIndex = index
-                    view.currentItem.forceActiveFocus()
-
-                }
-                onPlayClicked: medialib.addAndPlay( model.id )
-                onAddToPlaylistClicked : medialib.addToPlaylist( model.id )
-            }
-
-            Utils.GridItem {
-                Package.name: "gridBottom"
-                image: model.cover || VLCStyle.noArtAlbum
-                title: model.title || qsTr("Unknown title")
-                subtitle: model.main_artist || qsTr("Unknown artist")
-                selected: element.DelegateModel.inSelected || view.currentItem.currentIndex === index
-                shiftX: view.currentItem.shiftX(model.index)
-
-                onItemClicked : {
-                    view._switchExpandItem( index )
-                    delegateModel.updateSelection( modifier , view.currentItem.currentIndex, index)
-                    view.currentItem.currentIndex = index
-                    view.currentItem.forceActiveFocus()
-                }
-                onPlayClicked: medialib.addAndPlay( model.id )
-                onAddToPlaylistClicked : medialib.addToPlaylist( model.id )
-            }
-
             Utils.ListItem {
                 Package.name: "list"
                 width: root.width
@@ -152,40 +116,57 @@ Utils.NavigableFocusScope {
             cellWidth: VLCStyle.cover_normal + VLCStyle.margin_small
             cellHeight: VLCStyle.cover_normal + VLCStyle.fontHeight_normal * 2 + VLCStyle.margin_small
 
+            gridDelegate: Utils.GridItem {
+                property variant delegateModelItem: ({
+                    model: ({}),
+                    itemsIndex: 0,
+                    inSelected: false
+                })
+
+                shiftX: view.currentItem.shiftX(delegateModelItem.itemsIndex)
+                image: delegateModelItem.model.cover || VLCStyle.noArtAlbum
+                title: delegateModelItem.model.title || qsTr("Unknown title")
+                subtitle: delegateModelItem.model.main_artist || qsTr("Unknown artist")
+                selected: delegateModelItem.inSelected
+                onItemClicked : {
+                    delegateModel.updateSelection( modifier , view.currentItem.currentIndex, delegateModelItem.itemsIndex)
+                    view.currentItem.currentIndex = delegateModelItem.itemsIndex
+                    //view.currentItem.forceActiveFocus()
+                    view._switchExpandItem( delegateModelItem.itemsIndex )
+
+                }
+                onPlayClicked: medialib.addAndPlay( delegateModelItem.model.id )
+                onAddToPlaylistClicked : medialib.addToPlaylist( delegateModelItem.model.id )
+            }
+
             expandDelegate:  Rectangle {
                 id: expandDelegateId
-                height: albumDetail.implicitHeight
+                implicitHeight: albumDetail.implicitHeight
                 width: root.width
                 color: VLCStyle.colors.bgAlt
                 property int currentId: -1
                 property alias model : albumDetail.model
+                property alias currentItemY: albumDetail.currentItemY
+                property alias currentItemHeight: albumDetail.currentItemHeight
+
+                onActiveFocusChanged: {
+                    if (activeFocus)
+                        albumDetail.forceActiveFocus()
+                }
 
                 MusicAlbumsGridExpandDelegate {
                     id: albumDetail
                     anchors.fill: parent
-                    visible: true
-                    focus: true
-                    model: delegateModel.items.get(gridView_id.expandIndex).model
-                    onActionCancel:  gridView_id.expandIndex = -1
-                    onActionUp:  gridView_id.expandIndex = -1
-                    onActionDown: gridView_id.expandIndex = -1
+                    onActionCancel:  gridView_id.retract()
+                    onActionUp:  gridView_id.retract()
+                    onActionDown: gridView_id.retract()
                     onActionLeft: root.actionLeft(index)
                     onActionRight: root.actionRight(index)
                 }
-
-                Connections {
-                    target: gridView_id
-                    onExpandIndexChanged: {
-                        if (gridView_id.expandIndex !== -1)
-                        {
-                            expandDelegateId.model = delegateModel.items.get(gridView_id.expandIndex).model
-                        }
-                    }
-                }
             }
 
+            model: delegateModel
             modelTop: delegateModel.parts.gridTop
-            modelBottom: delegateModel.parts.gridBottom
             modelCount: delegateModel.items.count
 
             onActionAtIndex: {
@@ -250,10 +231,12 @@ Utils.NavigableFocusScope {
         }
 
         function _switchExpandItem(index) {
-            if (view.currentItem.expandIndex === index)
+            view.currentItem.switchExpandItem(index)
+
+            /*if (view.currentItem.expandIndex === index)
                 view.currentItem.expandIndex = -1
             else
-                view.currentItem.expandIndex = index
+                view.currentItem.expandIndex = index*/
         }
     }
 
diff --git a/modules/gui/qt/qml/mediacenter/MusicAlbumsGridExpandDelegate.qml b/modules/gui/qt/qml/mediacenter/MusicAlbumsGridExpandDelegate.qml
index 701907f036..b7afd83cae 100644
--- a/modules/gui/qt/qml/mediacenter/MusicAlbumsGridExpandDelegate.qml
+++ b/modules/gui/qt/qml/mediacenter/MusicAlbumsGridExpandDelegate.qml
@@ -26,9 +26,15 @@ import "qrc:///style/"
 
 Utils.NavigableFocusScope {
     id: root
-    property var model: []
+    property variant model: MLAlbumModel{}
     //color: VLCStyle.colors.bg
     implicitHeight: layout.height
+    clip: true
+
+    width: parent.width
+
+    property int currentItemY
+    property int currentItemHeight
 
     Row {
         id: layout
@@ -181,6 +187,18 @@ Utils.NavigableFocusScope {
                 interactive: false
 
                 parentId : root.model.id
+                onParentIdChanged: {
+                    currentIndex = 0
+                    focus = true
+                }
+
+                onCurrentItemChanged: {
+                    if (currentItem != undefined) {
+                        root.currentItemY = expand_infos_id.y + expand_track_id.y + currentItem.y
+                        root.currentItemHeight = currentItem.height
+                    }
+                }
+
                 sortModel: ListModel {
                     ListElement{ criteria: "track_number";  width:0.10; visible: true; text: qsTr("#"); showSection: "" }
                     ListElement{ criteria: "title";         width:0.70; visible: true; text: qsTr("Title"); showSection: "" }
diff --git a/modules/gui/qt/qml/utils/ExpandGridView.qml b/modules/gui/qt/qml/utils/ExpandGridView.qml
index 40b4aa6430..ad18c9b582 100644
--- a/modules/gui/qt/qml/utils/ExpandGridView.qml
+++ b/modules/gui/qt/qml/utils/ExpandGridView.qml
@@ -31,18 +31,18 @@ NavigableFocusScope {
     property int marginTop: root.cellHeight / 3
 
     //model to be rendered, model has to be passed twice, as they cannot be shared between views
-    property alias modelTop: topView.model
-    property alias modelBottom: bottomView.model
+    property alias model: flickable.model
+    property variant modelTop
     property int modelCount: 0
 
-    property alias delegateTop: topView.delegate
-    property alias delegateBottom: bottomView.delegate
-
     property int currentIndex: 0
 
     /// the id of the item to be expanded
-    property int expandIndex: -1
+    property int _expandIndex: -1
+    property int _newExpandIndex: -1
+
     //delegate to display the extended item
+    property Component gridDelegate: Item{}
     property Component expandDelegate: Item{}
 
     //signals emitted when selected items is updated from keyboard
@@ -50,273 +50,360 @@ NavigableFocusScope {
     signal selectAll()
     signal actionAtIndex(int index)
 
-    property alias contentY: flickable.contentY
-    property alias interactive: flickable.interactive
-    property alias clip: flickable.clip
-    property alias contentHeight: flickable.contentHeight
-    property alias contentWidth: flickable.contentWidth
+    property double _expandRetractSpeed: 1.
+
+    function shiftX(index) {
+        var colCount = flickable.getNbItemsPerRow()
+        var rightSpace = width - colCount * root.cellWidth
+        return ((index % colCount) + 1) * (rightSpace / (colCount + 1))
+    }
+
+    function switchExpandItem(index) {
+        if (index === _expandIndex)
+            _newExpandIndex = -1
+        else
+            _newExpandIndex = index
+
+        if (_expandIndex !== -1)
+            flickable.retract()
+        else
+            flickable.expand()
+    }
 
-    //compute a delta that can be applied to grid elements to obtain an horizontal distribution
-    function shiftX( index ) {
-        var rightSpace = width - (flickable._colCount * root.cellWidth)
-        return ((index % flickable._colCount) + 1) * (rightSpace / (flickable._colCount + 1))
+    function retract() {
+        _newExpandIndex = -1
+        flickable.retract()
     }
 
+    //Gridview visible above the expanded item
     Flickable {
         id: flickable
-
-        anchors.fill: parent
         clip: true
-        //ScrollBar.vertical: ScrollBar { }
 
-        //disable bound behaviors to avoid visual artifacts around the expand delegate
-        boundsBehavior: Flickable.StopAtBounds
+        property variant model
+        property Item expandItem: root.expandDelegate.createObject(contentItem, {"height": 0})
 
+        anchors.fill: parent
 
-        // number of elements per row, for internal computation
-        property int _colCount: Math.floor(width / root.cellWidth)
-        property int topContentY: flickable.contentY
-        property int bottomContentY: flickable.contentY + flickable.height
+        onWidthChanged: { layout() }
+        onHeightChanged: { layout() }
+        onContentYChanged: { layout() }
 
-        property int _oldExpandIndex: -1
-        property bool _expandActive: root.expandIndex !== -1
+        function getNbItemsPerRow() {
+            return Math.max(Math.floor(width / root.cellWidth), 1)
+        }
 
-        function _rowOfIndex( index ) {
-            return Math.ceil( (index + 1) / flickable._colCount) - 1
+        function getItemRowCol(id) {
+            var nbItemsPerRow = getNbItemsPerRow()
+            var rowId = Math.floor(id / nbItemsPerRow)
+            var colId = id % nbItemsPerRow
+            return [colId, rowId]
         }
 
-        //from KeyNavigableGridView
-        function _yOfIndex( index ) {
-            if ( root.expandIndex != -1
-                 && (index > (flickable._rowOfIndex( root.expandIndex ) + 1) * flickable._colCount )  )
-                return flickable._rowOfIndex(root.currentIndex) * root.cellHeight + expandItem.height
-            else
-                return flickable._rowOfIndex(root.currentIndex) * root.cellHeight
+        function getItemPos(id) {
+            var rowCol = getItemRowCol(id)
+            return [rowCol[0] * root.cellWidth, rowCol[1] * root.cellHeight]
         }
 
-        Connections {
-            target: root
-            onExpandIndexChanged: {
-                flickable._updateExpandPosition()
+        function getExpandItemGridId() {
+            var ret
+            if (root._expandIndex !== -1) {
+                var rowCol = getItemRowCol(root._expandIndex)
+                var rowId = rowCol[1] + 1
+                ret = rowId * getNbItemsPerRow()
+            } else {
+                ret = model.count
             }
+            return ret
         }
-        on_ColCountChanged: _updateExpandPosition()
-        function _updateExpandPosition() {
-            expandItem.y = root.cellHeight * (Math.floor(root.expandIndex / flickable._colCount) + 1)
-            _oldExpandIndex = root.expandIndex
+
+        property variant idChildrenMap: ({})
+
+        function getFirstAndLastInstanciatedItemIds() {
+            var contentYWithoutExpand = contentY
+            var heightWithoutExpand = height
+            if (root._expandIndex !== -1) {
+                if (contentY >= expandItem.y && contentY < expandItem.y + expandItem.height)
+                    contentYWithoutExpand = expandItem.y
+                if (contentY >= expandItem.y + expandItem.height)
+                    contentYWithoutExpand = contentY - expandItem.height
+
+                var expandYStart = Math.max(contentY, expandItem.y)
+                var expandYEnd = Math.min(contentY + height, expandItem.y + expandItem.height)
+                var expandDisplayedHeight = Math.max(expandYEnd - expandYStart, 0)
+                heightWithoutExpand -= expandDisplayedHeight
+            }
+
+            var rowId = Math.floor(contentYWithoutExpand / root.cellHeight)
+            var firstId = Math.max(rowId * getNbItemsPerRow(), 0)
+
+            rowId = Math.ceil((contentYWithoutExpand + heightWithoutExpand) / root.cellHeight)
+            var lastId = Math.min(rowId * getNbItemsPerRow(), model.count)
+
+            return [firstId, lastId]
         }
 
+        function getChild(id, toUse) {
+            var ret
+            if (id in idChildrenMap) {
+                ret = idChildrenMap[id]
+                if (ret === undefined)
+                    throw "wrong child: " + id
+            }
+            else {
+                ret = toUse.pop()
+                if (ret === undefined)
+                    throw "wrong toRecycle child " + id + ", len " + toUse.length
+                idChildrenMap[id] = ret
+            }
+            return ret
+        }
 
-        states: [
-            State {
-                name: "-expand"
-                when: ! flickable._expandActive
-                PropertyChanges {
-                    target: flickable
-                    topContentY: flickable.contentY
-                    contentHeight: root.cellHeight * Math.ceil(root.modelCount / flickable._colCount)
-                }
-            },
-            State {
-                name: "+expand"
-                when: flickable._expandActive
-                PropertyChanges {
-                    target: flickable
-                    topContentY: flickable.contentY
-                    contentHeight: root.cellHeight * Math.ceil(root.modelCount / flickable._colCount) + expandItem.height
+        function layout() {
+            var i
+            var expandItemGridId = getExpandItemGridId()
+
+            var f_l = getFirstAndLastInstanciatedItemIds()
+            var nbItems = f_l[1] - f_l[0]
+            var firstId = f_l[0]
+            var lastId = f_l[1]
+
+            var topGridEndId = Math.max(Math.min(expandItemGridId, lastId), firstId)
+
+            // Clean the no longer used ids
+            var toKeep = {}
+            var toUse = []
+            for (var id in idChildrenMap) {
+                var val = idChildrenMap[id]
+                if (id >= firstId && id < lastId)
+                    toKeep[id] = val
+                else {
+                    toUse.push(val)
+                    val.visible = false
                 }
             }
-        ]
-
-        //Gridview visible above the expanded item
-        GridView {
-            id: topView
-            clip: true
-            interactive: false
-
-            focus: !flickable._expandActive
-
-            highlightFollowsCurrentItem: false
-            currentIndex: root.currentIndex
-
-            cellWidth: root.cellWidth
-            cellHeight: root.cellHeight
-
-            anchors.left: parent.left
-            anchors.right: parent.right
-
-            states: [
-                //expand is unactive or below the view
-                State {
-                    name: "visible_noexpand"
-                    when: !flickable._expandActive || expandItem.y >= flickable.bottomContentY
-                    PropertyChanges {
-                        target: topView
-                        y: flickable.topContentY
-
-                        height:flickable.height
-                        //FIXME: should we add + originY? this seemed to fix some issues but has performance impacts
-                        //OriginY, seems to change randomly on grid resize
-                        contentY: flickable.topContentY
-                        visible: true
-                        enabled: true
-                    }
-                },
-                //expand is active and within the view
-                State {
-                    name: "visible_expand"
-                    when: flickable._expandActive && (expandItem.y >= flickable.contentY) && (expandItem.y < flickable.bottomContentY)
-                    PropertyChanges {
-                        target: topView
-                        y: flickable.contentY
-                        height: expandItem.y - flickable.topContentY
-                        //FIXME: should we add + originY? this seemed to fix some issues but has performance impacts
-                        //OriginY, seems to change randomly on grid resize
-                        contentY: flickable.topContentY
-                        visible: true
-                        enabled: true
-                    }
-                },
-                //expand is active and above the view
-                State {
-                    name: "hidden"
-                    when: flickable._expandActive && (expandItem.y < flickable.contentY)
-                    PropertyChanges {
-                        target: topView
-                        visible: false
-                        enabled: false
-                        height: 1
-                        y: 0
-                        contentY: 0
-                    }
+            idChildrenMap = toKeep
+
+            // Create delegates if we do not have enough
+            if (nbItems > toUse.length + Object.keys(toKeep).length) {
+                var toCreate = nbItems - (toUse.length + Object.keys(toKeep).length)
+                for (i = 0; i < toCreate; ++i) {
+                    val = root.gridDelegate.createObject(contentItem);
+                    toUse.push(val)
                 }
-            ]
+            }
+
+            // Place the delegates before the expandItem
+            for (i = firstId; i < topGridEndId; ++i) {
+                var pos = getItemPos(i)
+                var item = getChild(i, toUse)
+                item.delegateModelItem = model.items.get(i)
+                item.x = pos[0]
+                item.y = pos[1]
+                item.visible = true
+            }
+
+            expandItem.y = getItemPos(expandItemGridId)[1]
+
+            // Place the delegates after the expandItem
+            for (i = topGridEndId; i < lastId; ++i) {
+                pos = getItemPos(i)
+                item = getChild(i, toUse)
+                item.delegateModelItem = model.items.get(i)
+                item.x = pos[0]
+                item.y = pos[1] + expandItem.height
+                item.visible = true
+            }
+
+            // Calculate and set the contentHeight
+            var newContentHeight = getItemPos(model.count - 1)[1] + root.cellHeight
+            if (root._expandIndex !== -1)
+                newContentHeight += expandItem.height
+            contentHeight = newContentHeight
+            setCurrentItemFocus()
         }
 
-        //Expanded item view
-        Loader {
-            id: expandItem
-            sourceComponent: root.expandDelegate
-            active: flickable._expandActive
-            focus: flickable._expandActive
-            y: 0 //updated by _updateExpandPosition
-            property int bottomY: y + height
-            anchors.left: parent.left
-            anchors.right: parent.right
+        Connections {
+            target: model.items
+            onChanged: {
+                // Hide the expandItem with no animation
+                _expandIndex = -1
+                flickable.expandItem.height = 0
+                // Regenerate the gridview layout
+                flickable.layout()
+            }
         }
 
-        //Gridview visible below the expand item
-        GridView {
-            id: bottomView
-            clip: true
-            interactive: false
-            highlightFollowsCurrentItem: false
-            //don't bind the current index, otherwise it reposition the contentY on it's own
-            //currentIndex: root.currentIndex
-
-            cellWidth: root.cellWidth
-            cellHeight: root.cellHeight
-
-            anchors.left: parent.left
-            anchors.right: parent.right
-
-            property bool hidden: !flickable._expandActive
-                                  || (expandItem.bottomY >= flickable.bottomContentY)
-                                  || flickable._rowOfIndex(root.expandIndex) === flickable._rowOfIndex(root.modelCount - 1)
-            states: [
-                //expand is visible and above the view
-                State {
-                    name: "visible_noexpand"
-                    when: !bottomView.hidden && (expandItem.bottomY < flickable.contentY)
-                    PropertyChanges {
-                        target: bottomView
-                        enabled: true
-                        visible: true
-                        height: flickable.height
-                        y: flickable.contentY
-                        contentY: expandItem.y + flickable.contentY - expandItem.bottomY
-                    }
-                },
-                //expand is visible and within the view
-                State {
-                    name: "visible_expand"
-                    when: !bottomView.hidden && (expandItem.bottomY > flickable.contentY) && (expandItem.bottomY < flickable.bottomContentY)
-                    PropertyChanges {
-                        target: bottomView
-                        enabled: true
-                        visible: true
-                        height: Math.min(flickable.bottomContentY - expandItem.bottomY, root.cellHeight * ( flickable._rowOfIndex(root.modelCount - 1) - flickable._rowOfIndex(root.expandIndex)))
-                        y: expandItem.bottomY
-                        contentY:  expandItem.y
-                    }
-                },
-                //expand is inactive or below the view
-                State {
-                    name: "hidden"
-                    when: bottomView.hidden
-                    PropertyChanges {
-                        target: bottomView
-                        enabled: false
-                        visible: false
-                        height: 1
-                        y: 0
-                        contentY: 0
-                    }
+        Connections {
+            target: flickable.expandItem
+            onHeightChanged: {
+                flickable.layout()
+            }
+            onImplicitHeightChanged: {
+                /* This is the only event we have after the expandItem height content was resized.
+                   We can trigger here the expand animation with the right final height. */
+                if (root._expandIndex !== -1)
+                    flickable.expandAnimation()
+            }
+            onCurrentItemYChanged: {
+                var newContentY = flickable.contentY;
+                var currentItemYPos = flickable.getItemPos(currentIndex)[1] + cellHeight + flickable.expandItem.currentItemY
+                if (currentItemYPos + flickable.expandItem.currentItemHeight > flickable.contentY + flickable.height) {
+                    //move viewport to see current item bottom
+                    newContentY = Math.min(
+                                currentItemYPos + flickable.expandItem.currentItemHeight - flickable.height,
+                                flickable.contentHeight - flickable.height)
+                } else if (currentItemYPos < flickable.contentY) {
+                    //move viewport to see current item top
+                    newContentY = Math.max(currentItemYPos, 0)
                 }
-            ]
+
+                if (newContentY !== flickable.contentY)
+                    animateFlickableContentY(newContentY)
+            }
+        }
+
+        function expand() {
+            _expandIndex = _newExpandIndex
+            expandItem.model = model.items.get(_expandIndex).model
+            /* We must also start the expand animation here since the expandItem implicitHeight is not
+               changed if it had the same height at previous opening. */
+            expandAnimation()
+        }
+
+        function expandAnimation() {
+            var expandItemHeight = flickable.expandItem.implicitHeight;
+
+            // Expand animation
+
+            flickable.expandItem.focus = true
+            // The animation may have already been triggered, we must stop it.
+            animateExpandItem.stop()
+            animateExpandItem.to = expandItemHeight
+            animateExpandItem.start()
+
+            // Sliding animation
+
+            var newContentY = flickable.contentY;
+            var currentItemYPos = flickable.getItemPos(currentIndex)[1]
+            if (currentItemYPos + cellHeight + expandItemHeight > flickable.contentY + flickable.height) {
+                if (cellHeight + expandItemHeight > flickable.height)
+                    newContentY = currentItemYPos
+                else
+                    newContentY = Math.min(
+                                currentItemYPos + cellHeight + expandItemHeight - flickable.height,
+                                flickable.contentHeight + expandItemHeight - flickable.height)
+            }
+
+            if (newContentY !== flickable.contentY)
+                animateFlickableContentY(newContentY)
+        }
+
+        function retract() {
+            animateRetractItem.start()
+        }
+
+        PropertyAnimation {
+            id: animateRetractItem;
+            target: flickable.expandItem;
+            properties: "height"
+            easing.type: Easing.OutQuad
+            duration: 250
+            to: 0
+            onStopped: {
+                _expandIndex = -1
+                flickable.setCurrentItemFocus()
+                root.animateToCurrentIndex()
+                if (_newExpandIndex !== -1)
+                    flickable.expand()
+            }
+        }
+
+        PropertyAnimation {
+            id: animateExpandItem;
+            target: flickable.expandItem;
+            properties: "height"
+            easing.type: Easing.InQuad
+            duration: 250
+            from: 0
+        }
+
+        function setCurrentItemFocus() {
+            var child
+            if (currentIndex in idChildrenMap)
+                child = idChildrenMap[currentIndex]
+            if (child !== undefined && _expandIndex === -1)
+                child.focus = true
         }
     }
 
-    onCurrentIndexChanged: {
-        if ( flickable._yOfIndex(root.currentIndex) + root.cellHeight > flickable.bottomContentY) {
-            //move viewport to see expanded item bottom
-            flickable.contentY = Math.min(
-                        flickable._yOfIndex(root.currentIndex) + root.cellHeight - flickable.height, // + flickable.marginBottom,
+    PropertyAnimation {
+        id: animateContentY;
+        target: flickable;
+        properties: "contentY"
+    }
+
+    function animateFlickableContentY( newContentY ) {
+        animateContentY.stop()
+        animateContentY.duration = 250
+        animateContentY.to = newContentY
+        animateContentY.start()
+    }
+
+    function animateToCurrentIndex() {
+        var newContentY = flickable.contentY;
+        var currentItemYPos = flickable.getItemPos(currentIndex)[1]
+        if (currentItemYPos + cellHeight > flickable.contentY + flickable.height) {
+            //move viewport to see current item bottom
+            newContentY = Math.min(
+                        currentItemYPos + cellHeight - flickable.height,
                         flickable.contentHeight - flickable.height)
-        } else if (flickable._yOfIndex(root.currentIndex)  < flickable.contentY) {
-            //move viewport to see expanded item at top
-            flickable.contentY = Math.max(
-                        flickable._yOfIndex(root.currentIndex) - root.marginTop,
-                        0)
+        } else if (currentItemYPos < flickable.contentY) {
+            //move viewport to see current item top
+            newContentY = Math.max(currentItemYPos, 0)
         }
+
+        if (newContentY !== flickable.contentY)
+            animateFlickableContentY(newContentY)
     }
 
-    onExpandIndexChanged: {
-        if (expandIndex != -1) {
-            //move viewport to see expanded item at top
-            flickable.contentY = Math.max( (flickable._rowOfIndex( root.expandIndex ) * root.cellHeight) - root.marginTop, 0)
-        }
+    onCurrentIndexChanged: {
+        flickable.setCurrentItemFocus()
+        animateToCurrentIndex()
     }
 
     Keys.onPressed: {
+        var colCount = flickable.getNbItemsPerRow()
+
         var newIndex = -1
         if (event.key === Qt.Key_Right || event.matches(StandardKey.MoveToNextChar)) {
-            if ((root.currentIndex + 1) % flickable._colCount !== 0) {//are we not at the end of line
-                newIndex = Math.min(root.modelCount - 1, root.currentIndex + 1)
+            if ((currentIndex + 1) % colCount !== 0) {//are we not at the end of line
+                newIndex = Math.min(root.modelCount - 1, currentIndex + 1)
             }
         } else if (event.key === Qt.Key_Left || event.matches(StandardKey.MoveToPreviousChar)) {
-            if (root.currentIndex % flickable._colCount !== 0) {//are we not at the begining of line
-                newIndex = Math.max(0, root.currentIndex - 1)
+            if (currentIndex % colCount !== 0) {//are we not at the begining of line
+                newIndex = Math.max(0, currentIndex - 1)
             }
         } else if (event.key === Qt.Key_Down || event.matches(StandardKey.MoveToNextLine) ||event.matches(StandardKey.SelectNextLine) ) {
-            if (Math.floor(root.currentIndex / flickable._colCount) !== Math.floor(root.modelCount / flickable._colCount)) { //we are not on the last line
-                newIndex = Math.min(root.modelCount - 1, root.currentIndex + flickable._colCount)
+            if (Math.floor(currentIndex / colCount) !== Math.floor(root.modelCount / colCount)) { //we are not on the last line
+                newIndex = Math.min(root.modelCount - 1, currentIndex + colCount)
             }
         } else if (event.key === Qt.Key_PageDown || event.matches(StandardKey.MoveToNextPage) ||event.matches(StandardKey.SelectNextPage)) {
-            newIndex = Math.min(root.modelCount - 1, root.currentIndex + flickable._colCount * 5)
+            newIndex = Math.min(root.modelCount - 1, currentIndex + colCount * 5)
         } else if (event.key === Qt.Key_Up || event.matches(StandardKey.MoveToPreviousLine) ||event.matches(StandardKey.SelectPreviousLine)) {
-             if (Math.floor(root.currentIndex / flickable._colCount) !== 0) { //we are not on the first line
-                newIndex = Math.max(0, root.currentIndex - flickable._colCount)
+             if (Math.floor(currentIndex / colCount) !== 0) { //we are not on the first line
+                newIndex = Math.max(0, currentIndex - colCount)
              }
         } else if (event.key === Qt.Key_PageUp || event.matches(StandardKey.MoveToPreviousPage) ||event.matches(StandardKey.SelectPreviousPage)) {
-            newIndex = Math.max(0, root.currentIndex - flickable._colCount * 5)
+            newIndex = Math.max(0, currentIndex - colCount * 5)
         }
 
-        if (newIndex != -1 && newIndex != root.currentIndex) {
+        if (newIndex != -1 && newIndex != currentIndex) {
             event.accepted = true
             var oldIndex = currentIndex
             currentIndex = newIndex
             root.selectionUpdated(event.modifiers, oldIndex, newIndex)
+            flickable.layout()
         }
 
         if (!event.accepted)
@@ -329,7 +416,7 @@ NavigableFocusScope {
             root.selectAll()
         } else if (event.key === Qt.Key_Space || event.matches(StandardKey.InsertParagraphSeparator)) { //enter/return/space
             event.accepted = true
-            root.actionAtIndex(root.currentIndex)
+            root.actionAtIndex(currentIndex)
         }
     }
 }
diff --git a/modules/gui/qt/qml/utils/KeyNavigableListView.qml b/modules/gui/qt/qml/utils/KeyNavigableListView.qml
index c5eb93d240..4490282d91 100644
--- a/modules/gui/qt/qml/utils/KeyNavigableListView.qml
+++ b/modules/gui/qt/qml/utils/KeyNavigableListView.qml
@@ -51,6 +51,7 @@ NavigableFocusScope {
     property alias headerItem: view.headerItem
 
     property alias currentIndex: view.currentIndex
+    property alias currentItem: view.currentItem
 
     property alias highlightMoveVelocity: view.highlightMoveVelocity
 
diff --git a/modules/gui/qt/qml/utils/KeyNavigableTableView.qml b/modules/gui/qt/qml/utils/KeyNavigableTableView.qml
index 3be87f7709..178ab54c86 100644
--- a/modules/gui/qt/qml/utils/KeyNavigableTableView.qml
+++ b/modules/gui/qt/qml/utils/KeyNavigableTableView.qml
@@ -40,6 +40,9 @@ NavigableFocusScope {
 
     property alias section: view.section
 
+    property alias currentIndex: view.currentIndex
+    property alias currentItem: view.currentItem
+
     Utils.SelectableDelegateModel {
         id: delegateModel
 
-- 
2.20.1



More information about the vlc-devel mailing list