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