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