Qt QML Как вручную выбрать word в текстовой области двойным щелчком

#qt #qml

#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 который не нарушает существующую TextArea функциональность.

Не уверен, как это сделать.

Спасибо за любую помощь.

Обновить

Я пытался добавить MouseArea , но это не сработало. Пример;

 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
            }
        }
    }
}    

  

Обновление 2

Я думаю, что эта проблема является версией давней проблемы Qt, заключающейся в том, что у вас не может быть какого-то «наблюдателя событий», чья работа заключается в проверке событий, но не в том, чтобы мешать им продолжать свою обычную работу.

Если бы у меня был наблюдатель событий, я мог бы заставить его «наблюдать» за текстовой областью и делать что-то по щелчку или двойному щелчку.

Итак, здесь мы пытаемся создать один….

toucharea.h

 #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.cpp

 #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();
}

  

main.qml

 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 запустим событие в eventFilter . Когда нет, handled фильтр не видит MouseButtonRelease .

Почему это. Я ожидал, что это будет первый обработчик, с которым столкнутся, прежде, чем другие элементы QtQuick увидят его.

Любая помощь?

Комментарии:

1. Вы пытались сначала выполнить поиск по этому в документации ?

2. @folibis уверен, но в нем нет указаний, как переопределить doubleclick или иным образом. Если я добавлю свой собственный, MouseArea то TextArea произойдет сбой.

3. Вам следует уточнить свой вопрос, поскольку это замечание полностью меняет вопрос

4. @folibis ты прав. Я добавил то, что пробовал до сих пор.

Ответ №1:

Нашел способ;

Шаг 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, используйте Observer для обнаружения события

Определите, какое событие вы хотите, затем сделайте так, чтобы оно выполняло то, что вам нужно, например, для двойного щелчка, чтобы выбрать более широкий класс символов в 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" amp;amp; 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
        }
    }
}    
  

Комментарии:

1. О, здорово, я не думал, что вы сможете правильно перехватывать / выпускать события для сложного объекта, такого как текстовое слово . Хорошая работа!

Ответ №2:

Похоже, вы можете сделать это с помощью 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 работал и не работал после вышесказанного. Хотя щелчок перетаскивание для выбора всегда срабатывал.

Ответ №3:

Я не могу придумать ни одного решения, которое сохранило бы TextArea поведение, только на стороне QML. Вместо этого я бы попытался обернуть, наследовать или переопределить компонент на стороне C , чтобы добавить то, что вы хотите.

  • Наследование может быть сложным, поскольку немногие классы Qt предназначены для наследования напрямую, глядя на QQuickTextArea и QQuickTextEdit исходный код, это может быть непросто
  • Переопределение может потребовать некоторой работы, но, вероятно, я бы выбрал именно этот вариант, сведя логику и код к минимуму. QQuickTextArea кажется хорошей базой / ссылкой, поскольку она меньше / проще, чем QQuickTextArea (которая, в свою очередь, использует TextArea внутренне)

Два ключевых элемента для этого:

  • Управление событиями (мышь, выделение, …)
  • Рендеринг компонента, основанный на классах-оболочках Qt OpenGL (QSG * Классы графов сцен). Это немного непонятно, но есть несколько руководств и презентаций по этому поводу. И вы можете просто скопировать-вставить исходный код.

Для обоих я бы рекомендовал покопаться в исходном коде:

Комментарии:

1. Спасибо за ваш ответ. К сожалению, я использую TextArea 1.4, потому что мне нужно диалоговое окно «Вырезать и вставить», которого нет в controls2. Я попытался скопировать исходные тексты QML и изменить их, но слишком много закрытых разделов. Я попробовал другой подход C . Но это не работает.