QML拖放ListView始终在第一个元素上

huangapple go评论142阅读模式
英文:

QML Drag-and-drop ListView always on first element

问题

我需要能够移动ListView的行,例如:如果我将第2行拖动到第4项上,则应在第4项之前插入。我遵循了多个示例,但我唯一能够使拖动工作的事情如下所示,但是DropArea始终位于第一个元素之上。这是我的代码,只去掉了样式:

DragDropEx.qml

  1. import QtQuick 2.4
  2. import QtQuick.Window 2.2
  3. import QtQml.Models 2.1
  4. Window {
  5. width: 784; height: 250; visible: true;
  6. Flickable {
  7. anchors.fill: parent
  8. property var listOfStrings: ["Hello", "world", "I", "need", "help"]
  9. ListModel { id: listOfStringsModel }
  10. Component.onCompleted: {
  11. if (visible) {
  12. listOfStringsModel.clear();
  13. for (var i = 0; i < listOfStrings.length; i++) {
  14. listOfStringsModel.append({ "textEntry": listOfStrings[i] });
  15. }
  16. textListView.currentIndex = listOfStrings.length - 1;
  17. }
  18. }
  19. Rectangle {
  20. anchors.fill: parent; color: "lightblue"
  21. ListView {
  22. id: textListView
  23. x: 10; y: 10; width: parent.width - 20; height: parent.height - 20
  24. spacing: 4
  25. model: DelegateModel {
  26. id: visualModel
  27. model: listOfStringsModel
  28. delegate: TextListDelegate {
  29. id: textListDelegateItem
  30. height: 32; width: parent.width - 16
  31. anchors { horizontalCenter: parent.horizontalCenter }
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }

TextListDelegate.qml

  1. import QtQuick 2.10
  2. import QtQuick.Controls 2.3
  3. import QtQml.Models 2.1
  4. MouseArea {
  5. id: delegateRoot
  6. property bool held: false
  7. property int visualIndex: DelegateModel.itemsIndex
  8. drag.target: held ? textEntryContainerRect : undefined
  9. drag.axis: Drag.YAxis
  10. drag.filterChildren: true // this is because of the text field
  11. onPressAndHold: {
  12. held = true;
  13. textEntryContainerRect.opacity = 0.5;
  14. }
  15. onReleased: {
  16. if (held === true) {
  17. held = false;
  18. textEntryContainerRect.opacity = 1;
  19. textEntryContainerRect.Drag.drop();
  20. }
  21. }
  22. Rectangle {
  23. id: textEntryContainerRect
  24. anchors.fill: parent
  25. Drag.active: delegateRoot.drag.active
  26. Drag.source: delegateRoot
  27. Drag.hotSpot.x: textListDelegateItem.width / 2
  28. Drag.hotSpot.y: textListDelegateItem.height / 2
  29. states: [
  30. State {
  31. when: textEntryContainerRect.Drag.active
  32. ParentChange {
  33. target: textEntryContainerRect
  34. parent: textListView
  35. }
  36. AnchorChanges {
  37. target: textEntryContainerRect
  38. anchors.horizontalCenter: undefined
  39. anchors.verticalCenter: undefined
  40. }
  41. }
  42. ]
  43. TextField {
  44. id: textListEntryField
  45. anchors { fill: parent; leftMargin: 8 }
  46. height: 30; text: textEntry
  47. }
  48. }
  49. DropArea {
  50. id: dropArea
  51. anchors { fill: parent; }
  52. onDropped: {
  53. var sourceIndex = drag.source.visualIndex;
  54. var targetIndex = delegateRoot.visualIndex;
  55. listOfStringsModel.move(sourceIndex, targetIndex, 1);
  56. }
  57. Rectangle {
  58. anchors.fill: parent
  59. color: "hotpink"
  60. visible: parent.containsDrag
  61. }
  62. }
  63. }

我为了显示DropArea已经上色,以显示每次我按住并尝试移动行时它都顽固地跳到第一个元素。如何使它跳转到鼠标所在的行,类似于网上的所有示例?

英文:

I need to be able to move ListView rows, example: if I drag row 2 over item 4, it should insert before item 4. I followed multiple examples, the only thing I could make dragging work shows below, but the DropArea is always over the first element. This is my code, only with styling removed:

DragDropEx.qml

  1. import QtQuick 2.4
  2. import QtQuick.Window 2.2
  3. import QtQml.Models 2.1
  4. Window {
  5. width: 784; height: 250; visible: true;
  6. Flickable {
  7. anchors.fill: parent
  8. property var listOfStrings: [&quot;Hello&quot;, &quot;world&quot;, &quot;I&quot;, &quot;need&quot;, &quot;help&quot;]
  9. ListModel { id: listOfStringsModel }
  10. Component.onCompleted: {
  11. if (visible) {
  12. listOfStringsModel.clear();
  13. for (var i = 0; i &lt; listOfStrings.length; i++) {
  14. listOfStringsModel.append({ &quot;textEntry&quot;: listOfStrings[i] });
  15. }
  16. textListView.currentIndex = listOfStrings.length - 1;
  17. }
  18. }
  19. Rectangle {
  20. anchors.fill: parent; color: &quot;lightblue&quot;
  21. ListView {
  22. id: textListView
  23. x: 10; y: 10; width: parent.width - 20; height: parent.height - 20
  24. spacing: 4
  25. model: DelegateModel {
  26. id: visualModel
  27. model: listOfStringsModel
  28. delegate: TextListDelegate {
  29. id: textListDelegateItem
  30. height: 32; width: parent.width - 16
  31. anchors { horizontalCenter: parent.horizontalCenter }
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }

TextListDelegate.qml

  1. import QtQuick 2.10
  2. import QtQuick.Controls 2.3
  3. import QtQml.Models 2.1
  4. MouseArea {
  5. id: delegateRoot
  6. property bool held: false
  7. property int visualIndex: DelegateModel.itemsIndex
  8. drag.target: held ? textEntryContainerRect : undefined
  9. drag.axis: Drag.YAxis
  10. drag.filterChildren: true // this is because of the text field
  11. onPressAndHold: {
  12. held = true;
  13. textEntryContainerRect.opacity = 0.5;
  14. }
  15. onReleased: {
  16. if (held === true) {
  17. held = false;
  18. textEntryContainerRect.opacity = 1;
  19. textEntryContainerRect.Drag.drop();
  20. }
  21. }
  22. Rectangle {
  23. id: textEntryContainerRect
  24. anchors.fill: parent
  25. Drag.active: delegateRoot.drag.active
  26. Drag.source: delegateRoot
  27. Drag.hotSpot.x: textListDelegateItem.width / 2
  28. Drag.hotSpot.y: textListDelegateItem.height / 2
  29. states: [
  30. State {
  31. when: textEntryContainerRect.Drag.active
  32. ParentChange {
  33. target: textEntryContainerRect
  34. parent: textListView
  35. }
  36. AnchorChanges {
  37. target: textEntryContainerRect
  38. anchors.horizontalCenter: undefined
  39. anchors.verticalCenter: undefined
  40. }
  41. }
  42. ]
  43. TextField {
  44. id: textListEntryField
  45. anchors { fill: parent; leftMargin: 8 }
  46. height: 30; text: textEntry
  47. }
  48. }
  49. DropArea {
  50. id: dropArea
  51. anchors { fill: parent; }
  52. onDropped: {
  53. var sourceIndex = drag.source.visualIndex;
  54. var targetIndex = delegateRoot.visualIndex;
  55. listOfStringsModel.move(sourceIndex, targetIndex, 1);
  56. }
  57. Rectangle {
  58. anchors.fill: parent
  59. color: &quot;hotpink&quot;
  60. visible: parent.containsDrag
  61. }
  62. }
  63. }

I colored the drop area to show that it is stubbornly jumping to first element every time I press and hold, then try to move a row. How can I make it go to the row where the mouse is , similar to all examples on the web ?

Qt version: 5.12 - 5.17

Notes:

  • the DelegateModel was used in this Qt tutorial which is my most successful version.

  • press-and-hold needed for embedded

  • TextField is accompanied by other controls, and the model has other fields, that is why it may look more complicated than needed, solution needs to work with them.

答案1

得分: 0

我尝试为您创建了一个最小的ListView / DelegateModel拖放示例。

我删除了Flickable,因为我不确定它的作用,而且您已经有ListView和带有拖动的MouseArea,所以我不想在那里加入Flickable以免混淆事情。

我保留了您的ListModel,但缩短了填充它所需的代码行数。

我根据Qt的文档 https://doc.qt.io/qt-6/qml-qtquick-drag.html 重新编写了您的代理,其中显示了一个父项,其中包含您的DropArea和TextField。在TextField内部,您有一个MouseArea。这种排列很重要,因为父项是不可见的且不可移动的。当您完成拖动操作时,您希望TextField回到原位,即重置'y'位置,因为我们将在视觉模型中移动它,我们希望撤消拖动操作。

我删除了DelegateModel的所有痕迹。正确使用DelegateModel涉及对DelegateModelGroup的操作。看到您的示例实际上操作了底层的ListModel,我觉得在这里没有真正需要使用DelegateModel,而且删除它的好处是大大减少了示例的代码复杂性。

我将visualIndex重命名为index / _index,因为这是为所有代理提供的内置索引。跟踪此索引最终将使我们能够在拖放完成后正确调用move来获取源ListModel中的记录。

之后,我通过MouseArea的onReleased和相应的DropArea的onDropped将其连接起来,对于您来说基本是正确的。我将sourceIndex和targetIndex从变量更改为属性,因为在调试时,我发现将这些值属性绑定很有用(我已经在最终结果中删除了调试,但保留了这些属性,因为它使代码看起来更精简)。

我发现在早期版本中,代理的z顺序堆叠在一起,因此拖动的项目并不总是位于顶部。为了解决这个问题,我需要提升与当前正在拖动的项目对应的代理的z顺序。

最后,不清楚您使用的Qt版本是哪个,但我选择使用Qt6语法来缩短我的导入。

  1. import QtQuick
  2. import QtQuick.Controls
  3. import QtQuick.Layouts
  4. Page {
  5. Rectangle { anchors.fill: parent; color: "lightblue" }
  6. property var listOfStrings: ["Hello", "world", "I", "need", "help"]
  7. ListModel { id: listOfStringsModel }
  8. Component.onCompleted: {
  9. for (let textEntry of listOfStrings) listOfStringsModel.append({textEntry});
  10. }
  11. ListView {
  12. anchors { fill: parent; margins: 10 }
  13. spacing: 4
  14. model: listOfStringsModel
  15. delegate: TextListDelegate { }
  16. }
  17. }
  18. // TextListDelegate.qml
  19. import QtQuick
  20. import QtQuick.Controls
  21. import QtQml.Models
  22. Rectangle {
  23. height: 32
  24. width: ListView.view.width
  25. z: dragArea.drag.active ? 1 : 0
  26. border.color: "grey"
  27. DropArea {
  28. id: dropArea
  29. property int sourceIndex: drag.source._index
  30. property int targetIndex: index
  31. anchors { fill: parent; }
  32. onDropped: listOfStringsModel.move(sourceIndex, targetIndex, 1);
  33. Rectangle {
  34. anchors.fill: parent
  35. visible: parent.containsDrag
  36. color: "steelblue"
  37. opacity: 0.5
  38. Label { anchors.centerIn: parent; text: dropArea.sourceIndex + " -> " + dropArea.targetIndex }
  39. }
  40. }
  41. Label {
  42. id: label
  43. width: parent.width
  44. height: parent.height
  45. property int _index: index
  46. text: textEntry
  47. Drag.active: dragArea.drag.active
  48. Drag.hotSpot.x: 10
  49. Drag.hotSpot.y: 10
  50. MouseArea {
  51. id: dragArea
  52. anchors.fill: parent
  53. drag.target: parent
  54. drag.axis: Drag.YAxis
  55. onReleased: { parent.Drag.drop(); parent.y = 0; }
  56. }
  57. }
  58. }

您可以[在线试用!](https://stephenquan.github.io/qmlonline/?zcode=AAAGpXiclVTbbtswDH3PVxB+StFObXZ5cbANRTNgAVJsbYcBwzAMis04whTJk5WLW/jfR8mXWJkLbH4wpENSPCSPJDa5Nhbu7N1WJL9GItiyG62s0bI4xRe81FtbjD7zDOFpBPTdY2K5yiTtgatkrU3BVkLKGHJuUNkpJFpqE0MkRba2S7nFCCofmhudo7El7LgBKQr7afVgjVBZEcP36CNKqaMLiPbayNQt5u6nEP1mjTKPfvhzFhR6q1OUREGkcXhUbagT3mgqRxEpppVbS7RI/nUh7ltpA2NCweLBfqAelKBX4XlnA8cznueo0vFTF1adTf2hVUfwq8B9L1PTKmIcNmvDTSYUNWBy1QS7r8h5QtlieA0dtnGZh4rtPGiNGbcYwxfi5UjMGoTSVg2/ajS6vPzLgf3eyJPh/4NGNpJ5BsWopwqfZ41u+DG8eum3e5Haddz1he3cz4Pe/BhDanh2bZAzt2A8sWKH8B4mEMOV91lqk6Jhrbgyg2XkDTNSlYvsNduJIm3gDuzUJ5SFQm9NgnOV4qHOzWqE/RQOGw6yNCu0TVDo99x4jyPVyhHNcVCvbKN3OO6Ruugnu4BJI6+TC9hhPQrBZQwcdqIQS4mtjVqpLCftzaj8wLHtcWERpb/Ag

英文:

I tried to create a minimal ListView / DelegateModel drag-n-drop example for you.

I removed the Flickable, I wasn't sure what the purpose it had, and, you already had ListView and MouseArea with dragging so I didn't really want Flickable there confusing things.

I kept your ListModel, but, I shortened the lines of code needed to populate it.

I rewrote your delegate based on Qt's documentation https://doc.qt.io/qt-6/qml-qtquick-drag.html which showed a parent Item of which inside it you have your DropArea and the TextField. Inside the TextField you have a MouseArea. This arrangement is important because the parent Item is invisible an immovable. When you finish your drag action the TextField you want to snap back into place, i.e. reset the 'y' position, since, we're going to move it in the visual model, we want to undo the move from the drag.

I remove all traces on DelegateModel. Correct usage of DelegateModel involves manipulation of a DelegateModelGroup. Seeing your example really manipulates the underlying ListModel I felt that there was no real need for DelegateModel here, and, a benefit of removing it is it reduces the code complexity of the example dramatically.

I renamed visualIndex to just index / _index. Since this is the built-in index given to all delegates. Tracking this index will ultimately give us the record in the source ListModel which will enable us to call move correctly after the drag-drop is completed.

After that, I wired it up with a minimal implementation on MouseArea's onReleased and the corresponding DropArea onDropped which, for you pretty much had right. I changed the sourceIndex and targetIndex from being a variable to being a property since, when I was debugging I found it useful to property bind to these values so I can see them (I've removed the debugging in the final result but I've kept those as properties since it made the code look leaner).

I found in earlier versions that the delegates the z-order was stacked so that the dragged item wasn't always on top. In order to fix that problem I needed to elevate the z-order of the delegate corresponding to the current item currently being dragged.

Lastly, it wasn't clear which version of Qt you were using, but, I've opted to use Qt6 syntax to shorten my imports.

  1. import QtQuick
  2. import QtQuick.Controls
  3. import QtQuick.Layouts
  4. Page {
  5. Rectangle { anchors.fill: parent; color: &quot;lightblue&quot; }
  6. property var listOfStrings: [&quot;Hello&quot;, &quot;world&quot;, &quot;I&quot;, &quot;need&quot;, &quot;help&quot;]
  7. ListModel { id: listOfStringsModel }
  8. Component.onCompleted: {
  9. for (let textEntry of listOfStrings) listOfStringsModel.append({textEntry});
  10. }
  11. ListView {
  12. anchors { fill: parent; margins: 10 }
  13. spacing: 4
  14. model: listOfStringsModel
  15. delegate: TextListDelegate { }
  16. }
  17. }
  18. // TextListDelegate.qml
  19. import QtQuick
  20. import QtQuick.Controls
  21. import QtQml.Models
  22. Rectangle {
  23. height: 32
  24. width: ListView.view.width
  25. z: dragArea.drag.active ? 1 : 0
  26. border.color: &quot;grey&quot;
  27. DropArea {
  28. id: dropArea
  29. property int sourceIndex: drag.source._index
  30. property int targetIndex: index
  31. anchors { fill: parent; }
  32. onDropped: listOfStringsModel.move(sourceIndex, targetIndex, 1);
  33. Rectangle {
  34. anchors.fill: parent
  35. visible: parent.containsDrag
  36. color: &quot;steelblue&quot;
  37. opacity: 0.5
  38. Label { anchors.centerIn: parent; text: dropArea.sourceIndex + &quot; -&gt; &quot; + dropArea.targetIndex }
  39. }
  40. }
  41. Label {
  42. id: label
  43. width: parent.width
  44. height: parent.height
  45. property int _index: index
  46. text: textEntry
  47. Drag.active: dragArea.drag.active
  48. Drag.hotSpot.x: 10
  49. Drag.hotSpot.y: 10
  50. MouseArea {
  51. id: dragArea
  52. anchors.fill: parent
  53. drag.target: parent
  54. drag.axis: Drag.YAxis
  55. onReleased: { parent.Drag.drop(); parent.y = 0; }
  56. }
  57. }
  58. }

You can Try it Online!

huangapple
  • 本文由 发表于 2023年7月13日 22:47:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680727.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定