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