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