[vlc-commits] [Git][videolan/vlc][master] 4 commits: qml: enable ability to customize drop indicator in ListViewExt

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Fri Sep 13 14:08:12 UTC 2024



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
81a50fac by Fatih Uzunoglu at 2024-09-13T13:55:22+00:00
qml: enable ability to customize drop indicator in ListViewExt

In some cases, drop indicator may need to be customized. This
also allows disabling the indicator completely.

The way it works is by instantiating the drop indicator from
the component property once `itemContainsDrag`, which is an
optional property, changes.

- - - - -
43df33c6 by Fatih Uzunoglu at 2024-09-13T13:55:22+00:00
qml: fix invalid property accession in PlaylistDelegate

- - - - -
0174b492 by Fatih Uzunoglu at 2024-09-13T13:55:22+00:00
qml: consolidate `ListViewExt`'s drag and drop functionality over `TableViewExt`

This makes it possible to not re-invent drop indicator positioning, and also
allows rejecting the drag event when drop is not applicable.

In order to reject drag event when not applicable, there needs to be two
drop areas. This has been the case in `PlaylistListView/Delegate` but not
in `TableViewExt/Delegate`. With this patch, I followed the same approach
used in `PlaylistListView` which has a rather proven track record on drag
and drop functionality.

- - - - -
f482c601 by Fatih Uzunoglu at 2024-09-13T13:55:22+00:00
qml: factorize delegate vertical drop area layout in ListViewExt

- - - - -


6 changed files:

- modules/gui/qt/medialibrary/qml/PlaylistMedia.qml
- modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
- modules/gui/qt/playlist/qml/PlaylistDelegate.qml
- modules/gui/qt/widgets/qml/ListViewExt.qml
- modules/gui/qt/widgets/qml/TableViewDelegate.qml
- modules/gui/qt/widgets/qml/TableViewExt.qml


Changes:

=====================================
modules/gui/qt/medialibrary/qml/PlaylistMedia.qml
=====================================
@@ -98,8 +98,6 @@ MainTableView {
 
     rowHeight: VLCStyle.tableCoverRow_height
 
-    acceptDrop: true
-
     sortModel: (availableRowWidth < VLCStyle.colWidth(4)) ? _modelSmall
                                                           : _modelMedium
 
@@ -121,42 +119,6 @@ MainTableView {
     onActionForSelection: (selection) => model.addAndPlay( selection )
     onItemDoubleClicked: (index, model) => MediaLib.addAndPlay(model.id)
 
-
-    onDropEntered: (delegate, index, drag, before) => {
-        if (!root.model || root.model.transactionPending)
-        {
-            drag.accepted = false
-            return
-        }
-
-        root._dropUpdatePosition(drag, index, delegate, before)
-    }
-
-    onDropUpdatePosition: (delegate, index, drag, before) => {
-        root._dropUpdatePosition(drag, index, delegate, before)
-    }
-
-    onDropExited: (delegate, index, drag, before) => {
-        root.hideLine(delegate)
-    }
-
-    onDropEvent: (delegate, index, drag, drop, before) => {
-        root.applyDrop(drop, index, delegate, before)
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Connections
-    //---------------------------------------------------------------------------------------------
-
-    Connections {
-        target: root
-
-        // NOTE: We want to hide the drop line when scrolling so its position stays relevant.
-        function onContentYChanged() {
-            hideLine(_item)
-        }
-    }
-
     //---------------------------------------------------------------------------------------------
     // Functions
     //---------------------------------------------------------------------------------------------
@@ -176,7 +138,7 @@ MainTableView {
 
     function applyDrop(drop, index, delegate, before) {
         if (listView.isDropAcceptableFunc(drop, index + (before ? 0 : 1)) === false) {
-            root.hideLine(delegate)
+            drop.accepted = false
             return Promise.resolve()
         }
 
@@ -188,14 +150,13 @@ MainTableView {
         if (dragItem === item) {
             model.move(selectionModel.selectedRows(), destinationIndex)
             root.forceActiveFocus()
-            root.hideLine(delegate)
         // NOTE: Dropping medialibrary content into the playlist.
         } else if (Helpers.isValidInstanceOf(item, Widgets.DragItem)) {
             return item.getSelectedInputItem()
                         .then(inputItems => {
                             model.insert(inputItems, destinationIndex)
                         })
-                        .then(() => { root.forceActiveFocus(); root.hideLine(delegate); })
+                        .then(() => { root.forceActiveFocus(); })
         } else if (drop.hasUrls) {
             const urlList = []
             for (let url in drop.urls)
@@ -204,48 +165,11 @@ MainTableView {
             model.insert(urlList, destinationIndex)
 
             root.forceActiveFocus()
-            root.hideLine(delegate)
         }
 
         return Promise.resolve()
     }
 
-    function _dropUpdatePosition(drag, index, delegate, before) {
-        if (listView.isDropAcceptableFunc(drag, index + (before ? 0 : 1)) === false) {
-            root.hideLine(delegate)
-            return
-        }
-
-        root.showLine(delegate, before)
-    }
-
-    //---------------------------------------------------------------------------------------------
-    // Drop line
-
-    function showLine(item, before)
-    {
-        // NOTE: We want to avoid calling mapFromItem too many times.
-        if (_item === item && _before === before)
-            return;
-
-        _item   = item;
-        _before = before;
-
-        if (before)
-            line.y = view.mapFromItem(item, 0, 0).y;
-        else
-            line.y = view.mapFromItem(item, 0, item.height).y;
-    }
-
-    function hideLine(item)
-    {
-        // NOTE: We want to make sure we're not being called after the 'showLine' function.
-        if (_item !== item)
-            return;
-
-        _item = null;
-    }
-
     //---------------------------------------------------------------------------------------------
     // Childs
     //---------------------------------------------------------------------------------------------


=====================================
modules/gui/qt/medialibrary/qml/PlaylistMediaList.qml
=====================================
@@ -408,7 +408,17 @@ MainViewLoader {
 
             header: root.header
 
-            acceptDrop: true
+            listView.isDropAcceptableFunc: function(drag, index) {
+                root._adjustDragAccepted(drag)
+                return drag.accepted
+            }
+
+            listView.acceptDropFunc: function(index, drop) {
+                return new Promise(() => { root._dropAction(drop,
+                                                            listView.itemContainsDrag.index) } )
+            }
+
+            listView.dropIndicator: null
 
             Navigation.parentItem: root
 
@@ -427,14 +437,6 @@ MainViewLoader {
                 contextMenu.popup(selectionModel.selectedRows(), globalMousePos)
             }
 
-            onDropEntered: (_, _, drag, _) => {
-                root._adjustDragAccepted(drag)
-            }
-
-            onDropEvent: (_, index, _, drop, _) => {
-                root._dropAction(drop, index)
-            }
-
             //-------------------------------------------------------------------------------------
             // Childs
 


=====================================
modules/gui/qt/playlist/qml/PlaylistDelegate.qml
=====================================
@@ -38,23 +38,15 @@ T.Control {
 
     readonly property bool selected : view.selectionModel.selectedIndexesFlat.includes(index)
 
-    readonly property bool topContainsDrag: higherDropArea.containsDrag
+    readonly property bool topContainsDrag: dropAreaLayout.higherDropArea.containsDrag
 
-    readonly property bool bottomContainsDrag: lowerDropArea.containsDrag
+    readonly property bool bottomContainsDrag: dropAreaLayout.lowerDropArea.containsDrag
 
     readonly property bool containsDrag: (topContainsDrag || bottomContainsDrag)
 
-    // drag -> point
-    // current drag pos inside the item
-    readonly property point drag: {
-        if (!containsDrag)
-            return Qt.point(0, 0)
-
-        const d = topContainsDrag ? higherDropArea : lowerDropArea
-        const p = d.drag
-        return mapFromItem(d, p.x, p.y)
-    }
-
+    readonly property point dragPosition: mapFromItem(dropAreaLayout,
+                                                      dropAreaLayout.dragPosition.x,
+                                                      dropAreaLayout.dragPosition.y)
 
     // Optional
     property var contextMenu
@@ -63,10 +55,10 @@ T.Control {
     property Item dragItem
 
     // Optional, used to show the drop indicator
-    property var isDropAcceptable
+    property alias isDropAcceptable: dropAreaLayout.isDropAcceptable
 
     // Optional, but required to drop a drag
-    property var acceptDrop
+    property alias acceptDrop: dropAreaLayout.acceptDrop
 
     // Settings
 
@@ -325,56 +317,8 @@ T.Control {
         }
     }
 
-    ColumnLayout {
+    Widgets.ListViewExt.VerticalDropAreaLayout {
+        id: dropAreaLayout
         anchors.fill: parent
-        spacing: 0
-
-        DropArea {
-            id: higherDropArea
-
-            Layout.fillWidth: true
-            Layout.fillHeight: true
-
-            onEntered: (drag) => {
-                if (!acceptDrop) {
-                    drag.accept = false
-                    return
-                }
-
-                if (isDropAcceptable && !isDropAcceptable(drag, index)) {
-                    drag.accepted = false
-                    return
-                }
-            }
-
-            onDropped: (drop) => {
-                console.assert(acceptDrop)
-                acceptDrop(index, drop)
-            }
-        }
-
-        DropArea {
-            id: lowerDropArea
-
-            Layout.fillWidth: true
-            Layout.fillHeight: true
-
-            onEntered: (drag) =>  {
-                if (!acceptDrop) {
-                    drag.accept = false
-                    return
-                }
-
-                if (isDropAcceptable && !isDropAcceptable(drag, index + 1)) {
-                    drag.accepted = false
-                    return
-                }
-            }
-
-            onDropped: (drop) => {
-                console.assert(acceptDrop)
-                acceptDrop(index + 1, drop)
-            }
-        }
     }
 }


=====================================
modules/gui/qt/widgets/qml/ListViewExt.qml
=====================================
@@ -18,6 +18,7 @@
 import QtQuick
 import QtQuick.Controls
 import QtQuick.Window
+import QtQuick.Layouts
 import Qt5Compat.GraphicalEffects
 
 
@@ -106,6 +107,83 @@ ListView {
     
     footer: !!root.acceptDropFunc ? footerDragAccessoryComponent : null
 
+    onItemContainsDragChanged: {
+        if (!dropIndicatorItem && dropIndicator)
+            dropIndicatorItem = dropIndicator.createObject(this)
+    }
+
+    component VerticalDropAreaLayout : ColumnLayout {
+        spacing: 0
+
+        property alias higherDropArea: higherDropArea
+        property alias lowerDropArea: lowerDropArea
+
+        property var isDropAcceptable
+        property var acceptDrop
+
+        readonly property point dragPosition: {
+            let area = null
+
+            if (higherDropArea.containsDrag)
+                area = higherDropArea
+            else if (lowerDropArea.containsDrag)
+                area = lowerDropArea
+            else
+                return Qt.point(0, 0)
+
+            const drag = area.drag
+            return Qt.point(drag.x, drag.y)
+        }
+
+        DropArea {
+            id: higherDropArea
+
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            onEntered: (drag) => {
+                if (!acceptDrop) {
+                    drag.accepted = false
+                    return
+                }
+
+                if (isDropAcceptable && !isDropAcceptable(drag, index)) {
+                    drag.accepted = false
+                    return
+                }
+            }
+
+            onDropped: (drop) => {
+                console.assert(acceptDrop)
+                acceptDrop(index, drop)
+            }
+        }
+
+        DropArea {
+            id: lowerDropArea
+
+            Layout.fillWidth: true
+            Layout.fillHeight: true
+
+            onEntered: (drag) =>  {
+                if (!acceptDrop) {
+                    drag.accepted = false
+                    return
+                }
+
+                if (isDropAcceptable && !isDropAcceptable(drag, index + 1)) {
+                    drag.accepted = false
+                    return
+                }
+            }
+
+            onDropped: (drop) => {
+                console.assert(acceptDrop)
+                acceptDrop(index + 1, drop)
+            }
+        }
+    }
+
     Component {
         id: footerDragAccessoryComponent
 
@@ -135,7 +213,7 @@ ListView {
                 }
             }
 
-            property alias drag: dropArea.drag
+            property alias dragPosition: dropArea.drag
 
             Rectangle {
                 id: firstItemIndicator
@@ -424,7 +502,7 @@ ListView {
             dragging: root.itemContainsDrag !== null
             dragPosProvider: function () {
                 const source = root.itemContainsDrag
-                const point = source.drag
+                const point = source.dragPosition
                 return root.mapFromItem(source, point.x, point.y)
             }
         }
@@ -495,9 +573,7 @@ ListView {
         }
     }
 
-    Rectangle {
-        id: dropIndicator
-
+    property Component dropIndicator: Rectangle {
         parent: {
             const item = root.itemContainsDrag
             if (!item || item.topContainsDrag === undefined || item.bottomContainsDrag === undefined)
@@ -528,6 +604,8 @@ ListView {
         color: theme.accent
     }
 
+    property Item dropIndicatorItem
+
     // FIXME: We probably need to upgrade these RoundButton(s) eventually. And we probably need
     //        to have some kind of animation when switching pages.
 


=====================================
modules/gui/qt/widgets/qml/TableViewDelegate.qml
=====================================
@@ -34,15 +34,24 @@ T.Control {
     required property var sortModel
     required property bool selected
     required property Widgets.DragItem dragItem
-    required property bool acceptDrop
 
-    readonly property bool dragActive: dragHandler.active
-    property alias containsDrag: dropArea.containsDrag
-    property alias drag: dropArea.drag
+    readonly property bool topContainsDrag: dropAreaLayout.higherDropArea.containsDrag
+    readonly property bool bottomContainsDrag: dropAreaLayout.lowerDropArea.containsDrag
+    readonly property bool containsDrag: (topContainsDrag || bottomContainsDrag)
 
     required property real fixedColumnWidth
     required property real weightedColumnWidth
 
+    readonly property point dragPosition: mapFromItem(dropAreaLayout,
+                                                      dropAreaLayout.dragPosition.x,
+                                                      dropAreaLayout.dragPosition.y)
+
+    // Optional, used to show the drop indicator
+    property alias isDropAcceptable: dropAreaLayout.isDropAcceptable
+
+    // Optional, but required to drop a drag
+    property alias acceptDrop: dropAreaLayout.acceptDrop
+
     property int _modifiersOnLastPress: Qt.NoModifier
 
     signal contextMenuButtonClicked(Item menuParent, var menuModel, point globalMousePos)
@@ -51,11 +60,6 @@ T.Control {
 
     signal selectAndFocus(int modifiers, int focusReason)
 
-    signal dropEntered(var drag, bool before)
-    signal dropUpdatePosition(var drag, bool before)
-    signal dropExited(var drag, bool before)
-    signal dropEvent(var drag, var drop, bool before)
-
     property Component defaultDelegate: TableRowDelegate {
         id: defaultDelId
         Widgets.TextAutoScroller {
@@ -82,7 +86,7 @@ T.Control {
 
     hoverEnabled: true
 
-    ListView.delayRemove: dragActive
+    ListView.delayRemove: dragHandler.active
 
     Component.onCompleted: {
         Keys.menuPressed.connect(contextButton.clicked)
@@ -282,24 +286,8 @@ T.Control {
         }
     }
 
-    DropArea {
-        id: dropArea
-
-        enabled: delegate.acceptDrop
-
+    Widgets.ListViewExt.VerticalDropAreaLayout {
+        id: dropAreaLayout
         anchors.fill: parent
-
-        function isBefore(drag) {
-            return drag.y < height/2
-        }
-
-        onEntered: (drag) => { delegate.dropEntered(drag, isBefore(drag)) }
-
-        onPositionChanged: (drag) => { delegate.dropUpdatePosition(drag, isBefore(drag)) }
-
-        onExited:delegate.dropExited(drag, isBefore(drag))
-
-        onDropped: (drop) => { delegate.dropEvent(drag, drop, isBefore(drag)) }
-
     }
 }


=====================================
modules/gui/qt/widgets/qml/TableViewExt.qml
=====================================
@@ -99,7 +99,6 @@ FocusScope {
     property real availableRowWidth: 0
 
     property Widgets.DragItem dragItem: null
-    property bool acceptDrop: false
 
     // Private
 
@@ -162,11 +161,6 @@ FocusScope {
     signal rightClick(Item menuParent, var menuModel, point globalMousePos)
     signal itemDoubleClicked(var index, var model)
 
-    signal dropUpdatePosition(Item delegate, int index, var drag, bool before)
-    signal dropEntered(Item delegate, int index, var drag, bool before)
-    signal dropExited(Item delegate, int index,  var drag, bool before)
-    signal dropEvent(Item delegate, int index,  var drag, var drop, bool before)
-
     // Events
 
     Component.onCompleted: {
@@ -413,8 +407,6 @@ FocusScope {
 
             selected: selectionModel.selectedIndexesFlat.includes(index)
 
-            acceptDrop: root.acceptDrop
-
             onContextMenuButtonClicked: (menuParent, menuModel, globalMousePos) => {
                 root.contextMenuButtonClicked(menuParent, menuModel, globalMousePos)
             }
@@ -425,18 +417,8 @@ FocusScope {
                 root.itemDoubleClicked(index, model)
             }
 
-            onDropEntered: (drag, before) => {
-                root.dropEntered(tableDelegate, index, drag, before)
-            }
-            onDropUpdatePosition:  (drag, before) => {
-                root.dropUpdatePosition(tableDelegate, index, drag, before)
-            }
-            onDropExited:  (drag, before) => {
-                root.dropExited(tableDelegate, index, drag, before)
-            }
-            onDropEvent:  (drag, drop, before) => {
-                root.dropEvent(tableDelegate, index, drag, drop, before)
-            }
+            isDropAcceptable: view.isDropAcceptableFunc
+            acceptDrop: view.acceptDropFunc
 
             onSelectAndFocus: (modifiers, focusReason) => {
                 selectionModel.updateSelection(modifiers, view.currentIndex, index)



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3855b4610654aa4b926061e0e9d66f64566ca6f4...f482c6017586b5242ba6422d3b6cc43c3d3b11ac

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/3855b4610654aa4b926061e0e9d66f64566ca6f4...f482c6017586b5242ba6422d3b6cc43c3d3b11ac
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