Qt QML如何通过双击

时间:2019-03-19 18:00:25

标签: qt qml

双击选择单词,但我想对其进行自定义,例如选择$ 1000的词。

示例:

import QtQuick 2.7
import QtQuick.Controls 1.4

ApplicationWindow
{
    visible: true
    width: 640
    height: 480

    TextArea
    {
        anchors.fill: parent
        text: "hello, try to select the $1000 prize!"
        font.pointSize: 14
    } 
}

例如,通过以某种方式覆盖selectWord或通过覆盖onDoubleClicked或通过以某种方式添加我自己的MouseArea来使不会破坏现有的{{1 }}功能。

不确定如何执行此操作。

感谢您的帮助。

更新

我尝试添加TextArea,但没有成功。例子;

MouseArea

更新2

我认为这个问题是Qt问题长期存在的一个版本,您不能拥有某种“事件观察器”,其职责是检查事件而不阻止其继续正常运行。

如果我有一个事件观察器,则可以使其“观察” TextArea并在单击或双击时执行某些操作。

所以,这里尝试做一个。...

toucharea.h

ApplicationWindow
{
    visible: true
    width: 640
    height: 480

    TextArea
    {
        anchors.fill: parent
        text: "hello, try to select the $1000 prize!"
        font.pointSize: 14

        MouseArea
        {
            anchors.fill: parent
            propagateComposedEvents: true
            onClicked: 
            {
                // attempt to get an event on click without
                // affecting the TextArea. But it breaks selection.
                console.log("clicked some text")
                mouse.accepted = false
            }
        }
    }
}    

main.cpp

#pragma once

#include <QGuiApplication>
#include <QQuickItem>
#include <QTime>

class TouchArea : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)

public:

    QTime _lastMousePress;
    int   _clickThresholdMS = 300;

    bool eventFilter(QObject*, QEvent *event) override
    {
        // if false this will allow the event to continue as normal
        // if true it will stop the event propagating
        bool handled = false;

        // https://doc.qt.io/qt-5/qevent.html#Type-enum
        QEvent::Type t = event->type();
        switch (t)
        {
        case QEvent::TouchUpdate:
            break;
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
            {
                QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
                qDebug("key press %d", keyEvent->key());
            }
            break;
        case QEvent::MouseButtonPress:
            {
                qDebug() << "mouse press";
                _lastMousePress.start();
                break;
            }
        case QEvent::MouseButtonRelease:
            {
                qDebug() << "mouse release";
                int dt = _lastMousePress.elapsed();

                if (dt < _clickThresholdMS)
                {
                    qDebug() << "mouse click";
                    emit clicked();
                }
                break;
            }
        }

        return handled;
    }

    QQuickItem *target() const { return _target; }

    void setTarget(QQuickItem *target) 
    {
        qDebug() << "set target";
        if (_target == target) return;

        if (_target)
            _target->removeEventFilter(this);

        _target = target;

        if (_target)
            _target->installEventFilter(this);

        emit targetChanged();
    }

signals:

    void targetChanged();
    void clicked();

private:

    QQuickItem* _target = 0;
};

main.qml

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>

#include "toucharea.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<TouchArea>("App", 1, 0, "TouchArea");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

好吧,它几乎奏效了。如果我们在import QtQuick 2.12 import QtQuick.Controls 1.4 import App 1.0 ApplicationWindow { visible: true width: 640 height: 480 TextArea { id: tarea anchors.fill: parent text: "hello, try to select the $1000 prize!" font.pointSize: 14 MouseArea { enabled: false anchors.fill: parent TouchArea { target: parent onClicked: console.log("captured a click") } } } } handled进行活动,则只能捕获合成的 click 。如果不是eventFilter,过滤器将看不到handled

这是为什么。我期望这是其他QtQuick项目在之前遇到的第一个处理程序。

有帮助吗?

3 个答案:

答案 0 :(得分:1)

看起来你可以用 TapHandler 做到这一点:

    import QtQuick 2.15
    import QtQuick.Controls 2.15

    TextField {
        width: parent.width

        selectByMouse: true

        TapHandler {
            grabPermissions: PointerHandler.TakeOverForbidden
            onDoubleTapped: (eventPoint) => {
                print("taptap");
                eventPoint.accepted = true;
            }
        }
    }

请注意,在上述操作之后,我经历了 TextField 上的双击 = 选择字符串的工作和不工作。点击+拖动以选择始终有效。

答案 1 :(得分:0)

仅在QML方面,我想不出任何能够保留TextArea行为的解决方案。相反,我会尝试在C ++方面包装,继承或重新实现该组件,以添加所需的内容。

  • 继承可能会很棘手,因为要查看QQuickTextAreaQQuickTextEdit的源代码,意味着很少有Qt类可以直接继承
  • 重新实现可能是一些工作,但这可能是我追求的选择,同时将逻辑和代码保持在最低限度。与QQuickTextArea相比,QQuickTextArea似乎更小/更简单(反过来在内部使用TextArea)

两个关键要素:

  • 事件管理(鼠标,选择...)
  • 基于Qt OpenGL包装器类(QSG *场景图类)的组件渲染。有点晦涩,但是这里有一些教程和演示。而且您也许可以复制粘贴原始代码。

对于这两者,我建议深入研究源代码:

答案 2 :(得分:0)

找出一种方法;

步骤1,为事件定义一个观察者类

observer.h


#pragma once

#include <QGuiApplication>
#include <QQuickItem>
#include <QTime>
#include <QMouseEvent>

class Observer : public QQuickItem
{
    Q_OBJECT

public:

    QTime _lastMousePress;
    int   _clickThresholdMS = 300;

    Observer()
    {
        setFiltersChildMouseEvents(true);
    }

    bool childMouseEventFilter(QQuickItem*, QEvent *event) override
    {
        // if false this will allow the event to continue as normal
        // if true it will stop the event propagating
        bool handled = false;

        // https://doc.qt.io/qt-5/qevent.html#Type-enum
        QEvent::Type t = event->type();
        switch (t)
        {
        case QEvent::TouchUpdate:
            break;
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
            {
                QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
                qDebug("key press %d", keyEvent->key());
            }
            break;
        case QEvent::MouseButtonPress:
            {
                //qDebug() << "mouse press";
                _lastMousePress.start();
            }
            break;
        case QEvent::MouseButtonRelease:
            {
                //qDebug() << "mouse release";
                int dt = _lastMousePress.elapsed();

                if (dt < _clickThresholdMS)
                {
                    //qDebug() << "mouse click";
                    emit clicked();
                }
            }
            break;
        case QEvent::MouseButtonDblClick:
            {
                //QMouseEvent* mevent = static_cast<QMouseEvent*>(event);
                //qDebug() << "mouse double click";
                emit doubleClicked();
                handled = true;
            }
            break;
        }

        return handled;
    }

signals:

    void clicked();
    void doubleClicked();

};

第2步,将其放入main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <qqmlcontext.h>

#include "observer.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<Observer>("App", 1, 0, "Observer");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();
}

第3步,使用观察者检测事件

检测到您想要的任何事件,然后使其执行所需的操作,例如,双击以在TextArea中选择更大范围的字符;

import QtQuick 2.12
import QtQuick.Controls 1.4

import App 1.0

ApplicationWindow
{
    visible: true
    width: 640
    height: 480

    Observer
    {
        anchors.fill: parent

        onDoubleClicked:
        {
            tarea.selectWord();

            var s = tarea.selectionStart
            var e = tarea.selectionEnd

            function allowed(c)
            {
                if (c == "$" || c == "#") return true;
                if (c >= "0" && c <= "9") return true;
                if (c.toUpperCase() != c.toLowerCase()) return true;
                return false;
            }

            while (allowed(tarea.getText(s-1, s))) tarea.select(--s, e);
            while (allowed(tarea.getText(e, e+1))) tarea.select(s, ++e);
        }

        TextArea
        {
            id: tarea
            anchors.fill: parent
            text: "hello, try to select the #$$$1000###$foo prize!"
            font.pointSize: 14
        }
    }
}