Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-05 09:29:46

0001 // Copyright (C) 2016 The Qt Company Ltd.
0002 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0003 
0004 #ifndef QTESTACCESSIBLE_H
0005 #define QTESTACCESSIBLE_H
0006 
0007 #if 0
0008 // inform syncqt
0009 #pragma qt_no_master_include
0010 #endif
0011 
0012 #include <QtCore/qglobal.h>
0013 
0014 #define QVERIFY_EVENT(event) \
0015     QVERIFY(QTestAccessibility::verifyEvent(event))
0016 
0017 #include <QtCore/qlist.h>
0018 #include <QtCore/qdebug.h>
0019 #include <QtGui/qaccessible.h>
0020 #include <QtGui/qguiapplication.h>
0021 #include <QtTest/qttestglobal.h>
0022 #include <QtTest/qtestsystem.h>
0023 
0024 #if QT_CONFIG(accessibility)
0025 
0026 QT_BEGIN_NAMESPACE
0027 
0028 class QObject;
0029 
0030 inline bool QTestAccessibility_cmpEvent(const QAccessibleEvent &l, const QAccessibleEvent &r)
0031 {
0032     if (l.type() != r.type()) {
0033 //        qDebug() << "QAccessibleEvent with wrong type: " << qAccessibleEventString(l.type()) << " and " << qAccessibleEventString(r.type());
0034         return false;
0035     }
0036     if (l.object() != r.object() ||
0037             l.child() != r.child()) {
0038 //        qDebug() << "QAccessibleEvent for wrong object: " << l.object() << " and " << r.object() << " child: " << l.child() << " and " << r.child();
0039         return false;
0040     }
0041 
0042     if (l.type() == QAccessible::StateChanged) {
0043         return static_cast<const QAccessibleStateChangeEvent*>(&l)->changedStates()
0044                 == static_cast<const QAccessibleStateChangeEvent*>(&r)->changedStates();
0045     } else if (l.type() == QAccessible::TextCaretMoved) {
0046         return static_cast<const QAccessibleTextCursorEvent*>(&l)->cursorPosition()
0047                 == static_cast<const QAccessibleTextCursorEvent*>(&r)->cursorPosition();
0048     } else if (l.type() == QAccessible::TextSelectionChanged) {
0049         const QAccessibleTextSelectionEvent *le = static_cast<const QAccessibleTextSelectionEvent*>(&l);
0050         const QAccessibleTextSelectionEvent *re = static_cast<const QAccessibleTextSelectionEvent*>(&r);
0051         return  le->cursorPosition() == re->cursorPosition() &&
0052                 le->selectionStart() == re->selectionStart() &&
0053                 le->selectionEnd() == re->selectionEnd();
0054     } else if (l.type() == QAccessible::TextInserted) {
0055         const QAccessibleTextInsertEvent *le = static_cast<const QAccessibleTextInsertEvent*>(&l);
0056         const QAccessibleTextInsertEvent *re = static_cast<const QAccessibleTextInsertEvent*>(&r);
0057         return  le->cursorPosition() == re->cursorPosition() &&
0058                 le->changePosition() == re->changePosition() &&
0059                 le->textInserted() == re->textInserted();
0060     } else if (l.type() == QAccessible::TextRemoved) {
0061         const QAccessibleTextRemoveEvent *le = static_cast<const QAccessibleTextRemoveEvent*>(&l);
0062         const QAccessibleTextRemoveEvent *re = static_cast<const QAccessibleTextRemoveEvent*>(&r);
0063         return  le->cursorPosition() == re->cursorPosition() &&
0064                 le->changePosition() == re->changePosition() &&
0065                 le->textRemoved() == re->textRemoved();
0066     } else if (l.type() == QAccessible::TextUpdated) {
0067         const QAccessibleTextUpdateEvent *le = static_cast<const QAccessibleTextUpdateEvent*>(&l);
0068         const QAccessibleTextUpdateEvent *re = static_cast<const QAccessibleTextUpdateEvent*>(&r);
0069         return  le->cursorPosition() == re->cursorPosition() &&
0070                 le->changePosition() == re->changePosition() &&
0071                 le->textInserted() == re->textInserted() &&
0072                 le->textRemoved() == re->textRemoved();
0073     } else if (l.type() == QAccessible::ValueChanged) {
0074         const QAccessibleValueChangeEvent *le = static_cast<const QAccessibleValueChangeEvent*>(&l);
0075         const QAccessibleValueChangeEvent *re = static_cast<const QAccessibleValueChangeEvent*>(&r);
0076         return le->value() == re->value();
0077     }
0078     return true;
0079 }
0080 
0081 class QTestAccessibility
0082 {
0083     Q_DISABLE_COPY_MOVE(QTestAccessibility)
0084 public:
0085     // Use pointers since we subclass QAccessibleEvent
0086     using EventList = QList<QAccessibleEvent*>;
0087 
0088     static void initialize()
0089     {
0090         if (!instance()) {
0091             instance() = new QTestAccessibility;
0092             qAddPostRoutine(cleanup);
0093         }
0094     }
0095 
0096     static void cleanup()
0097     {
0098         delete instance();
0099         instance() = nullptr;
0100     }
0101     static void clearEvents() { eventList().clear(); }
0102     static EventList events() { return eventList(); }
0103     static bool verifyEvent(QAccessibleEvent *ev)
0104     {
0105         for (int i = 0; eventList().isEmpty() && i < 5; ++i)
0106             QTest::qWait(50);
0107         if (eventList().isEmpty()) {
0108             qWarning("Timeout waiting for accessibility event.");
0109             return false;
0110         }
0111 
0112         for (qsizetype i = 0; i < eventList().size(); ++i) {
0113             if (QTestAccessibility_cmpEvent(*eventList().at(i), *ev)) {
0114                 if (i != 0) {
0115                     qWarning() << " Found event at position " << i;
0116                     qWarning("%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
0117                 }
0118                 delete eventList().takeAt(i);
0119                 return true;
0120             }
0121         }
0122 
0123         qWarning("%s", qPrintable(msgAccessibilityEventListMismatch(eventList(), ev)));
0124         return false;
0125     }
0126     static bool containsEvent(QAccessibleEvent *event) {
0127         for (const QAccessibleEvent *ev : std::as_const(eventList())) {
0128             if (QTestAccessibility_cmpEvent(*ev, *event))
0129                 return true;
0130         }
0131         return false;
0132     }
0133     static bool containsEventOfType(QAccessible::Event evtype) {
0134         for (const QAccessibleEvent *ev : std::as_const(eventList())) {
0135             if (ev->type() == evtype)
0136                 return true;
0137         }
0138         return false;
0139     }
0140     static void setUpdateHandler(std::function<void(QAccessibleEvent *event)> updateHandler)
0141     {
0142         Q_ASSERT_X(updateHandler, __FUNCTION__, "Update handler cannot be nullptr");
0143         instance()->m_updateHandler = std::move(updateHandler);
0144     }
0145 
0146 private:
0147     QTestAccessibility()
0148     {
0149         QAccessible::installUpdateHandler(updateHandler);
0150         QAccessible::installRootObjectHandler(rootObjectHandler);
0151     }
0152 
0153     ~QTestAccessibility()
0154     {
0155         QAccessible::installUpdateHandler(nullptr);
0156         QAccessible::installRootObjectHandler(nullptr);
0157     }
0158 
0159     static void rootObjectHandler(QObject *object)
0160     {
0161         //    qDebug("rootObjectHandler called %p", object);
0162         if (object) {
0163             QGuiApplication* app = qobject_cast<QGuiApplication*>(object);
0164             if ( !app )
0165                 qWarning("root Object is not a QGuiApplication!");
0166         } else {
0167             qWarning("root Object called with 0 pointer");
0168         }
0169     }
0170 
0171     static void updateHandler(QAccessibleEvent *event)
0172     {
0173         instance()->m_updateHandler(event);
0174 
0175         auto ev = copyEvent(event);
0176         if (auto obj = ev->object()) {
0177             QObject::connect(obj, &QObject::destroyed, obj, [&, ev](){
0178                 auto index= eventList().indexOf(ev);
0179                 if (index == -1)
0180                     return;
0181                 eventList().at(index)->m_object = nullptr;
0182             });
0183         }
0184         eventList().append(ev);
0185     }
0186     static QAccessibleEvent *copyEvent(QAccessibleEvent *event)
0187     {
0188         QAccessibleEvent *ev;
0189         if (event->type() == QAccessible::StateChanged) {
0190             if (event->object())
0191                 ev = new QAccessibleStateChangeEvent(event->object(),
0192                         static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
0193             else
0194                 ev = new QAccessibleStateChangeEvent(event->accessibleInterface(),
0195                         static_cast<QAccessibleStateChangeEvent*>(event)->changedStates());
0196         } else if (event->type() == QAccessible::TextCaretMoved) {
0197             if (event->object())
0198                 ev = new QAccessibleTextCursorEvent(event->object(), static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
0199             else
0200                 ev = new QAccessibleTextCursorEvent(event->accessibleInterface(), static_cast<QAccessibleTextCursorEvent*>(event)->cursorPosition());
0201         } else if (event->type() == QAccessible::TextSelectionChanged) {
0202             const QAccessibleTextSelectionEvent *original = static_cast<QAccessibleTextSelectionEvent*>(event);
0203             QAccessibleTextSelectionEvent *sel;
0204             if (event->object())
0205                 sel = new QAccessibleTextSelectionEvent(event->object(), original->selectionStart(), original->selectionEnd());
0206             else
0207                 sel = new QAccessibleTextSelectionEvent(event->accessibleInterface(), original->selectionStart(), original->selectionEnd());
0208             sel->setCursorPosition(original->cursorPosition());
0209             ev = sel;
0210         } else if (event->type() == QAccessible::TextInserted) {
0211             const QAccessibleTextInsertEvent *original = static_cast<QAccessibleTextInsertEvent*>(event);
0212             QAccessibleTextInsertEvent *ins;
0213             if (original->object())
0214                 ins = new QAccessibleTextInsertEvent(event->object(), original->changePosition(), original->textInserted());
0215             else
0216                 ins = new QAccessibleTextInsertEvent(event->accessibleInterface(), original->changePosition(), original->textInserted());
0217             ins->setCursorPosition(original->cursorPosition());
0218             ev = ins;
0219         } else if (event->type() == QAccessible::TextRemoved) {
0220             const QAccessibleTextRemoveEvent *original = static_cast<QAccessibleTextRemoveEvent*>(event);
0221             QAccessibleTextRemoveEvent *rem;
0222             if (event->object())
0223                 rem = new QAccessibleTextRemoveEvent(event->object(), original->changePosition(), original->textRemoved());
0224             else
0225                 rem = new QAccessibleTextRemoveEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved());
0226             rem->setCursorPosition(original->cursorPosition());
0227             ev = rem;
0228         } else if (event->type() == QAccessible::TextUpdated) {
0229             const QAccessibleTextUpdateEvent *original = static_cast<QAccessibleTextUpdateEvent*>(event);
0230             QAccessibleTextUpdateEvent *upd;
0231             if (event->object())
0232                 upd = new QAccessibleTextUpdateEvent(event->object(), original->changePosition(), original->textRemoved(), original->textInserted());
0233             else
0234                 upd = new QAccessibleTextUpdateEvent(event->accessibleInterface(), original->changePosition(), original->textRemoved(), original->textInserted());
0235             upd->setCursorPosition(original->cursorPosition());
0236             ev = upd;
0237         } else if (event->type() == QAccessible::ValueChanged) {
0238             if (event->object())
0239                 ev = new QAccessibleValueChangeEvent(event->object(), static_cast<QAccessibleValueChangeEvent*>(event)->value());
0240             else
0241                 ev = new QAccessibleValueChangeEvent(event->accessibleInterface(), static_cast<QAccessibleValueChangeEvent*>(event)->value());
0242         } else if (event->type() == QAccessible::TableModelChanged) {
0243             QAccessibleTableModelChangeEvent *oldEvent = static_cast<QAccessibleTableModelChangeEvent*>(event);
0244             QAccessibleTableModelChangeEvent *newEvent;
0245             if (event->object())
0246                 newEvent = new QAccessibleTableModelChangeEvent(event->object(), oldEvent->modelChangeType());
0247             else
0248                 newEvent = new QAccessibleTableModelChangeEvent(event->accessibleInterface(), oldEvent->modelChangeType());
0249             newEvent->setFirstRow(oldEvent->firstRow());
0250             newEvent->setFirstColumn(oldEvent->firstColumn());
0251             newEvent->setLastRow(oldEvent->lastRow());
0252             newEvent->setLastColumn(oldEvent->lastColumn());
0253             ev = newEvent;
0254         } else if (event->type() == QAccessible::Announcement) {
0255             QAccessibleAnnouncementEvent *oldEvent =
0256                     static_cast<QAccessibleAnnouncementEvent *>(event);
0257             QAccessibleAnnouncementEvent *newEvent;
0258             if (event->object())
0259                 newEvent = new QAccessibleAnnouncementEvent(event->object(), oldEvent->message());
0260             else
0261                 newEvent = new QAccessibleAnnouncementEvent(event->accessibleInterface(),
0262                                                             oldEvent->message());
0263             newEvent->setPoliteness(oldEvent->politeness());
0264             ev = newEvent;
0265         } else {
0266             if (event->object())
0267                 ev = new QAccessibleEvent(event->object(), event->type());
0268             else
0269                 ev = new QAccessibleEvent(event->accessibleInterface(), event->type());
0270         }
0271 
0272         if (ev->type() != QAccessible::ObjectDestroyed)
0273             ev->setChild(event->child());
0274         return ev;
0275     }
0276 
0277     Q_TESTLIB_EXPORT static EventList &eventList();
0278     Q_TESTLIB_EXPORT static QTestAccessibility *&instance();
0279 
0280 private:
0281     static QString msgAccessibilityEventListMismatch(const EventList &haystack,
0282                                                      const QAccessibleEvent *needle)
0283     {
0284         QString rc;
0285         QDebug str = QDebug(&rc).nospace();
0286         str << "Event " << *needle << "\n"
0287             <<  " not found at head of event list of size " << haystack.size() << " :\n";
0288         for (const QAccessibleEvent *e : haystack)
0289             str << ' ' << *e << "\n";
0290         return rc;
0291     }
0292     std::function<void(QAccessibleEvent *event)> m_updateHandler = [](QAccessibleEvent *) { ; };
0293 
0294 };
0295 
0296 #if QT_DEPRECATED_SINCE(6, 11)
0297 using EventList QT_DEPRECATED_VERSION_X_6_11("Use QTestAccessibility::EventList")
0298     = QTestAccessibility::EventList;
0299 
0300 QT_DEPRECATED_VERSION_X_6_11("This was an internal function for QTestAccessibility. "
0301                              "Only QAccessibleEvent is allowed to define operator== for itself, "
0302                              "and it doesn't, so use a named function instead")
0303 inline bool operator==(const QAccessibleEvent &l, const QAccessibleEvent &r)
0304 {
0305     return QTestAccessibility_cmpEvent(l, r);
0306 }
0307 #endif // QT_DEPRECATED_SINCE(6, 11)
0308 
0309 QT_END_NAMESPACE
0310 
0311 #endif // QT_CONFIG(accessibility)
0312 #endif // QTESTACCESSIBLE_H