[vlc-commits] [Git][videolan/vlc][master] 4 commits: qml: document required properties from delegate of ExpandGridView

Steve Lhomme (@robUx4) gitlab at videolan.org
Fri Feb 16 15:28:12 UTC 2024



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
231dcc39 by Prince Gupta at 2024-02-16T14:47:14+00:00
qml: document required properties from delegate of ExpandGridView

- - - - -
4dac3a17 by Prince Gupta at 2024-02-16T14:47:14+00:00
qml: minor refactor code in positioning item in ExpandGridView

- - - - -
5418ed66 by Prince Gupta at 2024-02-16T14:47:14+00:00
qml: implement 'delayRemove' for delegates of ExpandGridView

- - - - -
e215f686 by Prince Gupta at 2024-02-16T14:47:14+00:00
qml: implement 'reuseItems' for ExpandGridView

- - - - -


1 changed file:

- modules/gui/qt/widgets/qml/ExpandGridView.qml


Changes:

=====================================
modules/gui/qt/widgets/qml/ExpandGridView.qml
=====================================
@@ -57,6 +57,12 @@ FocusScope {
 
     readonly property int rowHeight: cellHeight + verticalSpacing
 
+    // This property enables you to reuse items that are instantiated for
+    // different indexes when particular index goes out of view
+    // not setting may result in large performance penalty
+    // default is true
+    property bool reuseItems: true
+
     property int rowX: 0
     property int horizontalSpacing: VLCStyle.column_spacing
     property int verticalSpacing: VLCStyle.column_spacing
@@ -94,12 +100,33 @@ FocusScope {
 
     property int _currentFocusReason: Qt.OtherFocusReason
 
-    //delegate to display the extended item
-    property Component delegate: Item{}
+    // The delegate provides a template defining each item instantiated by the view.
+    // 'delegate' must have following properties defined -
+    // 'var model'
+    //      - set by ExpandGridView, this defines model data associated with item
+    //        index data can be accesses by the model roles
+    // 'int index'
+    //      - set by ExpandGridView, this defines the index to which this delegate
+    //        is associated to
+    // 'bool selected'
+    //      - set by ExpandGridView, this defines if the associated index is selected
+    //        in selectionModel
+    //
+    // optional properties -
+    // 'bool delayRemove'
+    //      - if defined and set, item will not be removed when it goes out of view
+    //        ExpandGridView will never modify this value
+    //
+    property Component delegate: Item {
+        property var model: null
+        property int index: - 1
+        property bool selected: false
+    }
 
     property var _idChildrenList: []
     property var _unusedItemList: []
     property var _currentRange: [0,0]
+    property var _delayedChildrenMap: ({})
 
     // Aliases
 
@@ -175,6 +202,13 @@ FocusScope {
         flickable.layout(true)
     }
 
+    onReuseItemsChanged: {
+        if (!reuseItems) {
+            _unusedItemList.forEach((item) => { item.destroy() })
+            _unusedItemList = []
+        }
+    }
+
     // Keys
 
     Keys.onPressed: (event) => {
@@ -255,6 +289,9 @@ FocusScope {
             const iMin = topLeft.row
             const iMax = bottomRight.row + 1 // [] => [)
             const f_l = _currentRange
+
+            _refreshDelayedChildData(iMin, iMax)
+
             if (iMin < f_l[1] && f_l[0] < iMax) {
                 _refreshData(iMin, iMax)
             }
@@ -283,9 +320,15 @@ FocusScope {
         function _updateSelectedRange(topLeft, bottomRight, select) {
             let iMin = topLeft.row
             let iMax = bottomRight.row + 1 // [] => [)
+
+            // delayed children can be out of currentRange
+            _updateDelayedChildSelected(iMin, iMax, select)
+
             if (iMin < root._currentRange[1] && root._currentRange[0] < iMax) {
+                // only update item in the view
                 iMin = Math.max(iMin, root._currentRange[0])
                 iMax = Math.min(iMax, root._currentRange[1])
+
                 for (let j = iMin; j < iMax; j++) {
                     const item = root._getItem(j)
                     console.assert(item)
@@ -511,16 +554,28 @@ FocusScope {
         return rowCol[0] % 2 + 2 * (rowCol[1] % 2)
     }
 
-    function _repositionItem(id, x, y) {
-        const item = _getItem(id)
-        console.assert(item !== undefined, "wrong child: " + id)
-
+    function _updatePosition(id, item, x, y) {
         //theses properties are always defined in Item
         item.x = x
         item.y = y
         item.z = _indexToZ(id)
+
+        // update required property (do we need this??)
         item.selected = selectionModel.isSelected(id)
+    }
+
+    function _repositionItem(id, x, y) {
+        const item = _getItem(id)
+        console.assert(item !== undefined, "wrong child: " + id)
 
+        _updatePosition(id, item, x, y)
+        return item
+    }
+
+    function _repositionDelayedItem(id, x, y) {
+        const item = _delayedChildrenMap[id]
+
+        _updatePosition(id, item, x, y)
         return item
     }
 
@@ -558,17 +613,63 @@ FocusScope {
         return item
     }
 
+    function _takeDelayedChild(id) {
+        const item = _delayedChildrenMap[id]
+        console.assert(typeof item !== "undefined")
+        delete _delayedChildrenMap[id]
+        return item
+    }
+
+    function _shouldDelayRemove(item) {
+        return Helpers.get(item, "delayRemove", false)
+    }
+
+    function _delayRemove(id, item) {
+        _delayedChildrenMap[id] = item
+
+        item.delayRemoveChanged.connect(() => {
+            if (id in _delayedChildrenMap && !item.delayRemove) {
+                const removed = _takeDelayedChild(id)
+                console.assert(removed === item)
+                item.destroy()
+            }
+        })
+    }
+
+    function _refreshDelayedChildData(iMin, iMax) {
+        for (let i = iMin; i < iMax; ++i) {
+            if (!(i in _delayedChildrenMap))
+                continue
+
+            const item =  _delayedChildrenMap[i]
+            item.model = model.getDataAt(i)
+        }
+    }
+
+    function _updateDelayedChildSelected(iMin, iMax, select) {
+        for (let i = iMin; i < iMax; ++i) {
+            if (!(i in _delayedChildrenMap))
+                continue
+
+            const item =  _delayedChildrenMap[i]
+            item.selected = select
+        }
+    }
+
     function _setupChild(id, ydelta) {
         const pos = getItemPos(id)
+        pos[1] += ydelta
 
         let item;
 
         if (_containsItem(id))
-            item = _repositionItem(id, pos[0], pos[1] + ydelta)
-        else if (_unusedItemList.length > 0)
-            item = _recycleItem(id, pos[0], pos[1] + ydelta)
+            item = _repositionItem(id, pos[0], pos[1])
+        else if (id in _delayedChildrenMap)
+            item = _repositionDelayedItem(id, pos[0], pos[1])
+        else if (_unusedItemList.length > 0) // if reuseItems is false, _unusedItemList is always empty
+            item = _recycleItem(id, pos[0], pos[1])
         else
-            item = _createItem(id, pos[0], pos[1] + ydelta)
+            item = _createItem(id, pos[0], pos[1])
 
         // NOTE: This makes sure we have the proper focus reason on the GridItem.
         if (activeFocus && currentIndex === item.index && expandIndex === -1)
@@ -749,8 +850,13 @@ FocusScope {
 
         function _updateChildrenMap(first, last) {
             if (first >= last) {
-                root._idChildrenList.forEach(function(item) { item.visible = false; })
-                root._unusedItemList = root._idChildrenList
+                if (root.reuseItems) {
+                    root._idChildrenList.forEach((item) => { item.visible = false; })
+                    root._unusedItemList = root._idChildrenList
+                } else {
+                    root._idChildrenList.forEach((item) => { item.destroy() })
+                }
+
                 root._idChildrenList = []
                 root._currentRange = [0, 0]
                 return
@@ -760,16 +866,31 @@ FocusScope {
 
             const newList = new Array(last - first)
 
+            // move items from currentRange still in view
             for (let i = overlapped[0]; i < overlapped[1]; ++i) {
                 newList[i - first] = root._getItem(i)
                 root._setItem(i, undefined)
             }
 
+            for (let id in _delayedChildrenMap) {
+                if (id >= first && id < last) {
+                    newList[id - first] = _takeDelayedChild(id)
+                }
+            }
+
+            // handle item from current range which are not in view
             for (let i = root._currentRange[0]; i < root._currentRange[1]; ++i) {
                 const item = root._getItem(i)
                 if (typeof item !== "undefined") {
-                    item.visible = false
-                    root._unusedItemList.push(item)
+                    if (_shouldDelayRemove(item)) {
+                        _delayRemove(i, item)
+                    } else if (root.reuseItems) {
+                        item.visible = false
+                        root._unusedItemList.push(item)
+                    } else {
+                        item.destroy()
+                    }
+
                     //  root._setItem(i, undefined) // not needed the list will be reset following this loop
                 }
             }
@@ -812,6 +933,26 @@ FocusScope {
 
             // Place the delegates after the expandItem
             _setupIndexes(forceRelayout, [topGridEndId, lastId], root._expandItemVerticalSpace)
+
+            // handle delayedRemoveChildren
+            if (forceRelayout) {
+                for (let id in _delayedChildrenMap) {
+                    // check invariant: delayedRemove child must be reused when they come in view
+                    console.assert((id < root._currentRange[0]) || (id >= root._currentRange[1]))
+
+                    if (id >= root._count) {
+                        // index is no longer valid
+                        const item = _takeDelayedChild(id)
+                        item.destroy()
+                    } else {
+                        const yDelta = (id >= topGridEndId) ? root._expandItemVerticalSpace : 0
+                        _setupChild(id, yDelta)
+                    }
+                }
+            }
+
+            if (!root.reuseItems) // check invariant: correct cleanup if reuseItems is not set
+                console.assert(_unusedItemList.length == 0)
         }
 
         Connections {



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f95c9c6aa692f8cb9ec56a637795d0cd65dc5ee4...e215f68602b9fb90d2f919db0005a63374110299

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/f95c9c6aa692f8cb9ec56a637795d0cd65dc5ee4...e215f68602b9fb90d2f919db0005a63374110299
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list