当父级的位置在拖动过程中变化时,如何在不忽视的情况下制作项目可拖动

发布于 2025-02-04 09:11:31 字数 1183 浏览 1 评论 0 原文

我正在尝试制作一个可以通过其边缘调整大小的项目。

为了显示问题的最小测试柜,足以拥有左边缘可拖动,因此,这里是:

Rectangle {
    id: root
    border.width: 1
    border.color: 'black'
    color: 'red'

    // save original position and size at drag start
    property real origX: 0
    property real origWidth: 0

    // drag this item:
    Item {
        id: dragDummy
        x: 0
        onXChanged: {
            root.x = root.origX + x
            root.width = root.origWidth - x
        }
    }

    MouseArea {
        anchors.fill: root
        drag.target: dragDummy
        drag.axis: Drag.XAxis
        drag.onActiveChanged: {
            // onDragStarted -> Cannot assign to non-existent property "onDragStarted" ???
            if(!active) return
            root.origX = root.x
            root.origWidth = root.width
        }
    }
}

问题似乎是,如果拖动导致父位置更改,则会触发另一个拖动事件,引起此闪烁:

我猜莫塞雷亚在这里无能为力吗?然后应像“老式”应用程序中使用低级别的鼠标事件(即在根项目中捕获事件,手动计算有关初始鼠标倒下位置等的偏移等等。)?

(或者我必须将Mousearea移到不会在阻力过程中移动的祖先,这几乎是一样的...)

I'm trying to make an item that can be resized by its edges.

For showing a minimal testcase of the problem it is enough to have its left edge draggable, so here it is:

Rectangle {
    id: root
    border.width: 1
    border.color: 'black'
    color: 'red'

    // save original position and size at drag start
    property real origX: 0
    property real origWidth: 0

    // drag this item:
    Item {
        id: dragDummy
        x: 0
        onXChanged: {
            root.x = root.origX + x
            root.width = root.origWidth - x
        }
    }

    MouseArea {
        anchors.fill: root
        drag.target: dragDummy
        drag.axis: Drag.XAxis
        drag.onActiveChanged: {
            // onDragStarted -> Cannot assign to non-existent property "onDragStarted" ???
            if(!active) return
            root.origX = root.x
            root.origWidth = root.width
        }
    }
}

the problem seems to be that if drag causes parent position to change, that triggers another drag event, causing this flicker:

screenshot

I'm guessing MouseArea can't help here? Then low level mouse events should be used like in "old-school" apps (i.e. capturing events at root Item, manually compute offset with respect to initial mouse down position, etc...)?

(or I have to move the MouseArea to an ancestor that won't move during drag, which is almost the same...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清晨说晚安 2025-02-11 09:11:31

有一个不错的QML项目类型,称为 dragHandler ,人们经常忽略它,但我发现它效果很好。

该解决方案比其他建议更惯用,因为它使用声明性的样式而不是命令:

import QtQuick 2.15

Item {
    id: root
    width: 500
    height: 100
    Item {
        height: 100
        width: handle.x + handle.width / 2
    }
    Rectangle {
        x: handle.x + handle.width / 2
        width: root.width - (handle.x - handle.width/2)
        height: 100
        border{
            width: 1
            color: 'black'
        }
        color: 'red'
    }
    Item {
        id: handle
        x: -width / 2
        width: 50
        height: 100
        DragHandler {
            yAxis.enabled: false
            xAxis{
                minimum: -handle.width
                maximum: root.width
            }
        }
    }
}

There is a nice QML Item type called DragHandler which people often overlook, but I find that it works very well.

This solution is a little more idiomatic than other suggestions in that it uses a declarative style rather than imperative:

import QtQuick 2.15

Item {
    id: root
    width: 500
    height: 100
    Item {
        height: 100
        width: handle.x + handle.width / 2
    }
    Rectangle {
        x: handle.x + handle.width / 2
        width: root.width - (handle.x - handle.width/2)
        height: 100
        border{
            width: 1
            color: 'black'
        }
        color: 'red'
    }
    Item {
        id: handle
        x: -width / 2
        width: 50
        height: 100
        DragHandler {
            yAxis.enabled: false
            xAxis{
                minimum: -handle.width
                maximum: root.width
            }
        }
    }
}
美人骨 2025-02-11 09:11:31

我提出的解决方案包括有两个 mousearea s:

  • a mousearea 与要拖动的项目移动,仅用于命中测试,因此其 Onpressed 处理程序是这样的:
    onPressed: (mouse) => {
        mouse.accepted = false
        root.container.myDragTarget = root
    }
    onReleased: root.container.myDragTarget = null
  • 另一个 mousearea ,堆叠在其他下方, 移动,处理鼠标位置更改和拖动:
    onPressed: _start = Qt.point(mouseX, mouseY)
    onPositionChanged: {
        if(myDragTarget) {
            var delta = Qt.point(mouseX - _start.x, mouseY - _start.y)
            // do any rounding/snapping of delta here...
            _start.x += delta.x
            _start.y += delta.y
            myDragTarget.x += delta.x
            myDragTarget.y += delta.y
        }
    }

这可以拖动该项目可靠。

这也是我想要避免的,因为它重新发明了鼠标阻力,但是在没有更好的解决方案的情况下,我将使用的是。

我不会接受这个答案,因为我很想看到其他方法来解决这个问题。

The solution I come up with consists of having two MouseAreas:

  • a MouseArea moves with the item to drag, that is used only for hit-testing, so its onPressed handler is something like this:
    onPressed: (mouse) => {
        mouse.accepted = false
        root.container.myDragTarget = root
    }
    onReleased: root.container.myDragTarget = null
  • another MouseArea, stacked below the others and not moving, handles the mouse position change and the dragging:
    onPressed: _start = Qt.point(mouseX, mouseY)
    onPositionChanged: {
        if(myDragTarget) {
            var delta = Qt.point(mouseX - _start.x, mouseY - _start.y)
            // do any rounding/snapping of delta here...
            _start.x += delta.x
            _start.y += delta.y
            myDragTarget.x += delta.x
            myDragTarget.y += delta.y
        }
    }

This is able to drag the item reliably.

This is also what I wanted to avoid, because it reinvents mouse drag, but in absence of a better solution it is what I am going to use.

I won't accept this answer as I'm curious to see other ways to approach this problem.

诗酒趁年少 2025-02-11 09:11:31

您可以通过用 item 的新移动和新定位加入。 html#maptoItem-method“ rel =“ nofollow noreferrer”> maptoItem 函数。

在我的解决方案中,我没有使用 mousearea 的阻力功能,因为它需要 drag.target 。我使用了压制和位置更改信号来实现阻力行为。唯一的缺点是背景 item 是maptoItem函数所需的,因为它不接受窗口,因为它不是<代码>项目。

import QtQuick
import QtQuick.Window
import QtQuick.Shapes

Window {
    id: root
    visible: true
    width: 400
    height: 400

    Item {
        id: background
        anchors.fill: parent

        Rectangle {
            id: rectangle
            property int rightX
            x: 50
            y: 50
            width: 200
            height: 80
            border.width: 1
            border.color: "black"
            color: "red"

            Rectangle {
                anchors.left: parent.left
                anchors.top: parent.top
                anchors.bottom: parent.bottom
                width: 10
                color: mouseArea.containsMouse || mouseArea.pressed ? "#ff808080" : "#aa808080"

                MouseArea {
                    id: mouseArea
                    anchors.fill: parent
                    hoverEnabled: true

                    onPressed: rectangle.rightX = rectangle.x + rectangle.width

                    onPositionChanged: function(mouse) {
                        if (mouseArea.pressed) {
                            var tmp = mouseArea.mapToItem(background, mouse.x, 0)
                            if (tmp.x <= rectangle.rightX)
                                rectangle.x = tmp.x
                            else
                                rectangle.x = rectangle.rightX

                            rectangle.width = rectangle.rightX - rectangle.x
                        }
                    }
                }
            }
        }
    }
}

You can workaround the movement and new positioning of the dragged Item by mapping the coordinates with the mapToItem functions.

In my solution, I've not used the drag functionality of the MouseArea as it needs a drag.target. I've used the pressed and position changed signals to implement drag behavior. The only downside is the background Item which is needed for the mapToItem function as it doesn't accept the Window due to it not being an Item.

import QtQuick
import QtQuick.Window
import QtQuick.Shapes

Window {
    id: root
    visible: true
    width: 400
    height: 400

    Item {
        id: background
        anchors.fill: parent

        Rectangle {
            id: rectangle
            property int rightX
            x: 50
            y: 50
            width: 200
            height: 80
            border.width: 1
            border.color: "black"
            color: "red"

            Rectangle {
                anchors.left: parent.left
                anchors.top: parent.top
                anchors.bottom: parent.bottom
                width: 10
                color: mouseArea.containsMouse || mouseArea.pressed ? "#ff808080" : "#aa808080"

                MouseArea {
                    id: mouseArea
                    anchors.fill: parent
                    hoverEnabled: true

                    onPressed: rectangle.rightX = rectangle.x + rectangle.width

                    onPositionChanged: function(mouse) {
                        if (mouseArea.pressed) {
                            var tmp = mouseArea.mapToItem(background, mouse.x, 0)
                            if (tmp.x <= rectangle.rightX)
                                rectangle.x = tmp.x
                            else
                                rectangle.x = rectangle.rightX

                            rectangle.width = rectangle.rightX - rectangle.x
                        }
                    }
                }
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文