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
complexwidgets.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#include <qaccessible.h>
7#include <qapplication.h>
8#include <qevent.h>
9#if QT_CONFIG(itemviews)
10#include <qheaderview.h>
11#endif
12#if QT_CONFIG(tabbar)
13#include <qtabbar.h>
14#include <private/qtabbar_p.h>
15#endif
16#if QT_CONFIG(combobox)
17#include <qcombobox.h>
18#endif
19#if QT_CONFIG(lineedit)
20#include <qlineedit.h>
21#endif
22#include <qstyle.h>
23#include <qstyleoption.h>
24#if QT_CONFIG(tooltip)
25#include <qtooltip.h>
26#endif
27#if QT_CONFIG(whatsthis)
28#include <qwhatsthis.h>
29#endif
30#include <QAbstractScrollArea>
31#if QT_CONFIG(scrollarea)
32#include <QScrollArea>
33#endif
34#if QT_CONFIG(scrollbar)
35#include <QScrollBar>
36#endif
37#include <QDebug>
38
39#if QT_CONFIG(accessibility)
40
41#include <QtGui/private/qaccessiblehelper_p.h>
42
43QT_BEGIN_NAMESPACE
44
45using namespace Qt::StringLiterals;
46
47QString qt_accHotKey(const QString &text);
48
49#if QT_CONFIG(tabbar)
50/*!
51 \class QAccessibleTabBar
52 \brief The QAccessibleTabBar class implements the QAccessibleInterface for tab bars.
53 \internal
54
55 \ingroup accessibility
56*/
57
58class QAccessibleTabButton: public QAccessibleInterface, public QAccessibleActionInterface
59{
60public:
61 QAccessibleTabButton(QTabBar *parent, int index)
62 : m_parent(parent), m_index(index)
63 {}
64
65 void *interface_cast(QAccessible::InterfaceType t) override {
66 if (t == QAccessible::ActionInterface) {
67 return static_cast<QAccessibleActionInterface*>(this);
68 }
69 return nullptr;
70 }
71
72 QObject *object() const override { return nullptr; }
73 QAccessible::Role role() const override { return QAccessible::PageTab; }
74 QAccessible::State state() const override {
75 if (!isValid()) {
76 QAccessible::State s;
77 s.invalid = true;
78 return s;
79 }
80
81 QAccessible::State s = parent()->state();
82 s.selectable = true;
83 s.focused = (m_index == m_parent->currentIndex());
84 s.selected = s.focused;
85 return s;
86 }
87 QRect rect() const override {
88 if (!isValid())
89 return QRect();
90
91 QPoint tp = m_parent->mapToGlobal(QPoint(0,0));
92 QRect rec = m_parent->tabRect(m_index);
93 rec = QRect(tp.x() + rec.x(), tp.y() + rec.y(), rec.width(), rec.height());
94 return rec;
95 }
96
97 bool isValid() const override {
98 if (m_parent) {
99 if (static_cast<QWidget *>(m_parent.data())->d_func()->data.in_destructor)
100 return false;
101 return m_parent->count() > m_index;
102 }
103 return false;
104 }
105
106 QAccessibleInterface *childAt(int, int) const override { return nullptr; }
107 int childCount() const override { return 0; }
108 int indexOfChild(const QAccessibleInterface *) const override { return -1; }
109
110 QString text(QAccessible::Text t) const override
111 {
112 if (!isValid())
113 return QString();
114 QString str;
115 switch (t) {
116 case QAccessible::Name:
117 str = m_parent->accessibleTabName(m_index);
118 if (str.isEmpty())
119 str = qt_accStripAmp(m_parent->tabText(m_index));
120 break;
121 case QAccessible::Accelerator:
122 str = qt_accHotKey(m_parent->tabText(m_index));
123 break;
124#if QT_CONFIG(tooltip)
125 case QAccessible::Description:
126 str = m_parent->tabToolTip(m_index);
127 break;
128#endif
129#if QT_CONFIG(whatsthis)
130 case QAccessible::Help:
131 str = m_parent->tabWhatsThis(m_index);
132 break;
133#endif
134 default:
135 break;
136 }
137 return str;
138 }
139
140 void setText(QAccessible::Text, const QString &) override {}
141
142 QAccessibleInterface *parent() const override {
143 return QAccessible::queryAccessibleInterface(m_parent.data());
144 }
145 QAccessibleInterface *child(int) const override { return nullptr; }
146
147 // action interface
148 QStringList actionNames() const override
149 {
150 return QStringList(pressAction());
151 }
152
153 void doAction(const QString &actionName) override
154 {
155 if (isValid() && actionName == pressAction())
156 m_parent->setCurrentIndex(m_index);
157 }
158
159 QStringList keyBindingsForAction(const QString &) const override
160 {
161 return QStringList();
162 }
163
164 int index() const { return m_index; }
165
166private:
167 QPointer<QTabBar> m_parent;
168 int m_index;
169
170};
171
172/*!
173 Constructs a QAccessibleTabBar object for \a w.
174*/
175QAccessibleTabBar::QAccessibleTabBar(QWidget *w)
176: QAccessibleWidgetV2(w, QAccessible::PageTabList)
177{
178 Q_ASSERT(tabBar());
179}
180
181QAccessibleTabBar::~QAccessibleTabBar()
182{
183 for (QAccessible::Id id : std::as_const(m_childInterfaces))
184 QAccessible::deleteAccessibleInterface(id);
185}
186
187void *QAccessibleTabBar::interface_cast(QAccessible::InterfaceType t)
188{
189 if (t == QAccessible::SelectionInterface) {
190 return static_cast<QAccessibleSelectionInterface*>(this);
191 }
192 return QAccessibleWidgetV2::interface_cast(t);
193}
194
195/*! Returns the QTabBar. */
196QTabBar *QAccessibleTabBar::tabBar() const
197{
198 return qobject_cast<QTabBar*>(object());
199}
200
201QAccessibleInterface* QAccessibleTabBar::focusChild() const
202{
203 for (int i = 0; i < childCount(); ++i) {
204 if (child(i)->state().focused)
205 return child(i);
206 }
207
208 return nullptr;
209}
210
211QAccessibleInterface* QAccessibleTabBar::child(int index) const
212{
213 if (QAccessible::Id id = m_childInterfaces.value(index))
214 return QAccessible::accessibleInterface(id);
215
216 // first the tabs, then 2 buttons
217 if (index < tabBar()->count()) {
218 QAccessibleTabButton *button = new QAccessibleTabButton(tabBar(), index);
219 QAccessible::registerAccessibleInterface(button);
220 m_childInterfaces.insert(index, QAccessible::uniqueId(button));
221 return button;
222 } else if (index >= tabBar()->count()) {
223 // left button
224 if (index - tabBar()->count() == 0) {
225 return QAccessible::queryAccessibleInterface(tabBar()->d_func()->leftB);
226 }
227 // right button
228 if (index - tabBar()->count() == 1) {
229 return QAccessible::queryAccessibleInterface(tabBar()->d_func()->rightB);
230 }
231 }
232 return nullptr;
233}
234
235int QAccessibleTabBar::indexOfChild(const QAccessibleInterface *child) const
236{
237 if (child->object() && child->object() == tabBar()->d_func()->leftB)
238 return tabBar()->count();
239 if (child->object() && child->object() == tabBar()->d_func()->rightB)
240 return tabBar()->count() + 1;
241 if (child->role() == QAccessible::PageTab) {
242 QAccessibleInterface *parent = child->parent();
243 if (parent == this) {
244 const QAccessibleTabButton *tabButton = static_cast<const QAccessibleTabButton *>(child);
245 return tabButton->index();
246 }
247 }
248 return -1;
249}
250
251int QAccessibleTabBar::childCount() const
252{
253 // tabs + scroll buttons
254 return tabBar()->count() + 2;
255}
256
257QString QAccessibleTabBar::text(QAccessible::Text t) const
258{
259 if (t == QAccessible::Name) {
260 const QTabBar *tBar = tabBar();
261 int idx = tBar->currentIndex();
262 QString str = tBar->accessibleTabName(idx);
263 if (str.isEmpty())
264 str = qt_accStripAmp(tBar->tabText(idx));
265 return str;
266 } else if (t == QAccessible::Accelerator) {
267 return qt_accHotKey(tabBar()->tabText(tabBar()->currentIndex()));
268 }
269 return QString();
270}
271
272int QAccessibleTabBar::selectedItemCount() const
273{
274 if (tabBar()->currentIndex() >= 0)
275 return 1;
276 return 0;
277}
278
279QList<QAccessibleInterface*> QAccessibleTabBar::selectedItems() const
280{
281 QList<QAccessibleInterface*> items;
282 QAccessibleInterface *selected = selectedItem(0);
283 if (selected)
284 items.push_back(selected);
285 return items;
286}
287
288QAccessibleInterface* QAccessibleTabBar::selectedItem(int selectionIndex) const
289{
290 const int currentIndex = tabBar()->currentIndex();
291 if (selectionIndex != 0 || currentIndex < 0)
292 return nullptr;
293 return child(currentIndex);
294}
295
296bool QAccessibleTabBar::isSelected(QAccessibleInterface *childItem) const
297{
298 return childItem && selectedItem(0) == childItem;
299}
300
301bool QAccessibleTabBar::select(QAccessibleInterface *childItem)
302{
303 const int childIndex = indexOfChild(childItem);
304 if (childIndex >= 0) {
305 tabBar()->setCurrentIndex(childIndex);
306 return true;
307 }
308 return false;
309}
310
311bool QAccessibleTabBar::unselect(QAccessibleInterface *)
312{
313 return false;
314}
315
316bool QAccessibleTabBar::selectAll()
317{
318 return false;
319}
320
321bool QAccessibleTabBar::clear()
322{
323 return false;
324}
325
326#endif // QT_CONFIG(tabbar)
327
328#if QT_CONFIG(combobox)
329/*!
330 \class QAccessibleComboBox
331 \brief The QAccessibleComboBox class implements the QAccessibleInterface for editable and read-only combo boxes.
332 \internal
333
334 \ingroup accessibility
335*/
336
337/*!
338 Constructs a QAccessibleComboBox object for \a w.
339*/
340QAccessibleComboBox::QAccessibleComboBox(QWidget *w)
341: QAccessibleWidgetV2(w, QAccessible::ComboBox)
342{
343 Q_ASSERT(comboBox());
344}
345
346/*!
347 Returns the combobox.
348*/
349QComboBox *QAccessibleComboBox::comboBox() const
350{
351 return qobject_cast<QComboBox *>(object());
352}
353
354QAccessibleInterface *QAccessibleComboBox::child(int index) const
355{
356 if (QComboBox *cBox = comboBox()) {
357 if (index == 0) {
358 QAbstractItemView *view = cBox->view();
359 //QWidget *parent = view ? view->parentWidget() : 0;
360 return QAccessible::queryAccessibleInterface(view);
361 } else if (index == 1 && cBox->isEditable()) {
362 return QAccessible::queryAccessibleInterface(cBox->lineEdit());
363 }
364 }
365 return nullptr;
366}
367
368int QAccessibleComboBox::childCount() const
369{
370 // list and text edit
371 if (QComboBox *cBox = comboBox())
372 return (cBox->isEditable()) ? 2 : 1;
373 return 0;
374}
375
376QAccessibleInterface *QAccessibleComboBox::childAt(int x, int y) const
377{
378 if (QComboBox *cBox = comboBox()) {
379 if (cBox->isEditable() && cBox->lineEdit()->rect().contains(x, y))
380 return child(1);
381 }
382 return nullptr;
383}
384
385int QAccessibleComboBox::indexOfChild(const QAccessibleInterface *child) const
386{
387 if (QComboBox *cBox = comboBox()) {
388 if (cBox->view() == child->object())
389 return 0;
390 if (cBox->isEditable() && cBox->lineEdit() == child->object())
391 return 1;
392 }
393 return -1;
394}
395
396QAccessibleInterface *QAccessibleComboBox::focusChild() const
397{
398 // The editable combobox is the focus proxy of its lineedit, so the
399 // lineedit itself never gets focus. But it is the accessible focus
400 // child of an editable combobox.
401 if (QComboBox *cBox = comboBox()) {
402 if (cBox->isEditable())
403 return child(1);
404 }
405 return nullptr;
406}
407
408/*! \reimp */
409QString QAccessibleComboBox::text(QAccessible::Text t) const
410{
411 QString str;
412 if (QComboBox *cBox = comboBox()) {
413 switch (t) {
414 case QAccessible::Name:
415#ifndef Q_OS_UNIX // on Linux we use relations for this, name is text (fall through to Value)
416 str = QAccessibleWidgetV2::text(t);
417 break;
418#endif
419 case QAccessible::Value:
420 if (cBox->isEditable())
421 str = cBox->lineEdit()->text();
422 else
423 str = cBox->currentText();
424 break;
425#ifndef QT_NO_SHORTCUT
426 case QAccessible::Accelerator:
427 str = QKeySequence(Qt::Key_Down).toString(QKeySequence::NativeText);
428 break;
429#endif
430 default:
431 break;
432 }
433 if (str.isEmpty())
434 str = QAccessibleWidgetV2::text(t);
435 }
436 return str;
437}
438
439QAccessible::State QAccessibleComboBox::state() const
440{
441 QAccessible::State s = QAccessibleWidgetV2::state();
442
443 if (QComboBox *cBox = comboBox()) {
444 s.expandable = true;
445 s.expanded = isValid() && cBox->view() && cBox->view()->isVisible();
446 s.editable = cBox->isEditable();
447 }
448 return s;
449}
450
451QStringList QAccessibleComboBox::actionNames() const
452{
453 return QStringList() << showMenuAction() << pressAction();
454}
455
456QString QAccessibleComboBox::localizedActionDescription(const QString &actionName) const
457{
458 if (actionName == showMenuAction() || actionName == pressAction())
459 return QComboBox::tr("Open the combo box selection popup");
460 return QString();
461}
462
463void QAccessibleComboBox::doAction(const QString &actionName)
464{
465 if (QComboBox *cBox = comboBox()) {
466 if (actionName == showMenuAction() || actionName == pressAction()) {
467 if (cBox->view()->isVisible()) {
468#if defined(Q_OS_ANDROID)
469 const auto list = child(0)->tableInterface();
470 if (list && list->selectedRowCount() > 0) {
471 cBox->setCurrentIndex(list->selectedRows().at(0));
472 }
473 cBox->setFocus();
474#endif
475 cBox->hidePopup();
476 } else {
477 cBox->showPopup();
478#if defined(Q_OS_ANDROID)
479 const auto list = child(0)->tableInterface();
480 if (list && list->selectedRowCount() > 0) {
481 auto selectedCells = list->selectedCells();
482 QAccessibleEvent ev(selectedCells.at(0),QAccessible::Focus);
483 QAccessible::updateAccessibility(&ev);
484 }
485#endif
486 }
487 }
488 }
489}
490
491QStringList QAccessibleComboBox::keyBindingsForAction(const QString &/*actionName*/) const
492{
493 return QStringList();
494}
495
496#endif // QT_CONFIG(combobox)
497
498#if QT_CONFIG(scrollarea)
499// ======================= QAccessibleAbstractScrollArea =======================
500QAccessibleAbstractScrollArea::QAccessibleAbstractScrollArea(QWidget *widget)
501 : QAccessibleWidgetV2(widget, QAccessible::Client)
502{
503 Q_ASSERT(qobject_cast<QAbstractScrollArea *>(widget));
504}
505
506QAccessibleInterface *QAccessibleAbstractScrollArea::child(int index) const
507{
508 return QAccessible::queryAccessibleInterface(accessibleChildren().at(index));
509}
510
511int QAccessibleAbstractScrollArea::childCount() const
512{
513 return accessibleChildren().size();
514}
515
516int QAccessibleAbstractScrollArea::indexOfChild(const QAccessibleInterface *child) const
517{
518 if (!child || !child->object())
519 return -1;
520 return accessibleChildren().indexOf(qobject_cast<QWidget *>(child->object()));
521}
522
523bool QAccessibleAbstractScrollArea::isValid() const
524{
525 return (QAccessibleWidgetV2::isValid() && abstractScrollArea() && abstractScrollArea()->viewport());
526}
527
528QAccessibleInterface *QAccessibleAbstractScrollArea::childAt(int x, int y) const
529{
530 if (!abstractScrollArea()->isVisible())
531 return nullptr;
532
533 for (int i = 0; i < childCount(); ++i) {
534 QPoint wpos = accessibleChildren().at(i)->mapToGlobal(QPoint(0, 0));
535 QRect rect = QRect(wpos, accessibleChildren().at(i)->size());
536 if (rect.contains(x, y))
537 return child(i);
538 }
539 return nullptr;
540}
541
542QAbstractScrollArea *QAccessibleAbstractScrollArea::abstractScrollArea() const
543{
544 return static_cast<QAbstractScrollArea *>(object());
545}
546
547QWidgetList QAccessibleAbstractScrollArea::accessibleChildren() const
548{
549 QWidgetList children;
550
551 // Viewport.
552 QWidget * viewport = abstractScrollArea()->viewport();
553 if (viewport)
554 children.append(viewport);
555
556 // Horizontal scrollBar container.
557 QScrollBar *horizontalScrollBar = abstractScrollArea()->horizontalScrollBar();
558 if (horizontalScrollBar && horizontalScrollBar->isVisible()) {
559 QWidget *scrollBarParent = horizontalScrollBar->parentWidget();
560 // Add container only if scroll bar is in the container
561 if (elementType(scrollBarParent) == HorizontalContainer)
562 children.append(scrollBarParent);
563 }
564
565 // Vertical scrollBar container.
566 QScrollBar *verticalScrollBar = abstractScrollArea()->verticalScrollBar();
567 if (verticalScrollBar && verticalScrollBar->isVisible()) {
568 QWidget *scrollBarParent = verticalScrollBar->parentWidget();
569 // Add container only if scroll bar is in the container
570 if (elementType(scrollBarParent) == VerticalContainer)
571 children.append(scrollBarParent);
572 }
573
574 // CornerWidget.
575 QWidget *cornerWidget = abstractScrollArea()->cornerWidget();
576 if (cornerWidget && cornerWidget->isVisible())
577 children.append(cornerWidget);
578
579 return children;
580}
581
582QAccessibleAbstractScrollArea::AbstractScrollAreaElement
583QAccessibleAbstractScrollArea::elementType(QWidget *widget) const
584{
585 if (!widget)
586 return Undefined;
587
588 if (widget == abstractScrollArea())
589 return Self;
590 if (widget == abstractScrollArea()->viewport())
591 return Viewport;
592 if (widget->objectName() == "qt_scrollarea_hcontainer"_L1)
593 return HorizontalContainer;
594 if (widget->objectName() == "qt_scrollarea_vcontainer"_L1)
595 return VerticalContainer;
596 if (widget == abstractScrollArea()->cornerWidget())
597 return CornerWidget;
598
599 return Undefined;
600}
601
602bool QAccessibleAbstractScrollArea::isLeftToRight() const
603{
604 return abstractScrollArea()->isLeftToRight();
605}
606
607// ======================= QAccessibleScrollArea ===========================
608QAccessibleScrollArea::QAccessibleScrollArea(QWidget *widget)
609 : QAccessibleAbstractScrollArea(widget)
610{
611 Q_ASSERT(qobject_cast<QScrollArea *>(widget));
612}
613#endif // QT_CONFIG(scrollarea)
614
615QT_END_NAMESPACE
616
617#endif // QT_CONFIG(accessibility)