在 Qml 中拖放多个项目
Drag and drop multiple items in Qml
我需要在我的应用程序中绘制一堆矩形。用户应该能够 select 他们中的每一个单独并自由移动他们并改变他们的位置。用户还应该能够 select 多个矩形并同时移动所有 selected 一个并在其他地方释放它们。
我已经可以实现基于 Gridview 的东西,它可以处理 selection 和一个矩形的移动,但我不能让它适用于多个 selection/movement。这是我目前拥有的一段代码:
GridView {
id: mainGrid
cellWidth: 7;
cellHeight: 7;
ListModel {
id: myModel
function createModel() {
for(var i = 0; i < totalZoneList.length; i++)
{
for (var j = 0; j < moduleZoneList.length; j++)
{
myModel.append({"item1": ITEM1, "item2": ITEM2})
}
}
}
Component.onCompleted: {createModel()}
}
Component {
id: myblocks
Item {
id: item
width: mainGrid.cellWidth;
height: mainGrid.cellHeight;
Rectangle {
id: box
parent: mainGrid
x: //CALCULATED BASED ON MODEL
y: //CALCULATED BASED ON MODEL
width: //CALCULATED BASED ON MODEL
height: //CALCULATED BASED ON MODEL
MouseArea {
id: gridArea
anchors.fill: parent
hoverEnabled: true
drag.axis: Drag.XandYAxis
drag.minimumX: 0
drag.minimumY: 0
property int mX: (mouseX < 0) ? 0 : ((mouseX < mainGrid.width - mainGrid.cellWidth) ? mouseX : mainGrid.width - mainGrid.cellWidth)
property int mY: (mouseY < 0) ? 0 : ((mouseY < mainGrid.height - mainGrid.cellHeight) ? mouseY : mainGrid.height - mainGrid.cellHeight)
property int index: parseInt(mX/mainGrid.cellWidth) + 5*parseInt(mY/mainGrid.cellHeight) //item underneath cursor
property int activeIndex
property var xWhenPressed
property var yWhenPressed
propagateComposedEvents: true
onPressed: {
activeIndex = index
drag.target = box
xWhenPressed = box.x
yWhenPressed = box.y
gridArea.drag.maximumX = mainGrid.width - box.width
gridArea.drag.maximumY = mainGrid.height - box.height
}
onReleased: {
if(xWhenPressed !== box.x || yWhenPressed !== box.y)
{
//RECALCULATE THE POSITION
}
}
onPositionChanged: {
if (drag.active && index !== -1 && index !== activeIndex) {
mainGrid.model.move(activeIndex, activeIndex = index, 1)
}
}
} // Mousearea
} // Rectangle
} // Item
} // Component
} //mainGrid
我没能让你的代码正常工作。首先,我看到上面的错误:
Rectangle {
id: box
parent: mainGrid
...
}
你只需要删除没有用的父 Item
,并将 Rectangle
设置为委托的根。
那你忘了说拖动的目标是Rectangle
drag.target: parent
修改后的代码如下:
Component {
id: myblocks
Rectangle {
id: box
color: "red"
width: 20
height: 20
MouseArea {
id: gridArea
anchors.fill: parent
drag.target: parent
hoverEnabled: true
drag.axis: Drag.XandYAxis
} // Mousearea
} // Rectangle
} // Component
那么,您不应该使用 GridView
,因为您希望移动元素。如果您使用 Repeater
它会起作用,您只需在 Rectangle
中设置 x
和 y
以将元素放在开头。
现在这是解决您的问题的方法:您单击一个元素以 select 它,然后您可以一次移动所有 selected 项目。
Window {
visible: true
width: 640
height: 480
property var totalZoneList: ["total1", "total2"]
property var moduleZoneList: ["module1", "module2"]
Repeater{
id: iRepeater
model: ListModel {
id: myModel
function createModel() {
for(var i = 0; i < totalZoneList.length; i++)
{
for (var j = 0; j < moduleZoneList.length; j++)
{
myModel.append({"item1": totalZoneList[i], "item2": moduleZoneList[j]})
}
}
}
Component.onCompleted: {createModel()}
}
delegate: myblocks
}
Component {
id: myblocks
Rectangle {
id: box
color: {
switch(index){
case 0: selected ? "red" : "#FF9999";break;
case 1: selected ? "blue" : "lightblue";break;
case 2: selected ? "green" : "lightgreen";break;
case 3: selected ? "grey" : "lightgrey";break;
}
}
x: (width + 5)*index
width: 20
height: 20
property int offsetX:0
property int offsetY:0
property bool selected: false
function setRelative(pressedRect){
disableDrag();
x = Qt.binding(function (){ return pressedRect.x + offsetX; })
y = Qt.binding(function (){ return pressedRect.y + offsetY; })
}
function enableDrag(){
gridArea.drag.target = box
}
function disableDrag(){
gridArea.drag.target = null
}
MouseArea {
id: gridArea
anchors.fill: parent
hoverEnabled: true
drag.axis: Drag.XandYAxis
onClicked: parent.selected=!parent.selected
onPressed: {
var pressedRect = iRepeater.itemAt(index);
if (pressedRect.selected == true){
for (var i=0; i<iRepeater.count; i++ ){
var rect = iRepeater.itemAt(i);
if (i != index){
//init for breaking existing binding
rect.x = rect.x
rect.y = rect.y
rect.disableDrag()
if (rect.selected == true){
rect.offsetX = rect.x - pressedRect.x
rect.offsetY = rect.y - pressedRect.y
rect.setRelative(pressedRect)
}
}
}
pressedRect.enableDrag()
}
}
} // Mousearea
} // Rectangle
} // Component
}
食谱一:
使用一个Repeater
,所以定位不是由视图决定的,而是你自己决定的。
使用隐形帮手Item
。这是你的 drag.target
.
实现您选择对象的首选方式 - 通过单击天气或绘制框并检查此框中包含哪些对象。使所有选定对象相对于您的不可见辅助对象的位置。
拖动辅助对象,并相应地移动所有其他对象。
完成后,再次取消选择对象并使其位置绝对化(在父坐标系内)
食谱二:
将所有选定对象重新设置为新项目,同时相应地映射它们的坐标
移动新项目
将所有对象重新映射回原始状态canvas,将它们的坐标映射回来。
我希望这足以解决您的问题(据我了解)
如果它解决了另一个问题,那么您需要更具体地说明拖动对象的预期行为。
我需要在我的应用程序中绘制一堆矩形。用户应该能够 select 他们中的每一个单独并自由移动他们并改变他们的位置。用户还应该能够 select 多个矩形并同时移动所有 selected 一个并在其他地方释放它们。 我已经可以实现基于 Gridview 的东西,它可以处理 selection 和一个矩形的移动,但我不能让它适用于多个 selection/movement。这是我目前拥有的一段代码:
GridView {
id: mainGrid
cellWidth: 7;
cellHeight: 7;
ListModel {
id: myModel
function createModel() {
for(var i = 0; i < totalZoneList.length; i++)
{
for (var j = 0; j < moduleZoneList.length; j++)
{
myModel.append({"item1": ITEM1, "item2": ITEM2})
}
}
}
Component.onCompleted: {createModel()}
}
Component {
id: myblocks
Item {
id: item
width: mainGrid.cellWidth;
height: mainGrid.cellHeight;
Rectangle {
id: box
parent: mainGrid
x: //CALCULATED BASED ON MODEL
y: //CALCULATED BASED ON MODEL
width: //CALCULATED BASED ON MODEL
height: //CALCULATED BASED ON MODEL
MouseArea {
id: gridArea
anchors.fill: parent
hoverEnabled: true
drag.axis: Drag.XandYAxis
drag.minimumX: 0
drag.minimumY: 0
property int mX: (mouseX < 0) ? 0 : ((mouseX < mainGrid.width - mainGrid.cellWidth) ? mouseX : mainGrid.width - mainGrid.cellWidth)
property int mY: (mouseY < 0) ? 0 : ((mouseY < mainGrid.height - mainGrid.cellHeight) ? mouseY : mainGrid.height - mainGrid.cellHeight)
property int index: parseInt(mX/mainGrid.cellWidth) + 5*parseInt(mY/mainGrid.cellHeight) //item underneath cursor
property int activeIndex
property var xWhenPressed
property var yWhenPressed
propagateComposedEvents: true
onPressed: {
activeIndex = index
drag.target = box
xWhenPressed = box.x
yWhenPressed = box.y
gridArea.drag.maximumX = mainGrid.width - box.width
gridArea.drag.maximumY = mainGrid.height - box.height
}
onReleased: {
if(xWhenPressed !== box.x || yWhenPressed !== box.y)
{
//RECALCULATE THE POSITION
}
}
onPositionChanged: {
if (drag.active && index !== -1 && index !== activeIndex) {
mainGrid.model.move(activeIndex, activeIndex = index, 1)
}
}
} // Mousearea
} // Rectangle
} // Item
} // Component
} //mainGrid
我没能让你的代码正常工作。首先,我看到上面的错误:
Rectangle {
id: box
parent: mainGrid
...
}
你只需要删除没有用的父 Item
,并将 Rectangle
设置为委托的根。
那你忘了说拖动的目标是Rectangle
drag.target: parent
修改后的代码如下:
Component {
id: myblocks
Rectangle {
id: box
color: "red"
width: 20
height: 20
MouseArea {
id: gridArea
anchors.fill: parent
drag.target: parent
hoverEnabled: true
drag.axis: Drag.XandYAxis
} // Mousearea
} // Rectangle
} // Component
那么,您不应该使用 GridView
,因为您希望移动元素。如果您使用 Repeater
它会起作用,您只需在 Rectangle
中设置 x
和 y
以将元素放在开头。
现在这是解决您的问题的方法:您单击一个元素以 select 它,然后您可以一次移动所有 selected 项目。
Window {
visible: true
width: 640
height: 480
property var totalZoneList: ["total1", "total2"]
property var moduleZoneList: ["module1", "module2"]
Repeater{
id: iRepeater
model: ListModel {
id: myModel
function createModel() {
for(var i = 0; i < totalZoneList.length; i++)
{
for (var j = 0; j < moduleZoneList.length; j++)
{
myModel.append({"item1": totalZoneList[i], "item2": moduleZoneList[j]})
}
}
}
Component.onCompleted: {createModel()}
}
delegate: myblocks
}
Component {
id: myblocks
Rectangle {
id: box
color: {
switch(index){
case 0: selected ? "red" : "#FF9999";break;
case 1: selected ? "blue" : "lightblue";break;
case 2: selected ? "green" : "lightgreen";break;
case 3: selected ? "grey" : "lightgrey";break;
}
}
x: (width + 5)*index
width: 20
height: 20
property int offsetX:0
property int offsetY:0
property bool selected: false
function setRelative(pressedRect){
disableDrag();
x = Qt.binding(function (){ return pressedRect.x + offsetX; })
y = Qt.binding(function (){ return pressedRect.y + offsetY; })
}
function enableDrag(){
gridArea.drag.target = box
}
function disableDrag(){
gridArea.drag.target = null
}
MouseArea {
id: gridArea
anchors.fill: parent
hoverEnabled: true
drag.axis: Drag.XandYAxis
onClicked: parent.selected=!parent.selected
onPressed: {
var pressedRect = iRepeater.itemAt(index);
if (pressedRect.selected == true){
for (var i=0; i<iRepeater.count; i++ ){
var rect = iRepeater.itemAt(i);
if (i != index){
//init for breaking existing binding
rect.x = rect.x
rect.y = rect.y
rect.disableDrag()
if (rect.selected == true){
rect.offsetX = rect.x - pressedRect.x
rect.offsetY = rect.y - pressedRect.y
rect.setRelative(pressedRect)
}
}
}
pressedRect.enableDrag()
}
}
} // Mousearea
} // Rectangle
} // Component
}
食谱一:
使用一个
Repeater
,所以定位不是由视图决定的,而是你自己决定的。使用隐形帮手
Item
。这是你的drag.target
.实现您选择对象的首选方式 - 通过单击天气或绘制框并检查此框中包含哪些对象。使所有选定对象相对于您的不可见辅助对象的位置。
拖动辅助对象,并相应地移动所有其他对象。
完成后,再次取消选择对象并使其位置绝对化(在父坐标系内)
食谱二:
将所有选定对象重新设置为新项目,同时相应地映射它们的坐标
移动新项目
将所有对象重新映射回原始状态canvas,将它们的坐标映射回来。
我希望这足以解决您的问题(据我了解) 如果它解决了另一个问题,那么您需要更具体地说明拖动对象的预期行为。