Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qaccessiblewidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#if QT_CONFIG(accessibility)
8
9#include "qapplication.h"
10#if QT_CONFIG(groupbox)
11#include "qgroupbox.h"
12#endif
13#if QT_CONFIG(label)
14#include "qlabel.h"
15#endif
16#if QT_CONFIG(tooltip)
17#include "qtooltip.h"
18#endif
19#if QT_CONFIG(whatsthis)
20#include "qwhatsthis.h"
21#endif
22#include "qwidget.h"
23#include "qdebug.h"
24#include <qmath.h>
25#if QT_CONFIG(rubberband)
26#include <QRubberBand>
27#endif
28#include <QFocusFrame>
29#if QT_CONFIG(menu)
30#include <QMenu>
31#endif
32#include <QtGui/private/qaccessiblehelper_p.h>
33#include <QtWidgets/private/qwidget_p.h>
34
35#include <qpa/qplatformwindow.h>
36
37QT_BEGIN_NAMESPACE
38
39using namespace Qt::StringLiterals;
40
41QWidgetList _q_ac_childWidgets(const QWidget *widget);
42
43static QString buddyString(const QWidget *widget)
44{
45 if (!widget)
46 return QString();
47 QWidget *parent = widget->parentWidget();
48 if (!parent)
49 return QString();
50#if QT_CONFIG(shortcut) && QT_CONFIG(label)
51 for (QObject *o : parent->children()) {
52 QLabel *label = qobject_cast<QLabel*>(o);
53 if (label && label->buddy() == widget)
54 return label->text();
55 }
56#endif
57
58#if QT_CONFIG(groupbox)
59 QGroupBox *groupbox = qobject_cast<QGroupBox*>(parent);
60 if (groupbox)
61 return groupbox->title();
62#endif
63
64 return QString();
65}
66
67QString qt_accHotKey(const QString &text)
68{
69#ifndef QT_NO_SHORTCUT
70 return QKeySequence::mnemonic(text).toString(QKeySequence::NativeText);
71#else
72 Q_UNUSED(text);
73#endif
74
75 return QString();
76}
77
78// ### inherit QAccessibleObjectPrivate
79class QAccessibleWidgetPrivate
80{
81public:
82 QAccessibleWidgetPrivate()
83 :role(QAccessible::Client)
84 {}
85
86 QAccessible::Role role;
87 QString name;
88 QStringList primarySignals;
89};
90
91/*!
92 \class QAccessibleWidget
93 \brief The QAccessibleWidget class implements the QAccessibleInterface for QWidgets.
94
95 \ingroup accessibility
96 \inmodule QtWidgets
97
98 This class is part of \l {Accessibility for QWidget Applications}.
99
100 This class is convenient to use as a base class for custom
101 implementations of QAccessibleInterfaces that provide information
102 about widget objects.
103
104 The class provides functions to retrieve the parentObject() (the
105 widget's parent widget), and the associated widget(). Controlling
106 signals can be added with addControllingSignal(), and setters are
107 provided for various aspects of the interface implementation, for
108 example setValue(), setDescription(), setAccelerator(), and
109 setHelp().
110
111 \sa QAccessible, QAccessibleObject
112*/
113
114/*!
115 Creates a QAccessibleWidget object for widget \a w.
116 \a role is an optional parameter that sets the object's role property.
117*/
118QAccessibleWidget::QAccessibleWidget(QWidget *w, QAccessible::Role role)
119: QAccessibleObject(w)
120{
121 Q_ASSERT(widget());
122 d = new QAccessibleWidgetPrivate();
123 d->role = role;
124}
125
126/*!
127 Creates a QAccessibleWidget object for widget \a w.
128 \a role and \a name are optional parameters that set the object's
129 role and name properties.
130*/
131QAccessibleWidget::QAccessibleWidget(QWidget *w, QAccessible::Role role, const QString &name)
132 : QAccessibleWidget(w, role)
133{
134 d->name = name;
135}
136
137/*! \reimp */
138bool QAccessibleWidget::isValid() const
139{
140 if (!object() || static_cast<QWidget *>(object())->d_func()->data.in_destructor)
141 return false;
142 return QAccessibleObject::isValid();
143}
144
145/*! \reimp */
146QWindow *QAccessibleWidget::window() const
147{
148 const QWidget *w = widget();
149 Q_ASSERT(w);
150 QWindow *result = w->windowHandle();
151 if (!result) {
152 if (const QWidget *nativeParent = w->nativeParentWidget())
153 result = nativeParent->windowHandle();
154 }
155 return result;
156}
157
158/*!
159 Destroys this object.
160*/
161QAccessibleWidget::~QAccessibleWidget()
162{
163 delete d;
164}
165
166/*!
167 Returns the associated widget.
168*/
169QWidget *QAccessibleWidget::widget() const
170{
171 return qobject_cast<QWidget*>(object());
172}
173
174/*!
175 Returns the associated widget's parent object, which is either the
176 parent widget, or qApp for top-level widgets.
177*/
178QObject *QAccessibleWidget::parentObject() const
179{
180 QWidget *w = widget();
181 if (!w || w->isWindow() || !w->parentWidget())
182 return qApp;
183 return w->parent();
184}
185
186/*! \reimp */
187QRect QAccessibleWidget::rect() const
188{
189 QWidget *w = widget();
190 if (!w->isVisible())
191 return QRect();
192 QPoint wpos = w->mapToGlobal(QPoint(0, 0));
193
194 return QRect(wpos.x(), wpos.y(), w->width(), w->height());
195}
196
197/*!
198 Registers \a signal as a controlling signal.
199
200 An object is a Controller to any other object connected to a
201 controlling signal.
202*/
203void QAccessibleWidget::addControllingSignal(const QString &signal)
204{
205 QByteArray s = QMetaObject::normalizedSignature(signal.toLatin1());
206 if (Q_UNLIKELY(object()->metaObject()->indexOfSignal(s) < 0))
207 qWarning("Signal %s unknown in %s", s.constData(), object()->metaObject()->className());
208 d->primarySignals << QLatin1StringView(s);
209}
210
211static inline bool isAncestor(const QObject *obj, const QObject *child)
212{
213 while (child) {
214 if (child == obj)
215 return true;
216 child = child->parent();
217 }
218 return false;
219}
220
221/*! \reimp */
222QList<std::pair<QAccessibleInterface *, QAccessible::Relation>>
223QAccessibleWidget::relations(QAccessible::Relation match /*= QAccessible::AllRelations*/) const
224{
225 QList<std::pair<QAccessibleInterface *, QAccessible::Relation>> rels;
226 if (match & QAccessible::Label) {
227 const QAccessible::Relation rel = QAccessible::Label;
228#if QT_CONFIG(shortcut) && QT_CONFIG(label)
229 for (QLabel *label : std::as_const(widget()->d_func()->labels)) {
230 Q_ASSERT(label);
231 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(label);
232 rels.emplace_back(iface, rel);
233 }
234#endif
235#if QT_CONFIG(groupbox)
236 QGroupBox *groupbox = qobject_cast<QGroupBox *>(widget()->parentWidget());
237 if (groupbox && !groupbox->title().isEmpty()) {
238 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(groupbox);
239 rels.emplace_back(iface, rel);
240 }
241#endif
242 }
243
244 if (match & QAccessible::Controlled) {
245 QObjectList allReceivers;
246 QObject *connectionObject = object();
247 for (int sig = 0; sig < d->primarySignals.size(); ++sig) {
248 const QObjectList receivers = connectionObject->d_func()->receiverList(d->primarySignals.at(sig).toLatin1());
249 allReceivers += receivers;
250 }
251
252 allReceivers.removeAll(object()); //### The object might connect to itself internally
253
254 for (int i = 0; i < allReceivers.size(); ++i) {
255 const QAccessible::Relation rel = QAccessible::Controlled;
256 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(allReceivers.at(i));
257 if (iface)
258 rels.emplace_back(iface, rel);
259 }
260 }
261
262 return rels;
263}
264
265/*! \reimp */
266QAccessibleInterface *QAccessibleWidget::parent() const
267{
268 return QAccessible::queryAccessibleInterface(parentObject());
269}
270
271/*! \reimp */
272QAccessibleInterface *QAccessibleWidget::child(int index) const
273{
274 Q_ASSERT(widget());
275 QWidgetList childList = _q_ac_childWidgets(widget());
276 if (index >= 0 && index < childList.size())
277 return QAccessible::queryAccessibleInterface(childList.at(index));
278 return nullptr;
279}
280
281/*! \reimp */
282QAccessibleInterface *QAccessibleWidget::focusChild() const
283{
284 if (widget()->hasFocus())
285 return QAccessible::queryAccessibleInterface(object());
286
287 QWidget *fw = widget()->focusWidget();
288 if (!fw)
289 return nullptr;
290
291 if (isAncestor(widget(), fw)) {
292 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(fw);
293 if (!iface || iface == this || !iface->focusChild())
294 return iface;
295 return iface->focusChild();
296 }
297 return nullptr;
298}
299
300/*! \reimp */
301int QAccessibleWidget::childCount() const
302{
303 QWidgetList cl = _q_ac_childWidgets(widget());
304 return cl.size();
305}
306
307/*! \reimp */
308int QAccessibleWidget::indexOfChild(const QAccessibleInterface *child) const
309{
310 if (!child)
311 return -1;
312 QWidgetList cl = _q_ac_childWidgets(widget());
313 return cl.indexOf(qobject_cast<QWidget *>(child->object()));
314}
315
316/*! \reimp */
317QString QAccessibleWidget::text(QAccessible::Text t) const
318{
319 QString str;
320
321 switch (t) {
322 case QAccessible::Name:
323 if (!d->name.isEmpty()) {
324 str = d->name;
325 } else if (!widget()->accessibleName().isEmpty()) {
326 str = widget()->accessibleName();
327 } else if (widget()->isWindow()) {
328 if (QWindow *window = widget()->windowHandle()) {
329 if (QPlatformWindow *platformWindow = window->handle())
330 str = platformWindow->windowTitle();
331 }
332 } else {
333 str = qt_accStripAmp(buddyString(widget()));
334 }
335 break;
336 case QAccessible::Description:
337 str = widget()->accessibleDescription();
338#if QT_CONFIG(tooltip)
339 if (str.isEmpty())
340 str = widget()->toolTip();
341#endif
342 break;
343 case QAccessible::Identifier:
344 str = widget()->accessibleIdentifier();
345 break;
346 case QAccessible::Help:
347#if QT_CONFIG(whatsthis)
348 str = widget()->whatsThis();
349#endif
350 break;
351 case QAccessible::Accelerator:
352 str = qt_accHotKey(buddyString(widget()));
353 break;
354 case QAccessible::Value:
355 break;
356 default:
357 break;
358 }
359 return str;
360}
361
362/*! \reimp */
363QStringList QAccessibleWidget::actionNames() const
364{
365 QStringList names;
366 if (widget()->isEnabled()) {
367 if (widget()->focusPolicy() != Qt::NoFocus)
368 names << setFocusAction();
369 if (widget()->contextMenuPolicy() == Qt::ActionsContextMenu && widget()->actions().size() > 0)
370 names << showMenuAction();
371 }
372 return names;
373}
374
375/*! \reimp */
376void QAccessibleWidget::doAction(const QString &actionName)
377{
378 if (!widget()->isEnabled())
379 return;
380
381 if (actionName == setFocusAction()) {
382 if (widget()->isWindow())
383 widget()->activateWindow();
384 widget()->setFocus();
385 } else if (actionName == showMenuAction()) {
386 QContextMenuEvent e(QContextMenuEvent::Other,
387 QPoint(), widget()->mapToGlobal(QPoint()),
388 QGuiApplication::keyboardModifiers());
389 QCoreApplication::sendEvent(widget(), &e);
390 }
391}
392
393/*! \reimp */
394QStringList QAccessibleWidget::keyBindingsForAction(const QString & /* actionName */) const
395{
396 return QStringList();
397}
398
399/*! \reimp */
400QAccessible::Role QAccessibleWidget::role() const
401{
402 return d->role;
403}
404
405/*! \reimp */
406QAccessible::State QAccessibleWidget::state() const
407{
408 QAccessible::State state;
409
410 QWidget *w = widget();
411 if (w->testAttribute(Qt::WA_WState_Visible) == false)
412 state.invisible = true;
413 if (w->focusPolicy() != Qt::NoFocus)
414 state.focusable = true;
415 if (w->hasFocus())
416 state.focused = true;
417 if (!w->isEnabled())
418 state.disabled = true;
419 if (w->isWindow()) {
420 if (w->windowFlags() & Qt::WindowSystemMenuHint)
421 state.movable = true;
422 if (w->minimumSize() != w->maximumSize())
423 state.sizeable = true;
424 if (w->isActiveWindow())
425 state.active = true;
426 }
427
428 return state;
429}
430
431/*! \reimp */
432QColor QAccessibleWidget::foregroundColor() const
433{
434 return widget()->palette().color(widget()->foregroundRole());
435}
436
437/*! \reimp */
438QColor QAccessibleWidget::backgroundColor() const
439{
440 return widget()->palette().color(widget()->backgroundRole());
441}
442
443/*! \reimp */
444void *QAccessibleWidget::interface_cast(QAccessible::InterfaceType t)
445{
446 if (t == QAccessible::ActionInterface)
447 return static_cast<QAccessibleActionInterface*>(this);
448 return nullptr;
449}
450
451// QAccessibleWidgetV2 implementation
452
453QAccessibleWidgetV2::QAccessibleWidgetV2(QWidget *object, QAccessible::Role role,
454 const QString &name)
455 : QAccessibleWidget(object, role, name)
456{
457}
458
459/*!
460 \class QAccessibleWidgetV2
461 \inmodule QtWidgets
462 \internal
463*/
464QAccessibleWidgetV2::QAccessibleWidgetV2(QWidget *object, QAccessible::Role role)
465 : QAccessibleWidget(object, role)
466{
467}
468
469QAccessibleWidgetV2::~QAccessibleWidgetV2() = default;
470
471/*! \reimp */
472void *QAccessibleWidgetV2::interface_cast(QAccessible::InterfaceType t)
473{
474 if (t == QAccessible::AttributesInterface)
475 return static_cast<QAccessibleAttributesInterface *>(this);
476 return QAccessibleWidget::interface_cast(t);
477}
478
479/*! \reimp */
480QList<QAccessible::Attribute> QAccessibleWidgetV2::attributeKeys() const
481{
482 return { QAccessible::Attribute::Locale };
483}
484
485/*! \reimp */
486QVariant QAccessibleWidgetV2::attributeValue(QAccessible::Attribute key) const
487{
488 if (key == QAccessible::Attribute::Locale)
489 return QVariant::fromValue(widget()->locale());
490
491 return QVariant();
492}
493
494QT_END_NAMESPACE
495
496#endif // QT_CONFIG(accessibility)