QML:来自动态 MouseArea 的 'Steal' 个事件
QML: 'Steal' events from dynamic MouseArea
我目前正在尝试在 QML 中实现 拖动创建 机制,但我遇到了一个问题,我需要新创建的 MouseArea
成为鼠标事件的目标,即使原始 MouseArea
还没有鼠标按钮释放事件。
Window {
id: window
width: 300
height: 300
Rectangle {
id: base
width: 20
height: 20
color: "red"
MouseArea {
anchors.fill: parent
property var lastPoint
property var draggedObj: null
function vecLength( vec ) {
return Math.abs( Math.sqrt( Math.pow( vec.x, 2 ) +
Math.pow( vec.y, 2 ) ) );
}
onPressed: lastPoint = Qt.point( mouse.x, mouse.y )
onPositionChanged: {
if ( !draggedObj ) {
var diff = Qt.point( mouse.x - lastPoint.x,
mouse.y - lastPoint.y );
if ( vecLength( diff ) > 4 ) {
draggedObj = dragObj.createObject( window );
}
}
mouse.accepted = !draggedObj;
}
}
}
Component {
id: dragObj
Rectangle {
width: 20
height: 20
color: "blue"
Drag.active: dragArea.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
}
}
}
}
如果您 运行 这段代码并尝试一下,您会看到拖动红色 Rectangle
导致创建可拖动的蓝色 Rectangle
,但它不会跟随鼠标,因为红色 MouseArea
仍在接收鼠标事件,尽管蓝色 MouseArea
在其上方。
有没有办法强制蓝色MouseArea
接收鼠标事件?
我以前遇到过这种情况,并在我的阁楼上开始了解决方案。
这里的技巧是调用 QQuickItem::grabMouse()
并向新创建的对象发送鼠标按下事件。
不幸的是,我认为这只能通过 C++ 完成。
然后我创建了一个助手 class 来向 qml 公开此功能:
MouseGrabber.h
#ifndef MOUSEGRABBER
#define MOUSEGRABBER
#include <QObject>
#include <QQuickItem>
#include <QGuiApplication>
#include <QMouseEvent>
class MouseGrabber : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
public:
explicit MouseGrabber(QObject *parent = 0) : QObject(parent), m_target(nullptr), m_active(true) { }
QQuickItem* target() const { return m_target; }
bool active() const { return m_active;}
signals:
void targetChanged();
void activeChanged();
public slots:
void setTarget(QQuickItem* target)
{
if (m_target == target)
return;
ungrabMouse(m_target);
if (m_active)
grabMouse(target);
m_target = target;
emit targetChanged();
}
void setActive(bool arg)
{
if (m_active == arg)
return;
m_active = arg;
if (m_active)
grabMouse(m_target);
else
ungrabMouse(m_target);
emit activeChanged();
}
private:
static void grabMouse(QQuickItem* target)
{
if (target)
{
target->grabMouse();
QMouseEvent event(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
QGuiApplication::sendEvent(target, &event);
}
}
static void ungrabMouse(QQuickItem* target)
{
if (target)
target->ungrabMouse();
}
QQuickItem* m_target;
bool m_active;
};
#endif // MOUSEGRABBER
通过直接调用插槽而不是操纵属性可以使这更方便,但这就是我的库存。例如,一个名为 grabMouseUntilRelease(QQuickItem* item)
的插槽为该项目抓取鼠标,使用 installEventFilter
监听鼠标释放事件并自动取消抓取。
注册 class 以便它可以在代码中的某处使用 qmlRegisterType
在 QML 中实例化:
qmlRegisterType<MouseGrabber>("com.mycompany.qmlcomponents", 1, 0, "MouseGrabber");
之后,您可以在 QML 中实例化 MouseGrabber 并通过修改其属性(target
和 active
)来使用它:
QML
import com.mycompany.qmlcomponents 1.0
Window {
id: window
width: 300
height: 300
Rectangle {
id: base
width: 20
height: 20
color: "red"
MouseArea {
anchors.fill: parent
property var lastPoint
property var draggedObj: null
function vecLength( vec ) {
return Math.abs( Math.sqrt( Math.pow( vec.x, 2 ) +
Math.pow( vec.y, 2 ) ) );
}
onPressed: lastPoint = Qt.point( mouse.x, mouse.y )
onPositionChanged: {
if ( !draggedObj ) {
var diff = Qt.point( mouse.x - lastPoint.x,
mouse.y - lastPoint.y );
if ( vecLength( diff ) > 4 ) {
draggedObj = dragObj.createObject( window );
grabber.target = draggedObj.dragArea; // grab the mouse
}
}
mouse.accepted = !draggedObj;
}
}
}
MouseGrabber {
id: grabber
}
Component {
id: dragObj
Rectangle {
property alias dragArea: dragArea
width: 20
height: 20
color: "blue"
Drag.active: dragArea.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
onReleased: {
if (grabber.target === this)
grabber.target = null; // ungrab the mouse
}
}
}
}
}
我的另一个答案设计过度了。
在您的情况下无需窃取鼠标事件,您只想更新 onPositionChanged 处理程序中拖动的蓝色矩形的位置(或使用 Binding
或直接在 Rectangle
组件内)。
在你的 MouseArea
中写这个就够了 :
onPositionChanged: {
if ( !draggedObj ) {
var diff = Qt.point( mouse.x - lastPoint.x,
mouse.y - lastPoint.y );
if ( vecLength( diff ) > 4 ) {
draggedObj = dragObj.createObject( window );
}
} else { //update the position of the dragged rectangle
draggedObj.x = mouse.x - draggedObj.width/2;
draggedObj.y = mouse.y - draggedObj.height/2;
}
}
onReleased: draggedObj = null
我目前正在尝试在 QML 中实现 拖动创建 机制,但我遇到了一个问题,我需要新创建的 MouseArea
成为鼠标事件的目标,即使原始 MouseArea
还没有鼠标按钮释放事件。
Window {
id: window
width: 300
height: 300
Rectangle {
id: base
width: 20
height: 20
color: "red"
MouseArea {
anchors.fill: parent
property var lastPoint
property var draggedObj: null
function vecLength( vec ) {
return Math.abs( Math.sqrt( Math.pow( vec.x, 2 ) +
Math.pow( vec.y, 2 ) ) );
}
onPressed: lastPoint = Qt.point( mouse.x, mouse.y )
onPositionChanged: {
if ( !draggedObj ) {
var diff = Qt.point( mouse.x - lastPoint.x,
mouse.y - lastPoint.y );
if ( vecLength( diff ) > 4 ) {
draggedObj = dragObj.createObject( window );
}
}
mouse.accepted = !draggedObj;
}
}
}
Component {
id: dragObj
Rectangle {
width: 20
height: 20
color: "blue"
Drag.active: dragArea.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
}
}
}
}
如果您 运行 这段代码并尝试一下,您会看到拖动红色 Rectangle
导致创建可拖动的蓝色 Rectangle
,但它不会跟随鼠标,因为红色 MouseArea
仍在接收鼠标事件,尽管蓝色 MouseArea
在其上方。
有没有办法强制蓝色MouseArea
接收鼠标事件?
我以前遇到过这种情况,并在我的阁楼上开始了解决方案。
这里的技巧是调用 QQuickItem::grabMouse()
并向新创建的对象发送鼠标按下事件。
不幸的是,我认为这只能通过 C++ 完成。
然后我创建了一个助手 class 来向 qml 公开此功能:
MouseGrabber.h
#ifndef MOUSEGRABBER
#define MOUSEGRABBER
#include <QObject>
#include <QQuickItem>
#include <QGuiApplication>
#include <QMouseEvent>
class MouseGrabber : public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
public:
explicit MouseGrabber(QObject *parent = 0) : QObject(parent), m_target(nullptr), m_active(true) { }
QQuickItem* target() const { return m_target; }
bool active() const { return m_active;}
signals:
void targetChanged();
void activeChanged();
public slots:
void setTarget(QQuickItem* target)
{
if (m_target == target)
return;
ungrabMouse(m_target);
if (m_active)
grabMouse(target);
m_target = target;
emit targetChanged();
}
void setActive(bool arg)
{
if (m_active == arg)
return;
m_active = arg;
if (m_active)
grabMouse(m_target);
else
ungrabMouse(m_target);
emit activeChanged();
}
private:
static void grabMouse(QQuickItem* target)
{
if (target)
{
target->grabMouse();
QMouseEvent event(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
QGuiApplication::sendEvent(target, &event);
}
}
static void ungrabMouse(QQuickItem* target)
{
if (target)
target->ungrabMouse();
}
QQuickItem* m_target;
bool m_active;
};
#endif // MOUSEGRABBER
通过直接调用插槽而不是操纵属性可以使这更方便,但这就是我的库存。例如,一个名为 grabMouseUntilRelease(QQuickItem* item)
的插槽为该项目抓取鼠标,使用 installEventFilter
监听鼠标释放事件并自动取消抓取。
注册 class 以便它可以在代码中的某处使用 qmlRegisterType
在 QML 中实例化:
qmlRegisterType<MouseGrabber>("com.mycompany.qmlcomponents", 1, 0, "MouseGrabber");
之后,您可以在 QML 中实例化 MouseGrabber 并通过修改其属性(target
和 active
)来使用它:
QML
import com.mycompany.qmlcomponents 1.0
Window {
id: window
width: 300
height: 300
Rectangle {
id: base
width: 20
height: 20
color: "red"
MouseArea {
anchors.fill: parent
property var lastPoint
property var draggedObj: null
function vecLength( vec ) {
return Math.abs( Math.sqrt( Math.pow( vec.x, 2 ) +
Math.pow( vec.y, 2 ) ) );
}
onPressed: lastPoint = Qt.point( mouse.x, mouse.y )
onPositionChanged: {
if ( !draggedObj ) {
var diff = Qt.point( mouse.x - lastPoint.x,
mouse.y - lastPoint.y );
if ( vecLength( diff ) > 4 ) {
draggedObj = dragObj.createObject( window );
grabber.target = draggedObj.dragArea; // grab the mouse
}
}
mouse.accepted = !draggedObj;
}
}
}
MouseGrabber {
id: grabber
}
Component {
id: dragObj
Rectangle {
property alias dragArea: dragArea
width: 20
height: 20
color: "blue"
Drag.active: dragArea.drag.active
Drag.hotSpot.x: 10
Drag.hotSpot.y: 10
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
onReleased: {
if (grabber.target === this)
grabber.target = null; // ungrab the mouse
}
}
}
}
}
我的另一个答案设计过度了。
在您的情况下无需窃取鼠标事件,您只想更新 onPositionChanged 处理程序中拖动的蓝色矩形的位置(或使用 Binding
或直接在 Rectangle
组件内)。
在你的 MouseArea
中写这个就够了 :
onPositionChanged: {
if ( !draggedObj ) {
var diff = Qt.point( mouse.x - lastPoint.x,
mouse.y - lastPoint.y );
if ( vecLength( diff ) > 4 ) {
draggedObj = dragObj.createObject( window );
}
} else { //update the position of the dragged rectangle
draggedObj.x = mouse.x - draggedObj.width/2;
draggedObj.y = mouse.y - draggedObj.height/2;
}
}
onReleased: draggedObj = null