diff options
Diffstat (limited to 'src/widgets')
117 files changed, 10057 insertions, 2536 deletions
diff --git a/src/widgets/accessible/accessible.pri b/src/widgets/accessible/accessible.pri index 9fb2f18950..bcdfbd639c 100644 --- a/src/widgets/accessible/accessible.pri +++ b/src/widgets/accessible/accessible.pri @@ -1,6 +1,23 @@ # Qt accessibility module contains(QT_CONFIG, accessibility) { - HEADERS += accessible/qaccessiblewidget.h - SOURCES += accessible/qaccessiblewidget.cpp + HEADERS += \ + accessible/qaccessiblewidget.h \ + accessible/qaccessiblewidgetfactory_p.h \ + accessible/complexwidgets.h \ + accessible/itemviews.h \ + accessible/qaccessiblemenu.h \ + accessible/qaccessiblewidgets.h \ + accessible/rangecontrols.h \ + accessible/simplewidgets.h + + SOURCES += \ + accessible/qaccessiblewidget.cpp \ + accessible/qaccessiblewidgetfactory.cpp \ + accessible/complexwidgets.cpp \ + accessible/itemviews.cpp \ + accessible/qaccessiblemenu.cpp \ + accessible/qaccessiblewidgets.cpp \ + accessible/rangecontrols.cpp \ + accessible/simplewidgets.cpp } diff --git a/src/widgets/accessible/complexwidgets.cpp b/src/widgets/accessible/complexwidgets.cpp new file mode 100644 index 0000000000..f7c2ac0cf1 --- /dev/null +++ b/src/widgets/accessible/complexwidgets.cpp @@ -0,0 +1,469 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "complexwidgets.h" + +#include <qaccessible.h> +#include <qapplication.h> +#include <qabstractbutton.h> +#include <qevent.h> +#include <qheaderview.h> +#include <qtabbar.h> +#include <qcombobox.h> +#include <qlistview.h> +#include <qtableview.h> +#include <qlineedit.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qtooltip.h> +#include <qwhatsthis.h> +#include <qtreeview.h> +#include <private/qtabbar_p.h> +#include <QAbstractScrollArea> +#include <QScrollArea> +#include <QScrollBar> +#include <QDebug> + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +QString qt_accStripAmp(const QString &text); + +#ifndef QT_NO_TABBAR +/*! + \class QAccessibleTabBar + \brief The QAccessibleTabBar class implements the QAccessibleInterface for tab bars. + \internal + + \ingroup accessibility +*/ + +class QAccessibleTabButton: public QAccessibleInterface, public QAccessibleActionInterface +{ +public: + QAccessibleTabButton(QTabBar *parent, int index) + : m_parent(parent), m_index(index) + {} + + void *interface_cast(QAccessible::InterfaceType t) { + if (t == QAccessible::ActionInterface) { + return static_cast<QAccessibleActionInterface*>(this); + } + return 0; + } + + QObject *object() const { return 0; } + QAccessible::Role role() const { return QAccessible::PageTab; } + QAccessible::State state() const { + QAccessibleInterface *parentInterface = parent(); + QAccessible::State state = parentInterface->state(); + return state; + } + QRect rect() const { + if (!isValid()) + return QRect(); + + QPoint tp = m_parent->mapToGlobal(QPoint(0,0)); + QRect rec = m_parent->tabRect(m_index); + rec = QRect(tp.x() + rec.x(), tp.y() + rec.y(), rec.width(), rec.height()); + return rec; + } + + bool isValid() const { return true; }// (!m_parent.isNull()) && m_parent->count() > m_index; } + + QAccessibleInterface *childAt(int, int) const { return 0; } + int childCount() const { return 0; } + int indexOfChild(const QAccessibleInterface *) const { return -1; } + + QString text(QAccessible::Text t) const + { + if (t == QAccessible::Name) + return qt_accStripAmp(m_parent->tabText(m_index)); + else if (t == QAccessible::Description) + return m_parent->tabToolTip(m_index); + else if (t == QAccessible::Help) + return m_parent->tabWhatsThis(m_index); + return QString(); + } + + void setText(QAccessible::Text, const QString &) {} + + QAccessibleInterface *parent() const { + return QAccessible::queryAccessibleInterface(m_parent); + } + QAccessibleInterface *child(int) const { return 0; } + // action interface + QStringList actionNames() const + { + return QStringList(pressAction()); + } + + void doAction(const QString &actionName) + { + if (actionName == pressAction()) + m_parent->setCurrentIndex(m_index); + } + + QStringList keyBindingsForAction(const QString &) const + { + return QStringList(); + } + + int index() const { return m_index; } + +private: + QPointer<QTabBar> m_parent; + int m_index; + +}; + +/*! + Constructs a QAccessibleTabBar object for \a w. +*/ +QAccessibleTabBar::QAccessibleTabBar(QWidget *w) +: QAccessibleWidget(w, QAccessible::PageTabList) +{ + Q_ASSERT(tabBar()); +} + +QAccessibleTabBar::~QAccessibleTabBar() +{ + foreach (QAccessible::Id id, m_childInterfaces.values()) + QAccessible::deleteAccessibleInterface(id); +} + +/*! Returns the QTabBar. */ +QTabBar *QAccessibleTabBar::tabBar() const +{ + return qobject_cast<QTabBar*>(object()); +} + +QAccessibleInterface* QAccessibleTabBar::child(int index) const +{ + if (QAccessible::Id id = m_childInterfaces.value(index)) + return QAccessible::accessibleInterface(id); + + // first the tabs, then 2 buttons + if (index < tabBar()->count()) { + QAccessibleTabButton *button = new QAccessibleTabButton(tabBar(), index); + QAccessible::registerAccessibleInterface(button); + m_childInterfaces.insert(index, QAccessible::uniqueId(button)); + return button; + } else if (index >= tabBar()->count()) { + // left button + if (index - tabBar()->count() == 0) { + return QAccessible::queryAccessibleInterface(tabBar()->d_func()->leftB); + } + // right button + if (index - tabBar()->count() == 1) { + return QAccessible::queryAccessibleInterface(tabBar()->d_func()->rightB); + } + } + return 0; +} + +int QAccessibleTabBar::indexOfChild(const QAccessibleInterface *child) const +{ + if (child->object() && child->object() == tabBar()->d_func()->leftB) + return tabBar()->count(); + if (child->object() && child->object() == tabBar()->d_func()->rightB) + return tabBar()->count() + 1; + if (child->role() == QAccessible::PageTab) { + QAccessibleInterface *parent = child->parent(); + if (parent == this) { + const QAccessibleTabButton *tabButton = static_cast<const QAccessibleTabButton *>(child); + return tabButton->index(); + } + } + return -1; +} + +int QAccessibleTabBar::childCount() const +{ + // tabs + scroll buttons + return tabBar()->count() + 2; +} + +QString QAccessibleTabBar::text(QAccessible::Text t) const +{ + if (t == QAccessible::Name) { + return qt_accStripAmp(tabBar()->tabText(tabBar()->currentIndex())); + } + return QString(); +} + +#endif // QT_NO_TABBAR + +#ifndef QT_NO_COMBOBOX +/*! + \class QAccessibleComboBox + \brief The QAccessibleComboBox class implements the QAccessibleInterface for editable and read-only combo boxes. + \internal + + \ingroup accessibility +*/ + +/*! + Constructs a QAccessibleComboBox object for \a w. +*/ +QAccessibleComboBox::QAccessibleComboBox(QWidget *w) +: QAccessibleWidget(w, QAccessible::ComboBox) +{ + Q_ASSERT(comboBox()); +} + +/*! + Returns the combobox. +*/ +QComboBox *QAccessibleComboBox::comboBox() const +{ + return qobject_cast<QComboBox*>(object()); +} + +QAccessibleInterface *QAccessibleComboBox::child(int index) const +{ + if (index == 0) { + QAbstractItemView *view = comboBox()->view(); + //QWidget *parent = view ? view->parentWidget() : 0; + return QAccessible::queryAccessibleInterface(view); + } else if (index == 1 && comboBox()->isEditable()) { + return QAccessible::queryAccessibleInterface(comboBox()->lineEdit()); + } + return 0; +} + +int QAccessibleComboBox::childCount() const +{ + // list and text edit + return comboBox()->isEditable() ? 2 : 1; +} + +QAccessibleInterface *QAccessibleComboBox::childAt(int x, int y) const +{ + if (comboBox()->isEditable() && comboBox()->lineEdit()->rect().contains(x, y)) + return child(1); + return 0; +} + +int QAccessibleComboBox::indexOfChild(const QAccessibleInterface *child) const +{ + if (comboBox()->view() == child->object()) + return 0; + if (comboBox()->isEditable() && comboBox()->lineEdit() == child->object()) + return 1; + return -1; +} + +/*! \reimp */ +QString QAccessibleComboBox::text(QAccessible::Text t) const +{ + QString str; + + switch (t) { + case QAccessible::Name: +#ifndef Q_OS_UNIX // on Linux we use relations for this, name is text (fall through to Value) + str = QAccessibleWidget::text(t); + break; +#endif + case QAccessible::Value: + if (comboBox()->isEditable()) + str = comboBox()->lineEdit()->text(); + else + str = comboBox()->currentText(); + break; +#ifndef QT_NO_SHORTCUT + case QAccessible::Accelerator: + str = QKeySequence(Qt::Key_Down).toString(QKeySequence::NativeText); + break; +#endif + default: + break; + } + if (str.isEmpty()) + str = QAccessibleWidget::text(t); + return str; +} + +QStringList QAccessibleComboBox::actionNames() const +{ + return QStringList() << showMenuAction() << pressAction(); +} + +QString QAccessibleComboBox::localizedActionDescription(const QString &actionName) const +{ + if (actionName == showMenuAction() || actionName == pressAction()) + return QComboBox::tr("Open the combo box selection popup"); + return QString(); +} + +void QAccessibleComboBox::doAction(const QString &actionName) +{ + if (actionName == showMenuAction() || actionName == pressAction()) { + if (comboBox()->view()->isVisible()) { + comboBox()->hidePopup(); + } else { + comboBox()->showPopup(); + } + } +} + +QStringList QAccessibleComboBox::keyBindingsForAction(const QString &/*actionName*/) const +{ + return QStringList(); +} + +#endif // QT_NO_COMBOBOX + +#ifndef QT_NO_SCROLLAREA +// ======================= QAccessibleAbstractScrollArea ======================= +QAccessibleAbstractScrollArea::QAccessibleAbstractScrollArea(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::Client) +{ + Q_ASSERT(qobject_cast<QAbstractScrollArea *>(widget)); +} + +QAccessibleInterface *QAccessibleAbstractScrollArea::child(int index) const +{ + return QAccessible::queryAccessibleInterface(accessibleChildren().at(index)); +} + +int QAccessibleAbstractScrollArea::childCount() const +{ + return accessibleChildren().count(); +} + +int QAccessibleAbstractScrollArea::indexOfChild(const QAccessibleInterface *child) const +{ + if (!child || !child->object()) + return -1; + return accessibleChildren().indexOf(qobject_cast<QWidget *>(child->object())); +} + +bool QAccessibleAbstractScrollArea::isValid() const +{ + return (QAccessibleWidget::isValid() && abstractScrollArea() && abstractScrollArea()->viewport()); +} + +QAccessibleInterface *QAccessibleAbstractScrollArea::childAt(int x, int y) const +{ + if (!abstractScrollArea()->isVisible()) + return 0; + + for (int i = 0; i < childCount(); ++i) { + QPoint wpos = accessibleChildren().at(i)->mapToGlobal(QPoint(0, 0)); + QRect rect = QRect(wpos, accessibleChildren().at(i)->size()); + if (rect.contains(x, y)) + return child(i); + } + return 0; +} + +QAbstractScrollArea *QAccessibleAbstractScrollArea::abstractScrollArea() const +{ + return static_cast<QAbstractScrollArea *>(object()); +} + +QWidgetList QAccessibleAbstractScrollArea::accessibleChildren() const +{ + QWidgetList children; + + // Viewport. + QWidget * viewport = abstractScrollArea()->viewport(); + if (viewport) + children.append(viewport); + + // Horizontal scrollBar container. + QScrollBar *horizontalScrollBar = abstractScrollArea()->horizontalScrollBar(); + if (horizontalScrollBar && horizontalScrollBar->isVisible()) { + children.append(horizontalScrollBar->parentWidget()); + } + + // Vertical scrollBar container. + QScrollBar *verticalScrollBar = abstractScrollArea()->verticalScrollBar(); + if (verticalScrollBar && verticalScrollBar->isVisible()) { + children.append(verticalScrollBar->parentWidget()); + } + + // CornerWidget. + QWidget *cornerWidget = abstractScrollArea()->cornerWidget(); + if (cornerWidget && cornerWidget->isVisible()) + children.append(cornerWidget); + + return children; +} + +QAccessibleAbstractScrollArea::AbstractScrollAreaElement +QAccessibleAbstractScrollArea::elementType(QWidget *widget) const +{ + if (!widget) + return Undefined; + + if (widget == abstractScrollArea()) + return Self; + if (widget == abstractScrollArea()->viewport()) + return Viewport; + if (widget->objectName() == QLatin1String("qt_scrollarea_hcontainer")) + return HorizontalContainer; + if (widget->objectName() == QLatin1String("qt_scrollarea_vcontainer")) + return VerticalContainer; + if (widget == abstractScrollArea()->cornerWidget()) + return CornerWidget; + + return Undefined; +} + +bool QAccessibleAbstractScrollArea::isLeftToRight() const +{ + return abstractScrollArea()->isLeftToRight(); +} + +// ======================= QAccessibleScrollArea =========================== +QAccessibleScrollArea::QAccessibleScrollArea(QWidget *widget) + : QAccessibleAbstractScrollArea(widget) +{ + Q_ASSERT(qobject_cast<QScrollArea *>(widget)); +} +#endif // QT_NO_SCROLLAREA + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/widgets/accessible/complexwidgets.h b/src/widgets/accessible/complexwidgets.h new file mode 100644 index 0000000000..d3df224d95 --- /dev/null +++ b/src/widgets/accessible/complexwidgets.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPLEXWIDGETS_H +#define COMPLEXWIDGETS_H + +#include <QtCore/qpointer.h> +#include <QtWidgets/qaccessiblewidget.h> +#include <QtWidgets/qabstractitemview.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +class QAbstractButton; +class QHeaderView; +class QTabBar; +class QComboBox; +class QTitleBar; +class QAbstractScrollArea; +class QScrollArea; + +#ifndef QT_NO_SCROLLAREA +class QAccessibleAbstractScrollArea : public QAccessibleWidget +{ +public: + explicit QAccessibleAbstractScrollArea(QWidget *widget); + + enum AbstractScrollAreaElement { + Self = 0, + Viewport, + HorizontalContainer, + VerticalContainer, + CornerWidget, + Undefined + }; + + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + bool isValid() const Q_DECL_OVERRIDE; + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + +//protected: + QAbstractScrollArea *abstractScrollArea() const; + +private: + QWidgetList accessibleChildren() const; + AbstractScrollAreaElement elementType(QWidget *widget) const; + bool isLeftToRight() const; +}; + +class QAccessibleScrollArea : public QAccessibleAbstractScrollArea +{ +public: + explicit QAccessibleScrollArea(QWidget *widget); +}; +#endif // QT_NO_SCROLLAREA + +#ifndef QT_NO_TABBAR +class QAccessibleTabBar : public QAccessibleWidget +{ +public: + explicit QAccessibleTabBar(QWidget *w); + ~QAccessibleTabBar(); + + int childCount() const Q_DECL_OVERRIDE; + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + + QAccessibleInterface* child(int index) const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + +protected: + QTabBar *tabBar() const; + mutable QHash<int, QAccessible::Id> m_childInterfaces; +}; +#endif // QT_NO_TABBAR + +#ifndef QT_NO_COMBOBOX +class QAccessibleComboBox : public QAccessibleWidget +{ +public: + explicit QAccessibleComboBox(QWidget *w); + + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + QAccessibleInterface* child(int index) const Q_DECL_OVERRIDE; + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + + // QAccessibleActionInterface + QStringList actionNames() const Q_DECL_OVERRIDE; + QString localizedActionDescription(const QString &actionName) const Q_DECL_OVERRIDE; + void doAction(const QString &actionName) Q_DECL_OVERRIDE; + QStringList keyBindingsForAction(const QString &actionName) const Q_DECL_OVERRIDE; + +protected: + QComboBox *comboBox() const; +}; +#endif // QT_NO_COMBOBOX + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +#endif // COMPLEXWIDGETS_H diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp new file mode 100644 index 0000000000..723a670739 --- /dev/null +++ b/src/widgets/accessible/itemviews.cpp @@ -0,0 +1,1220 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemviews.h" + +#include <qheaderview.h> +#include <qtableview.h> +#include <qlistview.h> +#include <qtreeview.h> +#include <private/qtreewidget_p.h> + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ITEMVIEWS +/* +Implementation of the IAccessible2 table2 interface. Much simpler than +the other table interfaces since there is only the main table and cells: + +TABLE/LIST/TREE + |- HEADER CELL + |- CELL + |- CELL + ... +*/ + + +QAbstractItemView *QAccessibleTable::view() const +{ + return qobject_cast<QAbstractItemView*>(object()); +} + +int QAccessibleTable::logicalIndex(const QModelIndex &index) const +{ + if (!view()->model() || !index.isValid()) + return -1; + int vHeader = verticalHeader() ? 1 : 0; + int hHeader = horizontalHeader() ? 1 : 0; + return (index.row() + hHeader)*(index.model()->columnCount() + vHeader) + (index.column() + vHeader); +} + +QAccessibleTable::QAccessibleTable(QWidget *w) + : QAccessibleObject(w) +{ + Q_ASSERT(view()); + + if (qobject_cast<const QTableView*>(view())) { + m_role = QAccessible::Table; + } else if (qobject_cast<const QTreeView*>(view())) { + m_role = QAccessible::Tree; + } else if (qobject_cast<const QListView*>(view())) { + m_role = QAccessible::List; + } else { + // is this our best guess? + m_role = QAccessible::Table; + } +} + +bool QAccessibleTable::isValid() const +{ + return (view() && !qobject_cast<QWidget*>(view())->d_func()->data.in_destructor); +} + +QAccessibleTable::~QAccessibleTable() +{ + Q_FOREACH (QAccessible::Id id, childToId.values()) + QAccessible::deleteAccessibleInterface(id); +} + +QHeaderView *QAccessibleTable::horizontalHeader() const +{ + QHeaderView *header = 0; + if (false) { +#ifndef QT_NO_TABLEVIEW + } else if (const QTableView *tv = qobject_cast<const QTableView*>(view())) { + header = tv->horizontalHeader(); +#endif +#ifndef QT_NO_TREEVIEW + } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(view())) { + header = tv->header(); +#endif + } + return header; +} + +QHeaderView *QAccessibleTable::verticalHeader() const +{ + QHeaderView *header = 0; + if (false) { +#ifndef QT_NO_TABLEVIEW + } else if (const QTableView *tv = qobject_cast<const QTableView*>(view())) { + header = tv->verticalHeader(); +#endif + } + return header; +} + +QAccessibleInterface *QAccessibleTable::cellAt(int row, int column) const +{ + if (!view()->model()) + return 0; + Q_ASSERT(role() != QAccessible::Tree); + QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); + if (!index.isValid()) { + qWarning() << "QAccessibleTable::cellAt: invalid index: " << index << " for " << view(); + return 0; + } + return child(logicalIndex(index)); +} + +QAccessibleInterface *QAccessibleTable::caption() const +{ + return 0; +} + +QString QAccessibleTable::columnDescription(int column) const +{ + if (!view()->model()) + return QString(); + return view()->model()->headerData(column, Qt::Horizontal).toString(); +} + +int QAccessibleTable::columnCount() const +{ + if (!view()->model()) + return 0; + return view()->model()->columnCount(); +} + +int QAccessibleTable::rowCount() const +{ + if (!view()->model()) + return 0; + return view()->model()->rowCount(); +} + +int QAccessibleTable::selectedCellCount() const +{ + if (!view()->selectionModel()) + return 0; + return view()->selectionModel()->selectedIndexes().count(); +} + +int QAccessibleTable::selectedColumnCount() const +{ + if (!view()->selectionModel()) + return 0; + return view()->selectionModel()->selectedColumns().count(); +} + +int QAccessibleTable::selectedRowCount() const +{ + if (!view()->selectionModel()) + return 0; + return view()->selectionModel()->selectedRows().count(); +} + +QString QAccessibleTable::rowDescription(int row) const +{ + if (!view()->model()) + return QString(); + return view()->model()->headerData(row, Qt::Vertical).toString(); +} + +QList<QAccessibleInterface *> QAccessibleTable::selectedCells() const +{ + QList<QAccessibleInterface*> cells; + if (!view()->selectionModel()) + return cells; + Q_FOREACH (const QModelIndex &index, view()->selectionModel()->selectedIndexes()) + cells.append(child(logicalIndex(index))); + return cells; +} + +QList<int> QAccessibleTable::selectedColumns() const +{ + if (!view()->selectionModel()) + return QList<int>(); + QList<int> columns; + Q_FOREACH (const QModelIndex &index, view()->selectionModel()->selectedColumns()) { + columns.append(index.column()); + } + return columns; +} + +QList<int> QAccessibleTable::selectedRows() const +{ + if (!view()->selectionModel()) + return QList<int>(); + QList<int> rows; + Q_FOREACH (const QModelIndex &index, view()->selectionModel()->selectedRows()) { + rows.append(index.row()); + } + return rows; +} + +QAccessibleInterface *QAccessibleTable::summary() const +{ + return 0; +} + +bool QAccessibleTable::isColumnSelected(int column) const +{ + if (!view()->selectionModel()) + return false; + return view()->selectionModel()->isColumnSelected(column, QModelIndex()); +} + +bool QAccessibleTable::isRowSelected(int row) const +{ + if (!view()->selectionModel()) + return false; + return view()->selectionModel()->isRowSelected(row, QModelIndex()); +} + +bool QAccessibleTable::selectRow(int row) +{ + if (!view()->model() || !view()->selectionModel()) + return false; + QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); + + if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) + return false; + + switch (view()->selectionMode()) { + case QAbstractItemView::NoSelection: + return false; + case QAbstractItemView::SingleSelection: + if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1 ) + return false; + view()->clearSelection(); + break; + case QAbstractItemView::ContiguousSelection: + if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) + && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) + view()->clearSelection(); + break; + default: + break; + } + + view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + return true; +} + +bool QAccessibleTable::selectColumn(int column) +{ + if (!view()->model() || !view()->selectionModel()) + return false; + QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); + + if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectRows) + return false; + + switch (view()->selectionMode()) { + case QAbstractItemView::NoSelection: + return false; + case QAbstractItemView::SingleSelection: + if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) + return false; + case QAbstractItemView::ContiguousSelection: + if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) + && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) + view()->clearSelection(); + break; + default: + break; + } + + view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Columns); + return true; +} + +bool QAccessibleTable::unselectRow(int row) +{ + if (!view()->model() || !view()->selectionModel()) + return false; + + QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); + if (!index.isValid()) + return false; + + QItemSelection selection(index, index); + + switch (view()->selectionMode()) { + case QAbstractItemView::SingleSelection: + //In SingleSelection and ContiguousSelection once an item + //is selected, there's no way for the user to unselect all items + if (selectedRowCount() == 1) + return false; + break; + case QAbstractItemView::ContiguousSelection: + if (selectedRowCount() == 1) + return false; + + if ((!row || view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) + && view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) { + //If there are rows selected both up the current row and down the current rown, + //the ones which are down the current row will be deselected + selection = QItemSelection(index, view()->model()->index(rowCount() - 1, 0, view()->rootIndex())); + } + default: + break; + } + + view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); + return true; +} + +bool QAccessibleTable::unselectColumn(int column) +{ + if (!view()->model() || !view()->selectionModel()) + return false; + + QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); + if (!index.isValid()) + return false; + + QItemSelection selection(index, index); + + switch (view()->selectionMode()) { + case QAbstractItemView::SingleSelection: + //In SingleSelection and ContiguousSelection once an item + //is selected, there's no way for the user to unselect all items + if (selectedColumnCount() == 1) + return false; + break; + case QAbstractItemView::ContiguousSelection: + if (selectedColumnCount() == 1) + return false; + + if ((!column || view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) + && view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { + //If there are columns selected both at the left of the current row and at the right + //of the current rown, the ones which are at the right will be deselected + selection = QItemSelection(index, view()->model()->index(0, columnCount() - 1, view()->rootIndex())); + } + default: + break; + } + + view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Columns); + return true; +} + +QAccessible::Role QAccessibleTable::role() const +{ + return m_role; +} + +QAccessible::State QAccessibleTable::state() const +{ + return QAccessible::State(); +} + +QAccessibleInterface *QAccessibleTable::childAt(int x, int y) const +{ + QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0)); + QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset); + // FIXME: if indexPosition < 0 in one coordinate, return header + + QModelIndex index = view()->indexAt(indexPosition); + if (index.isValid()) { + return child(logicalIndex(index)); + } + return 0; +} + +int QAccessibleTable::childCount() const +{ + if (!view()->model()) + return 0; + int vHeader = verticalHeader() ? 1 : 0; + int hHeader = horizontalHeader() ? 1 : 0; + return (view()->model()->rowCount()+hHeader) * (view()->model()->columnCount()+vHeader); +} + +int QAccessibleTable::indexOfChild(const QAccessibleInterface *iface) const +{ + if (!view()->model()) + return -1; + QAccessibleInterface *parent = iface->parent(); + if (parent->object() != view()) + return -1; + + Q_ASSERT(iface->role() != QAccessible::TreeItem); // should be handled by tree class + if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { + const QAccessibleTableCell* cell = static_cast<const QAccessibleTableCell*>(iface); + return logicalIndex(cell->m_index); + } else if (iface->role() == QAccessible::ColumnHeader){ + const QAccessibleTableHeaderCell* cell = static_cast<const QAccessibleTableHeaderCell*>(iface); + return cell->index + (verticalHeader() ? 1 : 0); + } else if (iface->role() == QAccessible::RowHeader){ + const QAccessibleTableHeaderCell* cell = static_cast<const QAccessibleTableHeaderCell*>(iface); + return (cell->index + 1) * (view()->model()->columnCount() + 1); + } else if (iface->role() == QAccessible::Pane) { + return 0; // corner button + } else { + qWarning() << "WARNING QAccessibleTable::indexOfChild Fix my children..." + << iface->role() << iface->text(QAccessible::Name); + } + // FIXME: we are in denial of our children. this should stop. + return -1; +} + +QString QAccessibleTable::text(QAccessible::Text t) const +{ + if (t == QAccessible::Description) + return view()->accessibleDescription(); + return view()->accessibleName(); +} + +QRect QAccessibleTable::rect() const +{ + if (!view()->isVisible()) + return QRect(); + QPoint pos = view()->mapToGlobal(QPoint(0, 0)); + return QRect(pos.x(), pos.y(), view()->width(), view()->height()); +} + +QAccessibleInterface *QAccessibleTable::parent() const +{ + if (view() && view()->parent()) { + if (qstrcmp("QComboBoxPrivateContainer", view()->parent()->metaObject()->className()) == 0) { + return QAccessible::queryAccessibleInterface(view()->parent()->parent()); + } + return QAccessible::queryAccessibleInterface(view()->parent()); + } + return 0; +} + +QAccessibleInterface *QAccessibleTable::child(int logicalIndex) const +{ + if (!view()->model()) + return 0; + + if (childToId.contains(logicalIndex)) { + QAccessible::Id id = childToId.value(logicalIndex); + return QAccessible::accessibleInterface(id); + } + + int vHeader = verticalHeader() ? 1 : 0; + int hHeader = horizontalHeader() ? 1 : 0; + + int columns = view()->model()->columnCount() + vHeader; + + int row = logicalIndex / columns; + int column = logicalIndex % columns; + + QAccessibleInterface *iface = 0; + + if (vHeader) { + if (column == 0) { + if (hHeader && row == 0) { + iface = new QAccessibleTableCornerButton(view()); + } else { + iface = new QAccessibleTableHeaderCell(view(), row - hHeader, Qt::Vertical); + } + } + --column; + } + if (!iface && hHeader) { + if (row == 0) { + iface = new QAccessibleTableHeaderCell(view(), column, Qt::Horizontal); + } + --row; + } + + if (!iface) { + QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); + if (!index.isValid()) { + qWarning() << "QAccessibleTable::child: Invalid index at: " << row << column; + return 0; + } + iface = new QAccessibleTableCell(view(), index, cellRole()); + } + + QAccessible::registerAccessibleInterface(iface); + childToId.insert(logicalIndex, QAccessible::uniqueId(iface)); + return iface; +} + +void *QAccessibleTable::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::TableInterface) + return static_cast<QAccessibleTableInterface*>(this); + return 0; +} + +void QAccessibleTable::modelChange(QAccessibleTableModelChangeEvent *event) +{ + // if there is no cache yet, we don't update anything + if (childToId.isEmpty()) + return; + + switch (event->modelChangeType()) { + case QAccessibleTableModelChangeEvent::ModelReset: + Q_FOREACH (QAccessible::Id id, childToId.values()) + QAccessible::deleteAccessibleInterface(id); + childToId.clear(); + break; + + // rows are inserted: move every row after that + case QAccessibleTableModelChangeEvent::RowsInserted: + case QAccessibleTableModelChangeEvent::ColumnsInserted: { + int newRows = event->lastRow() - event->firstRow() + 1; + int newColumns = event->lastColumn() - event->firstColumn() + 1; + + ChildCache newCache; + ChildCache::ConstIterator iter = childToId.constBegin(); + + while (iter != childToId.constEnd()) { + QAccessible::Id id = iter.value(); + QAccessibleInterface *iface = QAccessible::accessibleInterface(id); + Q_ASSERT(iface); + if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { + Q_ASSERT(iface->tableCellInterface()); + QAccessibleTableCell *cell = static_cast<QAccessibleTableCell*>(iface->tableCellInterface()); + if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsInserted + && cell->m_index.row() >= event->firstRow()) { + int newRow = cell->m_index.row() + newRows; + cell->m_index = cell->m_index.sibling(newRow, cell->m_index.column()); + } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsInserted + && cell->m_index.column() >= event->firstColumn()) { + int newColumn = cell->m_index.column() + newColumns; + cell->m_index = cell->m_index.sibling(cell->m_index.row(), newColumn); + } + } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsInserted + && iface->role() == QAccessible::RowHeader) { + QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface); + if (cell->index >= event->firstRow()) { + cell->index += newRows; + } + } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsInserted + && iface->role() == QAccessible::ColumnHeader) { + QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface); + if (cell->index >= event->firstColumn()) { + cell->index += newColumns; + } + } + if (indexOfChild(iface) >= 0) { + newCache.insert(indexOfChild(iface), id); + } else { + // ### This should really not happen, + // but it might if the view has a root index set. + // This needs to be fixed. + QAccessible::deleteAccessibleInterface(id); + } + ++iter; + } + childToId = newCache; + break; + } + + case QAccessibleTableModelChangeEvent::ColumnsRemoved: + case QAccessibleTableModelChangeEvent::RowsRemoved: { + int deletedColumns = event->lastColumn() - event->firstColumn() + 1; + int deletedRows = event->lastRow() - event->firstRow() + 1; + ChildCache newCache; + ChildCache::ConstIterator iter = childToId.constBegin(); + while (iter != childToId.constEnd()) { + QAccessible::Id id = iter.value(); + QAccessibleInterface *iface = QAccessible::accessibleInterface(id); + Q_ASSERT(iface); + if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { + Q_ASSERT(iface->tableCellInterface()); + QAccessibleTableCell *cell = static_cast<QAccessibleTableCell*>(iface->tableCellInterface()); + if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsRemoved) { + if (cell->m_index.row() < event->firstRow()) { + newCache.insert(indexOfChild(cell), id); + } else if (cell->m_index.row() > event->lastRow()) { + int newRow = cell->m_index.row() - deletedRows; + cell->m_index = cell->m_index.sibling(newRow, cell->m_index.column()); + newCache.insert(indexOfChild(cell), id); + } else { + QAccessible::deleteAccessibleInterface(id); + } + } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsRemoved) { + if (cell->m_index.column() < event->firstColumn()) { + newCache.insert(indexOfChild(cell), id); + } else if (cell->m_index.column() > event->lastColumn()) { + int newColumn = cell->m_index.column() - deletedColumns; + cell->m_index = cell->m_index.sibling(cell->m_index.row(), newColumn); + newCache.insert(indexOfChild(cell), id); + } else { + QAccessible::deleteAccessibleInterface(id); + } + } + } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsRemoved + && iface->role() == QAccessible::RowHeader) { + QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface); + if (cell->index < event->firstRow()) { + newCache.insert(indexOfChild(cell), id); + } else if (cell->index > event->lastRow()) { + cell->index -= deletedRows; + newCache.insert(indexOfChild(cell), id); + } else { + QAccessible::deleteAccessibleInterface(id); + } + } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsRemoved + && iface->role() == QAccessible::ColumnHeader) { + QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface); + if (cell->index < event->firstColumn()) { + newCache.insert(indexOfChild(cell), id); + } else if (cell->index > event->lastColumn()) { + cell->index -= deletedColumns; + newCache.insert(indexOfChild(cell), id); + } else { + QAccessible::deleteAccessibleInterface(id); + } + } + ++iter; + } + childToId = newCache; + break; + } + + case QAccessibleTableModelChangeEvent::DataChanged: + // nothing to do in this case + break; + } +} + +// TREE VIEW + +QModelIndex QAccessibleTree::indexFromLogical(int row, int column) const +{ + if (!isValid() || !view()->model()) + return QModelIndex(); + + const QTreeView *treeView = qobject_cast<const QTreeView*>(view()); + if ((row < 0) || (column < 0) || (treeView->d_func()->viewItems.count() <= row)) { + qWarning() << "QAccessibleTree::indexFromLogical: invalid index: " << row << column << " for " << treeView; + return QModelIndex(); + } + QModelIndex modelIndex = treeView->d_func()->viewItems.at(row).index; + + if (modelIndex.isValid() && column > 0) { + modelIndex = view()->model()->index(modelIndex.row(), column, modelIndex.parent()); + } + return modelIndex; +} + +QAccessibleInterface *QAccessibleTree::childAt(int x, int y) const +{ + if (!view()->model()) + return 0; + QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0)); + QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset); + + QModelIndex index = view()->indexAt(indexPosition); + if (!index.isValid()) + return 0; + + const QTreeView *treeView = qobject_cast<const QTreeView*>(view()); + int row = treeView->d_func()->viewIndex(index) + (horizontalHeader() ? 1 : 0); + int column = index.column(); + + int i = row * view()->model()->columnCount() + column; + return child(i); +} + +int QAccessibleTree::childCount() const +{ + const QTreeView *treeView = qobject_cast<const QTreeView*>(view()); + Q_ASSERT(treeView); + if (!view()->model()) + return 0; + + int hHeader = horizontalHeader() ? 1 : 0; + return (treeView->d_func()->viewItems.count() + hHeader)* view()->model()->columnCount(); +} + + +QAccessibleInterface *QAccessibleTree::child(int logicalIndex) const +{ + if (logicalIndex < 0 || !view()->model() || !view()->model()->columnCount()) + return 0; + + QAccessibleInterface *iface = 0; + int index = logicalIndex; + + if (horizontalHeader()) { + if (index < view()->model()->columnCount()) { + iface = new QAccessibleTableHeaderCell(view(), index, Qt::Horizontal); + } else { + index -= view()->model()->columnCount(); + } + } + + if (!iface) { + int row = index / view()->model()->columnCount(); + int column = index % view()->model()->columnCount(); + QModelIndex modelIndex = indexFromLogical(row, column); + if (!modelIndex.isValid()) + return 0; + iface = new QAccessibleTableCell(view(), modelIndex, cellRole()); + } + QAccessible::registerAccessibleInterface(iface); + // ### FIXME: get interfaces from the cache instead of re-creating them + return iface; +} + +int QAccessibleTree::rowCount() const +{ + const QTreeView *treeView = qobject_cast<const QTreeView*>(view()); + Q_ASSERT(treeView); + return treeView->d_func()->viewItems.count(); +} + +int QAccessibleTree::indexOfChild(const QAccessibleInterface *iface) const +{ + if (!view()->model()) + return -1; + QAccessibleInterface *parent = iface->parent(); + if (parent->object() != view()) + return -1; + + if (iface->role() == QAccessible::TreeItem) { + const QAccessibleTableCell* cell = static_cast<const QAccessibleTableCell*>(iface); + const QTreeView *treeView = qobject_cast<const QTreeView*>(view()); + Q_ASSERT(treeView); + int row = treeView->d_func()->viewIndex(cell->m_index) + (horizontalHeader() ? 1 : 0); + int column = cell->m_index.column(); + + int index = row * view()->model()->columnCount() + column; + return index; + } else if (iface->role() == QAccessible::ColumnHeader){ + const QAccessibleTableHeaderCell* cell = static_cast<const QAccessibleTableHeaderCell*>(iface); + return cell->index; + } else { + qWarning() << "WARNING QAccessibleTable::indexOfChild invalid child" + << iface->role() << iface->text(QAccessible::Name); + } + // FIXME: add scrollbars and don't just ignore them + return -1; +} + +QAccessibleInterface *QAccessibleTree::cellAt(int row, int column) const +{ + QModelIndex index = indexFromLogical(row, column); + if (!index.isValid()) { + qWarning() << "Requested invalid tree cell: " << row << column; + return 0; + } + const QTreeView *treeView = qobject_cast<const QTreeView*>(view()); + Q_ASSERT(treeView); + int logicalIndex = treeView->d_func()->accessibleTable2Index(index); + + return child(logicalIndex); // FIXME ### new QAccessibleTableCell(view(), index, cellRole()); +} + +QString QAccessibleTree::rowDescription(int) const +{ + return QString(); // no headers for rows in trees +} + +bool QAccessibleTree::isRowSelected(int row) const +{ + if (!view()->selectionModel()) + return false; + QModelIndex index = indexFromLogical(row); + return view()->selectionModel()->isRowSelected(index.row(), index.parent()); +} + +bool QAccessibleTree::selectRow(int row) +{ + if (!view()->selectionModel()) + return false; + QModelIndex index = indexFromLogical(row); + + if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) + return false; + + switch (view()->selectionMode()) { + case QAbstractItemView::NoSelection: + return false; + case QAbstractItemView::SingleSelection: + if ((view()->selectionBehavior() != QAbstractItemView::SelectRows) && (columnCount() > 1)) + return false; + view()->clearSelection(); + break; + case QAbstractItemView::ContiguousSelection: + if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) + && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) + view()->clearSelection(); + break; + default: + break; + } + + view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); + return true; +} + +// TABLE CELL + +QAccessibleTableCell::QAccessibleTableCell(QAbstractItemView *view_, const QModelIndex &index_, QAccessible::Role role_) + : /* QAccessibleSimpleEditableTextInterface(this), */ view(view_), m_index(index_), m_role(role_) +{ + if (!index_.isValid()) + qWarning() << "QAccessibleTableCell::QAccessibleTableCell with invalid index: " << index_; +} + +void *QAccessibleTableCell::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::TableCellInterface) + return static_cast<QAccessibleTableCellInterface*>(this); + if (t == QAccessible::ActionInterface) + return static_cast<QAccessibleActionInterface*>(this); + return 0; +} + +int QAccessibleTableCell::columnExtent() const { return 1; } +int QAccessibleTableCell::rowExtent() const { return 1; } + +QList<QAccessibleInterface*> QAccessibleTableCell::rowHeaderCells() const +{ + QList<QAccessibleInterface*> headerCell; + if (verticalHeader()) { + // FIXME + headerCell.append(new QAccessibleTableHeaderCell(view, m_index.row(), Qt::Vertical)); + } + return headerCell; +} + +QList<QAccessibleInterface*> QAccessibleTableCell::columnHeaderCells() const +{ + QList<QAccessibleInterface*> headerCell; + if (horizontalHeader()) { + // FIXME + headerCell.append(new QAccessibleTableHeaderCell(view, m_index.column(), Qt::Horizontal)); + } + return headerCell; +} + +QHeaderView *QAccessibleTableCell::horizontalHeader() const +{ + QHeaderView *header = 0; + + if (false) { +#ifndef QT_NO_TABLEVIEW + } else if (const QTableView *tv = qobject_cast<const QTableView*>(view)) { + header = tv->horizontalHeader(); +#endif +#ifndef QT_NO_TREEVIEW + } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(view)) { + header = tv->header(); +#endif + } + + return header; +} + +QHeaderView *QAccessibleTableCell::verticalHeader() const +{ + QHeaderView *header = 0; +#ifndef QT_NO_TABLEVIEW + if (const QTableView *tv = qobject_cast<const QTableView*>(view)) + header = tv->verticalHeader(); +#endif + return header; +} + +int QAccessibleTableCell::columnIndex() const +{ + return m_index.column(); +} + +int QAccessibleTableCell::rowIndex() const +{ + if (role() == QAccessible::TreeItem) { + const QTreeView *treeView = qobject_cast<const QTreeView*>(view); + Q_ASSERT(treeView); + int row = treeView->d_func()->viewIndex(m_index); + return row; + } + return m_index.row(); +} + +bool QAccessibleTableCell::isSelected() const +{ + return view->selectionModel()->isSelected(m_index); +} + +QStringList QAccessibleTableCell::actionNames() const +{ + QStringList names; + names << toggleAction(); + return names; +} + +void QAccessibleTableCell::doAction(const QString& actionName) +{ + if (actionName == toggleAction()) { + if (isSelected()) + unselectCell(); + else + selectCell(); + } +} + +QStringList QAccessibleTableCell::keyBindingsForAction(const QString &) const +{ + return QStringList(); +} + + +void QAccessibleTableCell::selectCell() +{ + QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); + if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) + return; + Q_ASSERT(table()); + QAccessibleTableInterface *cellTable = table()->tableInterface(); + + switch (view->selectionBehavior()) { + case QAbstractItemView::SelectItems: + break; + case QAbstractItemView::SelectColumns: + if (cellTable) + cellTable->selectColumn(m_index.column()); + return; + case QAbstractItemView::SelectRows: + if (cellTable) + cellTable->selectRow(m_index.row()); + return; + } + + if (selectionMode == QAbstractItemView::SingleSelection) { + view->clearSelection(); + } + + view->selectionModel()->select(m_index, QItemSelectionModel::Select); +} + +void QAccessibleTableCell::unselectCell() +{ + + QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); + if (!m_index.isValid() || (selectionMode & QAbstractItemView::NoSelection)) + return; + + QAccessibleTableInterface *cellTable = table()->tableInterface(); + + switch (view->selectionBehavior()) { + case QAbstractItemView::SelectItems: + break; + case QAbstractItemView::SelectColumns: + if (cellTable) + cellTable->unselectColumn(m_index.column()); + return; + case QAbstractItemView::SelectRows: + if (cellTable) + cellTable->unselectRow(m_index.row()); + return; + } + + //If the mode is not MultiSelection or ExtendedSelection and only + //one cell is selected it cannot be unselected by the user + if ((selectionMode != QAbstractItemView::MultiSelection) + && (selectionMode != QAbstractItemView::ExtendedSelection) + && (view->selectionModel()->selectedIndexes().count() <= 1)) + return; + + view->selectionModel()->select(m_index, QItemSelectionModel::Deselect); +} + +QAccessibleInterface *QAccessibleTableCell::table() const +{ + return QAccessible::queryAccessibleInterface(view); +} + +QAccessible::Role QAccessibleTableCell::role() const +{ + return m_role; +} + +QAccessible::State QAccessibleTableCell::state() const +{ + QAccessible::State st; + QRect globalRect = view->rect(); + globalRect.translate(view->mapToGlobal(QPoint(0,0))); + if (!globalRect.intersects(rect())) + st.invisible = true; + + if (view->selectionModel()->isSelected(m_index)) + st.selected = true; + if (view->selectionModel()->currentIndex() == m_index) + st.focused = true; + if (m_index.model()->data(m_index, Qt::CheckStateRole).toInt() == Qt::Checked) + st.checked = true; + + Qt::ItemFlags flags = m_index.flags(); + if (flags & Qt::ItemIsSelectable) { + st.selectable = true; + st.focusable = true; + if (view->selectionMode() == QAbstractItemView::MultiSelection) + st.multiSelectable = true; + if (view->selectionMode() == QAbstractItemView::ExtendedSelection) + st.extSelectable = true; + } + if (m_role == QAccessible::TreeItem) { + const QTreeView *treeView = qobject_cast<const QTreeView*>(view); + if (treeView->model()->hasChildren(m_index)) + st.expandable = true; + if (treeView->isExpanded(m_index)) + st.expanded = true; + } + return st; +} + + +QRect QAccessibleTableCell::rect() const +{ + QRect r; + r = view->visualRect(m_index); + + if (!r.isNull()) { + r.translate(view->viewport()->mapTo(view, QPoint(0,0))); + r.translate(view->mapToGlobal(QPoint(0, 0))); + } + return r; +} + +QString QAccessibleTableCell::text(QAccessible::Text t) const +{ + QAbstractItemModel *model = view->model(); + QString value; + switch (t) { + case QAccessible::Name: + value = model->data(m_index, Qt::AccessibleTextRole).toString(); + if (value.isEmpty()) + value = model->data(m_index, Qt::DisplayRole).toString(); + break; + case QAccessible::Description: + value = model->data(m_index, Qt::AccessibleDescriptionRole).toString(); + break; + default: + break; + } + return value; +} + +void QAccessibleTableCell::setText(QAccessible::Text /*t*/, const QString &text) +{ + if (!(m_index.flags() & Qt::ItemIsEditable)) + return; + view->model()->setData(m_index, text); +} + +bool QAccessibleTableCell::isValid() const +{ + return view && view->model() && m_index.isValid(); +} + +QAccessibleInterface *QAccessibleTableCell::parent() const +{ + return QAccessible::queryAccessibleInterface(view); +} + +QAccessibleInterface *QAccessibleTableCell::child(int) const +{ + return 0; +} + +QAccessibleTableHeaderCell::QAccessibleTableHeaderCell(QAbstractItemView *view_, int index_, Qt::Orientation orientation_) + : view(view_), index(index_), orientation(orientation_) +{ + Q_ASSERT(index_ >= 0); +} + +QAccessible::Role QAccessibleTableHeaderCell::role() const +{ + if (orientation == Qt::Horizontal) + return QAccessible::ColumnHeader; + return QAccessible::RowHeader; +} + +QAccessible::State QAccessibleTableHeaderCell::state() const +{ + QAccessible::State s; + if (QHeaderView *h = headerView()) { + s.invisible = !h->testAttribute(Qt::WA_WState_Visible); + s.disabled = !h->isEnabled(); + } + return s; +} + +QRect QAccessibleTableHeaderCell::rect() const +{ + QHeaderView *header = 0; + if (false) { +#ifndef QT_NO_TABLEVIEW + } else if (const QTableView *tv = qobject_cast<const QTableView*>(view)) { + if (orientation == Qt::Horizontal) { + header = tv->horizontalHeader(); + } else { + header = tv->verticalHeader(); + } +#endif +#ifndef QT_NO_TREEVIEW + } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(view)) { + header = tv->header(); +#endif + } + if (!header) + return QRect(); + QPoint zero = header->mapToGlobal(QPoint(0, 0)); + int sectionSize = header->sectionSize(index); + int sectionPos = header->sectionPosition(index); + return orientation == Qt::Horizontal + ? QRect(zero.x() + sectionPos, zero.y(), sectionSize, header->height()) + : QRect(zero.x(), zero.y() + sectionPos, header->width(), sectionSize); +} + +QString QAccessibleTableHeaderCell::text(QAccessible::Text t) const +{ + QAbstractItemModel *model = view->model(); + QString value; + switch (t) { + case QAccessible::Name: + value = model->headerData(index, orientation, Qt::AccessibleTextRole).toString(); + if (value.isEmpty()) + value = model->headerData(index, orientation, Qt::DisplayRole).toString(); + break; + case QAccessible::Description: + value = model->headerData(index, orientation, Qt::AccessibleDescriptionRole).toString(); + break; + default: + break; + } + return value; +} + +void QAccessibleTableHeaderCell::setText(QAccessible::Text, const QString &) +{ + return; +} + +bool QAccessibleTableHeaderCell::isValid() const +{ + return view && view->model() && (index >= 0) + && ((orientation == Qt::Horizontal) ? (index < view->model()->columnCount()) : (index < view->model()->rowCount())); +} + +QAccessibleInterface *QAccessibleTableHeaderCell::parent() const +{ + return QAccessible::queryAccessibleInterface(view); +} + +QAccessibleInterface *QAccessibleTableHeaderCell::child(int) const +{ + return 0; +} + +QHeaderView *QAccessibleTableHeaderCell::headerView() const +{ + QHeaderView *header = 0; + if (false) { +#ifndef QT_NO_TABLEVIEW + } else if (const QTableView *tv = qobject_cast<const QTableView*>(view)) { + if (orientation == Qt::Horizontal) { + header = tv->horizontalHeader(); + } else { + header = tv->verticalHeader(); + } +#endif +#ifndef QT_NO_TREEVIEW + } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(view)) { + header = tv->header(); +#endif + } + return header; +} + +#endif // QT_NO_ITEMVIEWS + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/widgets/accessible/itemviews.h b/src/widgets/accessible/itemviews.h new file mode 100644 index 0000000000..2f9ca6163b --- /dev/null +++ b/src/widgets/accessible/itemviews.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ACCESSIBLE_ITEMVIEWS_H +#define ACCESSIBLE_ITEMVIEWS_H + +#include "QtCore/qpointer.h" +#include <QtGui/qaccessible.h> +#include <QtWidgets/qaccessiblewidget.h> +#include <QtWidgets/qabstractitemview.h> +#include <QtWidgets/qheaderview.h> + + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +#ifndef QT_NO_ITEMVIEWS + +class QAccessibleTableCell; +class QAccessibleTableHeaderCell; + +class QAccessibleTable :public QAccessibleTableInterface, public QAccessibleObject +{ +public: + explicit QAccessibleTable(QWidget *w); + bool isValid() const Q_DECL_OVERRIDE; + + QAccessible::Role role() const Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + QRect rect() const Q_DECL_OVERRIDE; + + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *) const Q_DECL_OVERRIDE; + + QAccessibleInterface *parent() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // table interface + virtual QAccessibleInterface *cellAt(int row, int column) const Q_DECL_OVERRIDE; + virtual QAccessibleInterface *caption() const Q_DECL_OVERRIDE; + virtual QAccessibleInterface *summary() const Q_DECL_OVERRIDE; + virtual QString columnDescription(int column) const Q_DECL_OVERRIDE; + virtual QString rowDescription(int row) const Q_DECL_OVERRIDE; + virtual int columnCount() const Q_DECL_OVERRIDE; + virtual int rowCount() const Q_DECL_OVERRIDE; + + // selection + virtual int selectedCellCount() const Q_DECL_OVERRIDE; + virtual int selectedColumnCount() const Q_DECL_OVERRIDE; + virtual int selectedRowCount() const Q_DECL_OVERRIDE; + virtual QList<QAccessibleInterface*> selectedCells() const Q_DECL_OVERRIDE; + virtual QList<int> selectedColumns() const Q_DECL_OVERRIDE; + virtual QList<int> selectedRows() const Q_DECL_OVERRIDE; + virtual bool isColumnSelected(int column) const Q_DECL_OVERRIDE; + virtual bool isRowSelected(int row) const Q_DECL_OVERRIDE; + virtual bool selectRow(int row) Q_DECL_OVERRIDE; + virtual bool selectColumn(int column) Q_DECL_OVERRIDE; + virtual bool unselectRow(int row) Q_DECL_OVERRIDE; + virtual bool unselectColumn(int column) Q_DECL_OVERRIDE; + + QAbstractItemView *view() const; + + void modelChange(QAccessibleTableModelChangeEvent *event) Q_DECL_OVERRIDE; + +protected: + inline QAccessible::Role cellRole() const { + switch (m_role) { + case QAccessible::List: + return QAccessible::ListItem; + case QAccessible::Table: + return QAccessible::Cell; + case QAccessible::Tree: + return QAccessible::TreeItem; + default: + Q_ASSERT(0); + } + return QAccessible::NoRole; + } + + QHeaderView *horizontalHeader() const; + QHeaderView *verticalHeader() const; + + // maybe vector + typedef QHash<int, QAccessible::Id> ChildCache; + mutable ChildCache childToId; + + virtual ~QAccessibleTable(); + +private: + // the child index for a model index + inline int logicalIndex(const QModelIndex &index) const; + QAccessible::Role m_role; +}; + +class QAccessibleTree :public QAccessibleTable +{ +public: + explicit QAccessibleTree(QWidget *w) + : QAccessibleTable(w) + {} + + + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + + int indexOfChild(const QAccessibleInterface *) const Q_DECL_OVERRIDE; + + int rowCount() const Q_DECL_OVERRIDE; + + // table interface + QAccessibleInterface *cellAt(int row, int column) const Q_DECL_OVERRIDE; + QString rowDescription(int row) const Q_DECL_OVERRIDE; + bool isRowSelected(int row) const Q_DECL_OVERRIDE; + bool selectRow(int row) Q_DECL_OVERRIDE; + +private: + QModelIndex indexFromLogical(int row, int column = 0) const; + + inline int logicalIndex(const QModelIndex &index) const; +}; + +class QAccessibleTableCell: public QAccessibleInterface, public QAccessibleTableCellInterface, public QAccessibleActionInterface +{ +public: + QAccessibleTableCell(QAbstractItemView *view, const QModelIndex &m_index, QAccessible::Role role); + + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + QObject *object() const Q_DECL_OVERRIDE { return 0; } + QAccessible::Role role() const Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + QRect rect() const Q_DECL_OVERRIDE; + bool isValid() const Q_DECL_OVERRIDE; + + QAccessibleInterface *childAt(int, int) const Q_DECL_OVERRIDE { return 0; } + int childCount() const Q_DECL_OVERRIDE { return 0; } + int indexOfChild(const QAccessibleInterface *) const Q_DECL_OVERRIDE { return -1; } + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE; + + QAccessibleInterface *parent() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int) const Q_DECL_OVERRIDE; + + // cell interface + virtual int columnExtent() const Q_DECL_OVERRIDE; + virtual QList<QAccessibleInterface*> columnHeaderCells() const Q_DECL_OVERRIDE; + virtual int columnIndex() const Q_DECL_OVERRIDE; + virtual int rowExtent() const Q_DECL_OVERRIDE; + virtual QList<QAccessibleInterface*> rowHeaderCells() const Q_DECL_OVERRIDE; + virtual int rowIndex() const Q_DECL_OVERRIDE; + virtual bool isSelected() const Q_DECL_OVERRIDE; + virtual QAccessibleInterface* table() const Q_DECL_OVERRIDE; + + //action interface + virtual QStringList actionNames() const Q_DECL_OVERRIDE; + virtual void doAction(const QString &actionName) Q_DECL_OVERRIDE; + virtual QStringList keyBindingsForAction(const QString &actionName) const Q_DECL_OVERRIDE; + +private: + QHeaderView *verticalHeader() const; + QHeaderView *horizontalHeader() const; + QPointer<QAbstractItemView > view; + QModelIndex m_index; + QAccessible::Role m_role; + + void selectCell(); + void unselectCell(); + +friend class QAccessibleTable; +friend class QAccessibleTree; +}; + + +class QAccessibleTableHeaderCell: public QAccessibleInterface +{ +public: + // For header cells, pass the header view in addition + QAccessibleTableHeaderCell(QAbstractItemView *view, int index, Qt::Orientation orientation); + + QObject *object() const Q_DECL_OVERRIDE { return 0; } + QAccessible::Role role() const Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + QRect rect() const Q_DECL_OVERRIDE; + bool isValid() const Q_DECL_OVERRIDE; + + QAccessibleInterface *childAt(int, int) const Q_DECL_OVERRIDE { return 0; } + int childCount() const Q_DECL_OVERRIDE { return 0; } + int indexOfChild(const QAccessibleInterface *) const Q_DECL_OVERRIDE { return -1; } + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE; + + QAccessibleInterface *parent() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + +private: + QHeaderView *headerView() const; + + QPointer<QAbstractItemView> view; + int index; + Qt::Orientation orientation; + +friend class QAccessibleTable; +friend class QAccessibleTree; +}; + +// This is the corner button on the top left of a table. +// It can be used to select all cells or it is not active at all. +// For now it is ignored. +class QAccessibleTableCornerButton: public QAccessibleInterface +{ +public: + QAccessibleTableCornerButton(QAbstractItemView *view_) + :view(view_) + {} + + QObject *object() const Q_DECL_OVERRIDE { return 0; } + QAccessible::Role role() const Q_DECL_OVERRIDE { return QAccessible::Pane; } + QAccessible::State state() const Q_DECL_OVERRIDE { return QAccessible::State(); } + QRect rect() const Q_DECL_OVERRIDE { return QRect(); } + bool isValid() const Q_DECL_OVERRIDE { return true; } + + QAccessibleInterface *childAt(int, int) const Q_DECL_OVERRIDE { return 0; } + int childCount() const Q_DECL_OVERRIDE { return 0; } + int indexOfChild(const QAccessibleInterface *) const Q_DECL_OVERRIDE { return -1; } + + QString text(QAccessible::Text) const Q_DECL_OVERRIDE { return QString(); } + void setText(QAccessible::Text, const QString &) Q_DECL_OVERRIDE {} + + QAccessibleInterface *parent() const Q_DECL_OVERRIDE { + return QAccessible::queryAccessibleInterface(view); + } + QAccessibleInterface *child(int) const Q_DECL_OVERRIDE { + return 0; + } + +private: + QPointer<QAbstractItemView> view; +}; + + +#endif + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +#endif // ACCESSIBLE_ITEMVIEWS_H diff --git a/src/widgets/accessible/qaccessiblemenu.cpp b/src/widgets/accessible/qaccessiblemenu.cpp new file mode 100644 index 0000000000..f47980d786 --- /dev/null +++ b/src/widgets/accessible/qaccessiblemenu.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblemenu.h" + +#include <qmenu.h> +#include <qmenubar.h> +#include <QtWidgets/QAction> +#include <qstyle.h> + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_MENU + +QString qt_accStripAmp(const QString &text); +QString qt_accHotKey(const QString &text); + +QAccessibleInterface *getOrCreateMenu(QWidget *menu, QAction *action) +{ + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(action); + if (!iface) { + iface = new QAccessibleMenuItem(menu, action); + QAccessible::registerAccessibleInterface(iface); + } + return iface; +} + +QAccessibleMenu::QAccessibleMenu(QWidget *w) +: QAccessibleWidget(w) +{ + Q_ASSERT(menu()); +} + +QMenu *QAccessibleMenu::menu() const +{ + return qobject_cast<QMenu*>(object()); +} + +int QAccessibleMenu::childCount() const +{ + return menu()->actions().count(); +} + +QAccessibleInterface *QAccessibleMenu::childAt(int x, int y) const +{ + QAction *act = menu()->actionAt(menu()->mapFromGlobal(QPoint(x,y))); + if(act && act->isSeparator()) + act = 0; + return act ? getOrCreateMenu(menu(), act) : 0; +} + +QString QAccessibleMenu::text(QAccessible::Text t) const +{ + QString tx = QAccessibleWidget::text(t); + if (!tx.isEmpty()) + return tx; + + if (t == QAccessible::Name) + return menu()->windowTitle(); + return tx; +} + +QAccessible::Role QAccessibleMenu::role() const +{ + return QAccessible::PopupMenu; +} + +QAccessibleInterface *QAccessibleMenu::child(int index) const +{ + if (index < childCount()) + return getOrCreateMenu(menu(), menu()->actions().at(index)); + return 0; +} + +QAccessibleInterface *QAccessibleMenu::parent() const +{ + if (QAction *menuAction = menu()->menuAction()) { + QList<QWidget *> parentCandidates; + parentCandidates << menu()->parentWidget(); + parentCandidates << menuAction->associatedWidgets(); + foreach (QWidget *w, parentCandidates) { + if (qobject_cast<QMenu*>(w) || qobject_cast<QMenuBar*>(w)) { + if (w->actions().indexOf(menuAction) != -1) + return getOrCreateMenu(w, menuAction); + } + } + } + return QAccessibleWidget::parent(); +} + +int QAccessibleMenu::indexOfChild( const QAccessibleInterface *child) const +{ + QAccessible::Role r = child->role(); + if ((r == QAccessible::MenuItem || r == QAccessible::Separator) && menu()) { + return menu()->actions().indexOf(qobject_cast<QAction*>(child->object())); + } + return -1; +} + +#ifndef QT_NO_MENUBAR +QAccessibleMenuBar::QAccessibleMenuBar(QWidget *w) + : QAccessibleWidget(w, QAccessible::MenuBar) +{ + Q_ASSERT(menuBar()); +} + +QMenuBar *QAccessibleMenuBar::menuBar() const +{ + return qobject_cast<QMenuBar*>(object()); +} + +int QAccessibleMenuBar::childCount() const +{ + return menuBar()->actions().count(); +} + +QAccessibleInterface *QAccessibleMenuBar::child(int index) const +{ + if (index < childCount()) { + return getOrCreateMenu(menuBar(), menuBar()->actions().at(index)); + } + return 0; +} + +int QAccessibleMenuBar::indexOfChild(const QAccessibleInterface *child) const +{ + QAccessible::Role r = child->role(); + if ((r == QAccessible::MenuItem || r == QAccessible::Separator) && menuBar()) { + return menuBar()->actions().indexOf(qobject_cast<QAction*>(child->object())); + } + return -1; +} + +#endif // QT_NO_MENUBAR + +QAccessibleMenuItem::QAccessibleMenuItem(QWidget *owner, QAction *action) +: m_action(action), m_owner(owner) +{ +} + +QAccessibleMenuItem::~QAccessibleMenuItem() +{} + +QAccessibleInterface *QAccessibleMenuItem::childAt(int x, int y ) const +{ + for (int i = childCount() - 1; i >= 0; --i) { + QAccessibleInterface *childInterface = child(i); + if (childInterface->rect().contains(x,y)) { + return childInterface; + } + } + return 0; +} + +int QAccessibleMenuItem::childCount() const +{ + return m_action->menu() ? 1 : 0; +} + +int QAccessibleMenuItem::indexOfChild(const QAccessibleInterface * child) const +{ + if (child && child->role() == QAccessible::PopupMenu && child->object() == m_action->menu()) + return 0; + return -1; +} + +bool QAccessibleMenuItem::isValid() const +{ + return m_action && m_owner ? true : false; +} + +QAccessibleInterface *QAccessibleMenuItem::parent() const +{ + return QAccessible::queryAccessibleInterface(owner()); +} + +QAccessibleInterface *QAccessibleMenuItem::child(int index) const +{ + if (index == 0 && action()->menu()) + return QAccessible::queryAccessibleInterface(action()->menu()); + return 0; +} + +void *QAccessibleMenuItem::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ActionInterface) + return static_cast<QAccessibleActionInterface*>(this); + return 0; +} + +QObject *QAccessibleMenuItem::object() const +{ + return m_action; +} + +QRect QAccessibleMenuItem::rect() const +{ + QRect rect; + QWidget *own = owner(); +#ifndef QT_NO_MENUBAR + if (QMenuBar *menuBar = qobject_cast<QMenuBar*>(own)) { + rect = menuBar->actionGeometry(m_action); + QPoint globalPos = menuBar->mapToGlobal(QPoint(0,0)); + rect = rect.translated(globalPos); + } else +#endif // QT_NO_MENUBAR + if (QMenu *menu = qobject_cast<QMenu*>(own)) { + rect = menu->actionGeometry(m_action); + QPoint globalPos = menu->mapToGlobal(QPoint(0,0)); + rect = rect.translated(globalPos); + } + return rect; +} + +QAccessible::Role QAccessibleMenuItem::role() const +{ + return m_action->isSeparator() ? QAccessible::Separator : QAccessible::MenuItem; +} + +void QAccessibleMenuItem::setText(QAccessible::Text /*t*/, const QString & /*text */) +{ +} + +QAccessible::State QAccessibleMenuItem::state() const +{ + QAccessible::State s; + QWidget *own = owner(); + + if (own && (own->testAttribute(Qt::WA_WState_Visible) == false || m_action->isVisible() == false)) { + s.invisible = true; + } + + if (QMenu *menu = qobject_cast<QMenu*>(own)) { + if (menu->activeAction() == m_action) + s.focused = true; +#ifndef QT_NO_MENUBAR + } else if (QMenuBar *menuBar = qobject_cast<QMenuBar*>(own)) { + if (menuBar->activeAction() == m_action) + s.focused = true; +#endif + } + if (own && own->style()->styleHint(QStyle::SH_Menu_MouseTracking)) + s.hotTracked = true; + if (m_action->isSeparator() || !m_action->isEnabled()) + s.disabled = true; + if (m_action->isChecked()) + s.checked = true; + + return s; +} + +QString QAccessibleMenuItem::text(QAccessible::Text t) const +{ + QString str; + switch (t) { + case QAccessible::Name: + str = m_action->text(); + str = qt_accStripAmp(str); + break; + case QAccessible::Accelerator: { +#ifndef QT_NO_SHORTCUT + QKeySequence key = m_action->shortcut(); + if (!key.isEmpty()) { + str = key.toString(); + } else +#endif + { + str = qt_accHotKey(m_action->text()); + } + break; + } + default: + break; + } + return str; +} + +QStringList QAccessibleMenuItem::actionNames() const +{ + QStringList actions; + if (!m_action || m_action->isSeparator()) + return actions; + + if (m_action->menu()) { + actions << showMenuAction(); + } else { + actions << pressAction(); + } + return actions; +} + +void QAccessibleMenuItem::doAction(const QString &actionName) +{ + if (!m_action->isEnabled()) + return; + + if (actionName == pressAction()) { + m_action->trigger(); + } else if (actionName == showMenuAction()) { + if (QMenuBar *bar = qobject_cast<QMenuBar*>(owner())) { + if (m_action->menu() && m_action->menu()->isVisible()) { + m_action->menu()->hide(); + } else { + bar->setActiveAction(m_action); + } + } else if (QMenu *menu = qobject_cast<QMenu*>(owner())){ + if (m_action->menu() && m_action->menu()->isVisible()) { + m_action->menu()->hide(); + } else { + menu->setActiveAction(m_action); + } + } + } +} + +QStringList QAccessibleMenuItem::keyBindingsForAction(const QString &) const +{ + return QStringList(); +} + + +QAction *QAccessibleMenuItem::action() const +{ + return m_action; +} + +QWidget *QAccessibleMenuItem::owner() const +{ + return m_owner; +} + +#endif // QT_NO_MENU + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY + diff --git a/src/widgets/accessible/qaccessiblemenu.h b/src/widgets/accessible/qaccessiblemenu.h new file mode 100644 index 0000000000..593821e7ff --- /dev/null +++ b/src/widgets/accessible/qaccessiblemenu.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLEMENU_H +#define QACCESSIBLEMENU_H + +#include <QtWidgets/qaccessiblewidget.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +#ifndef QT_NO_MENU +class QMenu; +class QMenuBar; +class QAction; + +class QAccessibleMenu : public QAccessibleWidget +{ +public: + explicit QAccessibleMenu(QWidget *w); + + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + QAccessible::Role role() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + QAccessibleInterface *parent() const Q_DECL_OVERRIDE; + int indexOfChild( const QAccessibleInterface *child ) const Q_DECL_OVERRIDE; + +protected: + QMenu *menu() const; +}; + +#ifndef QT_NO_MENUBAR +class QAccessibleMenuBar : public QAccessibleWidget +{ +public: + explicit QAccessibleMenuBar(QWidget *w); + + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + +protected: + QMenuBar *menuBar() const; +}; +#endif // QT_NO_MENUBAR + + +class QAccessibleMenuItem : public QAccessibleInterface, public QAccessibleActionInterface +{ +public: + explicit QAccessibleMenuItem(QWidget *owner, QAction *w); + + ~QAccessibleMenuItem(); + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + bool isValid() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface * child) const Q_DECL_OVERRIDE; + + QAccessibleInterface *parent() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + QObject * object() const Q_DECL_OVERRIDE; + QRect rect() const Q_DECL_OVERRIDE; + QAccessible::Role role() const Q_DECL_OVERRIDE; + void setText(QAccessible::Text t, const QString & text) Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + + // QAccessibleActionInterface + QStringList actionNames() const Q_DECL_OVERRIDE; + void doAction(const QString &actionName) Q_DECL_OVERRIDE; + QStringList keyBindingsForAction(const QString &actionName) const Q_DECL_OVERRIDE; + + QWidget *owner() const; +protected: + QAction *action() const; +private: + QAction *m_action; + QPointer<QWidget> m_owner; // can hold either QMenu or the QMenuBar that contains the action +}; + +#endif // QT_NO_MENU + +QT_END_NAMESPACE +#endif // QT_NO_ACCESSIBILITY +#endif // QACCESSIBLEMENU_H diff --git a/src/widgets/accessible/qaccessiblewidget.cpp b/src/widgets/accessible/qaccessiblewidget.cpp index 4e5fade3f4..48f99f4d35 100644 --- a/src/widgets/accessible/qaccessiblewidget.cpp +++ b/src/widgets/accessible/qaccessiblewidget.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -134,7 +134,7 @@ static int qt_accAmpIndex(const QString &text) #endif } -QString Q_WIDGETS_EXPORT qt_accStripAmp(const QString &text) +QString qt_accStripAmp(const QString &text) { QString newText(text); int ampIndex = qt_accAmpIndex(newText); @@ -144,7 +144,7 @@ QString Q_WIDGETS_EXPORT qt_accStripAmp(const QString &text) return newText.replace(QLatin1String("&&"), QLatin1String("&")); } -QString Q_WIDGETS_EXPORT qt_accHotKey(const QString &text) +QString qt_accHotKey(const QString &text) { int ampIndex = qt_accAmpIndex(text); if (ampIndex != -1) @@ -240,10 +240,10 @@ QWidget *QAccessibleWidget::widget() const */ QObject *QAccessibleWidget::parentObject() const { - QObject *parent = object()->parent(); - if (!parent) - parent = qApp; - return parent; + QWidget *w = widget(); + if (!w || w->isWindow() || !w->parentWidget()) + return qApp; + return w->parent(); } /*! \reimp */ @@ -353,11 +353,7 @@ QAccessibleWidget::relations(QAccessible::Relation match /*= QAccessible::AllRel /*! \reimp */ QAccessibleInterface *QAccessibleWidget::parent() const { - Q_ASSERT(widget()); - QObject *parentWidget= widget()->parentWidget(); - if (!parentWidget) - parentWidget = qApp; - return QAccessible::queryAccessibleInterface(parentWidget); + return QAccessible::queryAccessibleInterface(parentObject()); } /*! \reimp */ diff --git a/src/widgets/accessible/qaccessiblewidget.h b/src/widgets/accessible/qaccessiblewidget.h index 819bb5b8c4..079e5dbc42 100644 --- a/src/widgets/accessible/qaccessiblewidget.h +++ b/src/widgets/accessible/qaccessiblewidget.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. diff --git a/src/widgets/accessible/qaccessiblewidgetfactory.cpp b/src/widgets/accessible/qaccessiblewidgetfactory.cpp new file mode 100644 index 0000000000..3d123cc9ab --- /dev/null +++ b/src/widgets/accessible/qaccessiblewidgetfactory.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblewidgets.h" +#include "qaccessiblemenu.h" +#include "simplewidgets.h" +#include "rangecontrols.h" +#include "complexwidgets.h" +#include "itemviews.h" + +#include <qpushbutton.h> +#include <qtoolbutton.h> +#include <qtreeview.h> +#include <qvariant.h> +#include <qaccessible.h> + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +QAccessibleInterface *qAccessibleFactory(const QString &classname, QObject *object) +{ + QAccessibleInterface *iface = 0; + if (!object || !object->isWidgetType()) + return iface; + QWidget *widget = static_cast<QWidget*>(object); + + if (false) { +#ifndef QT_NO_LINEEDIT + } else if (classname == QLatin1String("QLineEdit")) { + iface = new QAccessibleLineEdit(widget); +#endif +#ifndef QT_NO_COMBOBOX + } else if (classname == QLatin1String("QComboBox")) { + iface = new QAccessibleComboBox(widget); +#endif +#ifndef QT_NO_SPINBOX + } else if (classname == QLatin1String("QAbstractSpinBox")) { + iface = new QAccessibleAbstractSpinBox(widget); + } else if (classname == QLatin1String("QSpinBox")) { + iface = new QAccessibleSpinBox(widget); + } else if (classname == QLatin1String("QDoubleSpinBox")) { + iface = new QAccessibleDoubleSpinBox(widget); +#endif +#ifndef QT_NO_SCROLLBAR + } else if (classname == QLatin1String("QScrollBar")) { + iface = new QAccessibleScrollBar(widget); +#endif + } else if (classname == QLatin1String("QAbstractSlider")) { + iface = new QAccessibleAbstractSlider(widget); +#ifndef QT_NO_SLIDER + } else if (classname == QLatin1String("QSlider")) { + iface = new QAccessibleSlider(widget); +#endif +#ifndef QT_NO_TOOLBUTTON + } else if (classname == QLatin1String("QToolButton")) { + QAccessible::Role role = QAccessible::NoRole; +#ifndef QT_NO_MENU + QToolButton *tb = qobject_cast<QToolButton*>(widget); + if (!tb->menu()) + role = tb->isCheckable() ? QAccessible::CheckBox : QAccessible::PushButton; + else if (tb->popupMode() == QToolButton::DelayedPopup) + role = QAccessible::ButtonDropDown; + else +#endif + role = QAccessible::ButtonMenu; + iface = new QAccessibleToolButton(widget, role); +#endif // QT_NO_TOOLBUTTON + } else if (classname == QLatin1String("QCheckBox")) { + iface = new QAccessibleButton(widget, QAccessible::CheckBox); + } else if (classname == QLatin1String("QRadioButton")) { + iface = new QAccessibleButton(widget, QAccessible::RadioButton); + } else if (classname == QLatin1String("QPushButton")) { + QAccessible::Role role = QAccessible::NoRole; + QPushButton *pb = qobject_cast<QPushButton*>(widget); +#ifndef QT_NO_MENU + if (pb->menu()) + role = QAccessible::ButtonMenu; + else +#endif + if (pb->isCheckable()) + role = QAccessible::CheckBox; + else + role = QAccessible::PushButton; + iface = new QAccessibleButton(widget, role); + } else if (classname == QLatin1String("QAbstractButton")) { + iface = new QAccessibleButton(widget, QAccessible::PushButton); + } else if (classname == QLatin1String("QDialog")) { + iface = new QAccessibleWidget(widget, QAccessible::Dialog); + } else if (classname == QLatin1String("QMessageBox")) { + iface = new QAccessibleWidget(widget, QAccessible::AlertMessage); +#ifndef QT_NO_MAINWINDOW + } else if (classname == QLatin1String("QMainWindow")) { + iface = new QAccessibleMainWindow(widget); +#endif + } else if (classname == QLatin1String("QLabel") || classname == QLatin1String("QLCDNumber")) { + iface = new QAccessibleDisplay(widget); +#ifndef QT_NO_GROUPBOX + } else if (classname == QLatin1String("QGroupBox")) { + iface = new QAccessibleGroupBox(widget); +#endif + } else if (classname == QLatin1String("QStatusBar")) { + iface = new QAccessibleDisplay(widget); +#ifndef QT_NO_PROGRESSBAR + } else if (classname == QLatin1String("QProgressBar")) { + iface = new QAccessibleProgressBar(widget); +#endif + } else if (classname == QLatin1String("QToolBar")) { + iface = new QAccessibleWidget(widget, QAccessible::ToolBar, widget->windowTitle()); +#ifndef QT_NO_MENUBAR + } else if (classname == QLatin1String("QMenuBar")) { + iface = new QAccessibleMenuBar(widget); +#endif +#ifndef QT_NO_MENU + } else if (classname == QLatin1String("QMenu")) { + iface = new QAccessibleMenu(widget); +#endif +#ifndef QT_NO_ITEMVIEWS + } else if (classname == QLatin1String("QTreeView")) { + iface = new QAccessibleTree(widget); + } else if (classname == QLatin1String("QTableView") || classname == QLatin1String("QListView")) { + iface = new QAccessibleTable(widget); + // ### This should be cleaned up. We return the parent for the scrollarea to hide it. +#endif // QT_NO_ITEMVIEWS +#ifndef QT_NO_TABBAR + } else if (classname == QLatin1String("QTabBar")) { + iface = new QAccessibleTabBar(widget); +#endif + } else if (classname == QLatin1String("QSizeGrip")) { + iface = new QAccessibleWidget(widget, QAccessible::Grip); +#ifndef QT_NO_SPLITTER + } else if (classname == QLatin1String("QSplitter")) { + iface = new QAccessibleWidget(widget, QAccessible::Splitter); + } else if (classname == QLatin1String("QSplitterHandle")) { + iface = new QAccessibleWidget(widget, QAccessible::Grip); +#endif +#if !defined(QT_NO_TEXTEDIT) && !defined(QT_NO_CURSOR) + } else if (classname == QLatin1String("QTextEdit")) { + iface = new QAccessibleTextEdit(widget); + } else if (classname == QLatin1String("QPlainTextEdit")) { + iface = new QAccessiblePlainTextEdit(widget); +#endif + } else if (classname == QLatin1String("QTipLabel")) { + iface = new QAccessibleDisplay(widget, QAccessible::ToolTip); + } else if (classname == QLatin1String("QFrame")) { + iface = new QAccessibleWidget(widget, QAccessible::Border); +#ifndef QT_NO_STACKEDWIDGET + } else if (classname == QLatin1String("QStackedWidget")) { + iface = new QAccessibleStackedWidget(widget); +#endif +#ifndef QT_NO_TOOLBOX + } else if (classname == QLatin1String("QToolBox")) { + iface = new QAccessibleToolBox(widget); +#endif +#ifndef QT_NO_MDIAREA + } else if (classname == QLatin1String("QMdiArea")) { + iface = new QAccessibleMdiArea(widget); + } else if (classname == QLatin1String("QMdiSubWindow")) { + iface = new QAccessibleMdiSubWindow(widget); +#endif + } else if (classname == QLatin1String("QDialogButtonBox")) { + iface = new QAccessibleDialogButtonBox(widget); +#ifndef QT_NO_DIAL + } else if (classname == QLatin1String("QDial")) { + iface = new QAccessibleDial(widget); +#endif +#ifndef QT_NO_RUBBERBAND + } else if (classname == QLatin1String("QRubberBand")) { + iface = new QAccessibleWidget(widget, QAccessible::Border); +#endif +#if !defined(QT_NO_TEXTBROWSER) && !defined(QT_NO_CURSOR) + } else if (classname == QLatin1String("QTextBrowser")) { + iface = new QAccessibleTextBrowser(widget); +#endif +#ifndef QT_NO_SCROLLAREA + } else if (classname == QLatin1String("QAbstractScrollArea")) { + iface = new QAccessibleAbstractScrollArea(widget); + } else if (classname == QLatin1String("QScrollArea")) { + iface = new QAccessibleScrollArea(widget); +#endif +#ifndef QT_NO_CALENDARWIDGET + } else if (classname == QLatin1String("QCalendarWidget")) { + iface = new QAccessibleCalendarWidget(widget); +#endif +#ifndef QT_NO_DOCKWIDGET + } else if (classname == QLatin1String("QDockWidget")) { + iface = new QAccessibleDockWidget(widget); +#endif + + } else if (classname == QLatin1String("QDesktopScreenWidget")) { + iface = 0; + } else if (classname == QLatin1String("QWidget")) { + iface = new QAccessibleWidget(widget); + } else if (classname == QLatin1String("QWindowContainer")) { + iface = new QAccessibleWindowContainer(widget); + } + + return iface; +} + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/widgets/accessible/qaccessiblewidgetfactory_p.h b/src/widgets/accessible/qaccessiblewidgetfactory_p.h new file mode 100644 index 0000000000..bf5e2da6bc --- /dev/null +++ b/src/widgets/accessible/qaccessiblewidgetfactory_p.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWidgets module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qaccessible.h> + +#ifndef QACCESSIBLEWIDGETFACTORY_H +#define QACCESSIBLEWIDGETFACTORY_H + +QT_BEGIN_NAMESPACE + +QAccessibleInterface *qAccessibleFactory(const QString &classname, QObject *object); + +QT_END_NAMESPACE + +#endif diff --git a/src/widgets/accessible/qaccessiblewidgets.cpp b/src/widgets/accessible/qaccessiblewidgets.cpp new file mode 100644 index 0000000000..cc6310d591 --- /dev/null +++ b/src/widgets/accessible/qaccessiblewidgets.cpp @@ -0,0 +1,1021 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessiblewidgets.h" +#include "qabstracttextdocumentlayout.h" +#include "qapplication.h" +#include "qclipboard.h" +#include "qtextedit.h" +#include "private/qtextedit_p.h" +#include "qtextdocument.h" +#include "qtextobject.h" +#include "qplaintextedit.h" +#include "qtextboundaryfinder.h" +#include "qscrollbar.h" +#include "qdebug.h" +#include <QApplication> +#include <QStackedWidget> +#include <QToolBox> +#include <QMdiArea> +#include <QMdiSubWindow> +#include <QDialogButtonBox> +#include <limits.h> +#include <QRubberBand> +#include <QTextBrowser> +#include <QCalendarWidget> +#include <QAbstractItemView> +#include <QDockWidget> +#include <QMainWindow> +#include <QAbstractButton> +#include <private/qdockwidget_p.h> +#include <QFocusFrame> + +#ifndef QT_NO_ACCESSIBILITY + +QT_BEGIN_NAMESPACE + +QString qt_accStripAmp(const QString &text); + +QList<QWidget*> childWidgets(const QWidget *widget) +{ + if (widget == 0) + return QList<QWidget*>(); + QList<QObject*> list = widget->children(); + QList<QWidget*> widgets; + for (int i = 0; i < list.size(); ++i) { + QWidget *w = qobject_cast<QWidget *>(list.at(i)); + if (!w) + continue; + QString objectName = w->objectName(); + if (!w->isWindow() + && !qobject_cast<QFocusFrame*>(w) + && !qobject_cast<QMenu*>(w) + && objectName != QLatin1String("qt_rubberband") + && objectName != QLatin1String("qt_qmainwindow_extended_splitter")) { + widgets.append(w); + } + } + return widgets; +} + +#if !defined(QT_NO_TEXTEDIT) && !defined(QT_NO_CURSOR) + +QAccessiblePlainTextEdit::QAccessiblePlainTextEdit(QWidget* o) + :QAccessibleTextWidget(o) +{ + Q_ASSERT(widget()->inherits("QPlainTextEdit")); +} + +QPlainTextEdit* QAccessiblePlainTextEdit::plainTextEdit() const +{ + return static_cast<QPlainTextEdit *>(widget()); +} + +QString QAccessiblePlainTextEdit::text(QAccessible::Text t) const +{ + if (t == QAccessible::Value) + return plainTextEdit()->toPlainText(); + + return QAccessibleWidget::text(t); +} + +void QAccessiblePlainTextEdit::setText(QAccessible::Text t, const QString &text) +{ + if (t != QAccessible::Value) { + QAccessibleWidget::setText(t, text); + return; + } + if (plainTextEdit()->isReadOnly()) + return; + + plainTextEdit()->setPlainText(text); +} + +QAccessible::State QAccessiblePlainTextEdit::state() const +{ + QAccessible::State st = QAccessibleTextWidget::state(); + if (plainTextEdit()->isReadOnly()) + st.readOnly = true; + else + st.editable = true; + return st; +} + +void *QAccessiblePlainTextEdit::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::TextInterface) + return static_cast<QAccessibleTextInterface*>(this); + else if (t == QAccessible::EditableTextInterface) + return static_cast<QAccessibleEditableTextInterface*>(this); + return QAccessibleWidget::interface_cast(t); +} + +QPoint QAccessiblePlainTextEdit::scrollBarPosition() const +{ + QPoint result; + result.setX(plainTextEdit()->horizontalScrollBar() ? plainTextEdit()->horizontalScrollBar()->sliderPosition() : 0); + result.setY(plainTextEdit()->verticalScrollBar() ? plainTextEdit()->verticalScrollBar()->sliderPosition() : 0); + return result; +} + +QTextCursor QAccessiblePlainTextEdit::textCursor() const +{ + return plainTextEdit()->textCursor(); +} + +void QAccessiblePlainTextEdit::setTextCursor(const QTextCursor &textCursor) +{ + plainTextEdit()->setTextCursor(textCursor); +} + +QTextDocument* QAccessiblePlainTextEdit::textDocument() const +{ + return plainTextEdit()->document(); +} + +QWidget* QAccessiblePlainTextEdit::viewport() const +{ + return plainTextEdit()->viewport(); +} + +void QAccessiblePlainTextEdit::scrollToSubstring(int startIndex, int endIndex) +{ + //TODO: Not implemented + Q_UNUSED(startIndex); + Q_UNUSED(endIndex); +} + + +/*! + \class QAccessibleTextEdit + \brief The QAccessibleTextEdit class implements the QAccessibleInterface for richtext editors. + \internal +*/ + +/*! + \fn QAccessibleTextEdit::QAccessibleTextEdit(QWidget *widget) + + Constructs a QAccessibleTextEdit object for a \a widget. +*/ +QAccessibleTextEdit::QAccessibleTextEdit(QWidget *o) +: QAccessibleTextWidget(o, QAccessible::EditableText) +{ + Q_ASSERT(widget()->inherits("QTextEdit")); +} + +/*! Returns the text edit. */ +QTextEdit *QAccessibleTextEdit::textEdit() const +{ + return static_cast<QTextEdit *>(widget()); +} + +QTextCursor QAccessibleTextEdit::textCursor() const +{ + return textEdit()->textCursor(); +} + +QTextDocument *QAccessibleTextEdit::textDocument() const +{ + return textEdit()->document(); +} + +void QAccessibleTextEdit::setTextCursor(const QTextCursor &textCursor) +{ + textEdit()->setTextCursor(textCursor); +} + +QWidget *QAccessibleTextEdit::viewport() const +{ + return textEdit()->viewport(); +} + +QPoint QAccessibleTextEdit::scrollBarPosition() const +{ + QPoint result; + result.setX(textEdit()->horizontalScrollBar() ? textEdit()->horizontalScrollBar()->sliderPosition() : 0); + result.setY(textEdit()->verticalScrollBar() ? textEdit()->verticalScrollBar()->sliderPosition() : 0); + return result; +} + +QString QAccessibleTextEdit::text(QAccessible::Text t) const +{ + if (t == QAccessible::Value) + return textEdit()->toPlainText(); + + return QAccessibleWidget::text(t); +} + +void QAccessibleTextEdit::setText(QAccessible::Text t, const QString &text) +{ + if (t != QAccessible::Value) { + QAccessibleWidget::setText(t, text); + return; + } + if (textEdit()->isReadOnly()) + return; + + textEdit()->setText(text); +} + +QAccessible::State QAccessibleTextEdit::state() const +{ + QAccessible::State st = QAccessibleTextWidget::state(); + if (textEdit()->isReadOnly()) + st.readOnly = true; + else + st.editable = true; + return st; +} + +void *QAccessibleTextEdit::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::TextInterface) + return static_cast<QAccessibleTextInterface*>(this); + else if (t == QAccessible::EditableTextInterface) + return static_cast<QAccessibleEditableTextInterface*>(this); + return QAccessibleWidget::interface_cast(t); +} + +void QAccessibleTextEdit::scrollToSubstring(int startIndex, int endIndex) +{ + QTextEdit *edit = textEdit(); + + QTextCursor cursor = textCursor(); + cursor.setPosition(startIndex); + QRect r = edit->cursorRect(cursor); + + cursor.setPosition(endIndex); + r.setBottomRight(edit->cursorRect(cursor).bottomRight()); + + r.moveTo(r.x() + edit->horizontalScrollBar()->value(), + r.y() + edit->verticalScrollBar()->value()); + + // E V I L, but ensureVisible is not public + if (!QMetaObject::invokeMethod(edit, "_q_ensureVisible", Q_ARG(QRectF, r))) + qWarning("AccessibleTextEdit::scrollToSubstring failed!"); +} + +#endif // QT_NO_TEXTEDIT && QT_NO_CURSOR + +#ifndef QT_NO_STACKEDWIDGET +// ======================= QAccessibleStackedWidget ====================== +QAccessibleStackedWidget::QAccessibleStackedWidget(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::LayeredPane) +{ + Q_ASSERT(qobject_cast<QStackedWidget *>(widget)); +} + +QAccessibleInterface *QAccessibleStackedWidget::childAt(int x, int y) const +{ + if (!stackedWidget()->isVisible()) + return 0; + QWidget *currentWidget = stackedWidget()->currentWidget(); + if (!currentWidget) + return 0; + QPoint position = currentWidget->mapFromGlobal(QPoint(x, y)); + if (currentWidget->rect().contains(position)) + return child(stackedWidget()->currentIndex()); + return 0; +} + +int QAccessibleStackedWidget::childCount() const +{ + return stackedWidget()->count(); +} + +int QAccessibleStackedWidget::indexOfChild(const QAccessibleInterface *child) const +{ + if (!child) + return -1; + + QWidget *widget = qobject_cast<QWidget*>(child->object()); + return stackedWidget()->indexOf(widget); +} + +QAccessibleInterface *QAccessibleStackedWidget::child(int index) const +{ + if (index < 0 || index >= stackedWidget()->count()) + return 0; + return QAccessible::queryAccessibleInterface(stackedWidget()->widget(index)); +} + +QStackedWidget *QAccessibleStackedWidget::stackedWidget() const +{ + return static_cast<QStackedWidget *>(object()); +} +#endif // QT_NO_STACKEDWIDGET + +#ifndef QT_NO_TOOLBOX +// ======================= QAccessibleToolBox ====================== +QAccessibleToolBox::QAccessibleToolBox(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::LayeredPane) +{ + Q_ASSERT(qobject_cast<QToolBox *>(widget)); +} + +QToolBox * QAccessibleToolBox::toolBox() const +{ + return static_cast<QToolBox *>(object()); +} +#endif // QT_NO_TOOLBOX + +// ======================= QAccessibleMdiArea ====================== +#ifndef QT_NO_MDIAREA +QAccessibleMdiArea::QAccessibleMdiArea(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::LayeredPane) +{ + Q_ASSERT(qobject_cast<QMdiArea *>(widget)); +} + +int QAccessibleMdiArea::childCount() const +{ + return mdiArea()->subWindowList().count(); +} + +QAccessibleInterface *QAccessibleMdiArea::child(int index) const +{ + QList<QMdiSubWindow *> subWindows = mdiArea()->subWindowList(); + QWidget *targetObject = subWindows.value(index); + if (!targetObject) + return 0; + return QAccessible::queryAccessibleInterface(targetObject); +} + + +int QAccessibleMdiArea::indexOfChild(const QAccessibleInterface *child) const +{ + if (!child || !child->object() || mdiArea()->subWindowList().isEmpty()) + return -1; + if (QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(child->object())) { + return mdiArea()->subWindowList().indexOf(window); + } + return -1; +} + +QMdiArea *QAccessibleMdiArea::mdiArea() const +{ + return static_cast<QMdiArea *>(object()); +} + +// ======================= QAccessibleMdiSubWindow ====================== +QAccessibleMdiSubWindow::QAccessibleMdiSubWindow(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::Window) +{ + Q_ASSERT(qobject_cast<QMdiSubWindow *>(widget)); +} + +QString QAccessibleMdiSubWindow::text(QAccessible::Text textType) const +{ + if (textType == QAccessible::Name) { + QString title = mdiSubWindow()->windowTitle(); + title.replace(QLatin1String("[*]"), QLatin1String("")); + return title; + } + return QAccessibleWidget::text(textType); +} + +void QAccessibleMdiSubWindow::setText(QAccessible::Text textType, const QString &text) +{ + if (textType == QAccessible::Name) + mdiSubWindow()->setWindowTitle(text); + else + QAccessibleWidget::setText(textType, text); +} + +QAccessible::State QAccessibleMdiSubWindow::state() const +{ + QAccessible::State state; + state.focusable = true; + if (!mdiSubWindow()->isMaximized()) { + state.movable = true; + state.sizeable = true; + } + if (mdiSubWindow()->isAncestorOf(QApplication::focusWidget()) + || QApplication::focusWidget() == mdiSubWindow()) + state.focused = true; + if (!mdiSubWindow()->isVisible()) + state.invisible = true; + if (const QWidget *parent = mdiSubWindow()->parentWidget()) + if (!parent->contentsRect().contains(mdiSubWindow()->geometry())) + state.offscreen = true; + if (!mdiSubWindow()->isEnabled()) + state.disabled = true; + return state; +} + +int QAccessibleMdiSubWindow::childCount() const +{ + if (mdiSubWindow()->widget()) + return 1; + return 0; +} + +QAccessibleInterface *QAccessibleMdiSubWindow::child(int index) const +{ + QMdiSubWindow *source = mdiSubWindow(); + if (index != 0 || !source->widget()) + return 0; + + return QAccessible::queryAccessibleInterface(source->widget()); +} + +int QAccessibleMdiSubWindow::indexOfChild(const QAccessibleInterface *child) const +{ + if (child && child->object() && child->object() == mdiSubWindow()->widget()) + return 0; + return -1; +} + +QRect QAccessibleMdiSubWindow::rect() const +{ + if (mdiSubWindow()->isHidden()) + return QRect(); + if (!mdiSubWindow()->parent()) + return QAccessibleWidget::rect(); + const QPoint pos = mdiSubWindow()->mapToGlobal(QPoint(0, 0)); + return QRect(pos, mdiSubWindow()->size()); +} + +QMdiSubWindow *QAccessibleMdiSubWindow::mdiSubWindow() const +{ + return static_cast<QMdiSubWindow *>(object()); +} +#endif // QT_NO_MDIAREA + +#ifndef QT_NO_DIALOGBUTTONBOX +// ======================= QAccessibleDialogButtonBox ====================== +QAccessibleDialogButtonBox::QAccessibleDialogButtonBox(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::Grouping) +{ + Q_ASSERT(qobject_cast<QDialogButtonBox*>(widget)); +} + +#endif // QT_NO_DIALOGBUTTONBOX + +#if !defined(QT_NO_TEXTBROWSER) && !defined(QT_NO_CURSOR) +QAccessibleTextBrowser::QAccessibleTextBrowser(QWidget *widget) + : QAccessibleTextEdit(widget) +{ + Q_ASSERT(qobject_cast<QTextBrowser *>(widget)); +} + +QAccessible::Role QAccessibleTextBrowser::role() const +{ + return QAccessible::StaticText; +} +#endif // QT_NO_TEXTBROWSER && QT_NO_CURSOR + +#ifndef QT_NO_CALENDARWIDGET +// ===================== QAccessibleCalendarWidget ======================== +QAccessibleCalendarWidget::QAccessibleCalendarWidget(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::Table) +{ + Q_ASSERT(qobject_cast<QCalendarWidget *>(widget)); +} + +int QAccessibleCalendarWidget::childCount() const +{ + return calendarWidget()->isNavigationBarVisible() ? 2 : 1; +} + +int QAccessibleCalendarWidget::indexOfChild(const QAccessibleInterface *child) const +{ + if (!child || !child->object() || childCount() <= 0) + return -1; + if (qobject_cast<QAbstractItemView *>(child->object())) + return childCount() - 1; // FIXME + return 0; +} + +QAccessibleInterface *QAccessibleCalendarWidget::child(int index) const +{ + if (index < 0 || index >= childCount()) + return 0; + + if (childCount() > 1 && index == 0) + return QAccessible::queryAccessibleInterface(navigationBar()); + + return QAccessible::queryAccessibleInterface(calendarView()); +} + +QCalendarWidget *QAccessibleCalendarWidget::calendarWidget() const +{ + return static_cast<QCalendarWidget *>(object()); +} + +QAbstractItemView *QAccessibleCalendarWidget::calendarView() const +{ + foreach (QObject *child, calendarWidget()->children()) { + if (child->objectName() == QLatin1String("qt_calendar_calendarview")) + return static_cast<QAbstractItemView *>(child); + } + return 0; +} + +QWidget *QAccessibleCalendarWidget::navigationBar() const +{ + foreach (QObject *child, calendarWidget()->children()) { + if (child->objectName() == QLatin1String("qt_calendar_navigationbar")) + return static_cast<QWidget *>(child); + } + return 0; +} +#endif // QT_NO_CALENDARWIDGET + +#ifndef QT_NO_DOCKWIDGET + +// Dock Widget - order of children: +// - Content widget +// - Float button +// - Close button +// If there is a custom title bar widget, that one becomes child 1, after the content 0 +// (in that case the buttons are ignored) +QAccessibleDockWidget::QAccessibleDockWidget(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::Window) +{ +} + +QDockWidgetLayout *QAccessibleDockWidget::dockWidgetLayout() const +{ + return qobject_cast<QDockWidgetLayout*>(dockWidget()->layout()); +} + +int QAccessibleDockWidget::childCount() const +{ + if (dockWidget()->titleBarWidget()) { + return dockWidget()->widget() ? 2 : 1; + } + return dockWidgetLayout()->count(); +} + +QAccessibleInterface *QAccessibleDockWidget::child(int index) const +{ + if (dockWidget()->titleBarWidget()) { + if ((!dockWidget()->widget() && index == 0) || (index == 1)) + return QAccessible::queryAccessibleInterface(dockWidget()->titleBarWidget()); + if (index == 0) + return QAccessible::queryAccessibleInterface(dockWidget()->widget()); + } else { + QLayoutItem *item = dockWidgetLayout()->itemAt(index); + if (item) + return QAccessible::queryAccessibleInterface(item->widget()); + } + return 0; +} + +int QAccessibleDockWidget::indexOfChild(const QAccessibleInterface *child) const +{ + if (!child || !child->object() || child->object()->parent() != object()) + return -1; + + if (dockWidget()->titleBarWidget() == child->object()) { + return dockWidget()->widget() ? 1 : 0; + } + + return dockWidgetLayout()->indexOf(qobject_cast<QWidget*>(child->object())); +} + +QRect QAccessibleDockWidget::rect() const +{ + QRect rect; + + if (dockWidget()->isFloating()) { + rect = dockWidget()->frameGeometry(); + } else { + rect = dockWidget()->rect(); + rect.moveTopLeft(dockWidget()->mapToGlobal(rect.topLeft())); + } + + return rect; +} + +QDockWidget *QAccessibleDockWidget::dockWidget() const +{ + return static_cast<QDockWidget *>(object()); +} + +QString QAccessibleDockWidget::text(QAccessible::Text t) const +{ + if (t == QAccessible::Name) { + return qt_accStripAmp(dockWidget()->windowTitle()); + } + return QString(); +} +#endif // QT_NO_DOCKWIDGET + +#ifndef QT_NO_CURSOR + +QAccessibleTextWidget::QAccessibleTextWidget(QWidget *o, QAccessible::Role r, const QString &name): + QAccessibleWidget(o, r, name) +{ + +} + +QAccessible::State QAccessibleTextWidget::state() const +{ + QAccessible::State s = QAccessibleWidget::state(); + s.selectableText = true; + s.multiLine = true; + return s; +} + +QRect QAccessibleTextWidget::characterRect(int offset) const +{ + QTextBlock block = textDocument()->findBlock(offset); + if (!block.isValid()) + return QRect(); + + QTextLayout *layout = block.layout(); + QPointF layoutPosition = layout->position(); + int relativeOffset = offset - block.position(); + QTextLine line = layout->lineForTextPosition(relativeOffset); + + QRect r; + + if (line.isValid()) { + qreal x = line.cursorToX(relativeOffset); + QFontMetrics fm(textCursor().charFormat().font()); + const QString ch = text(offset, offset + 1); + if (!ch.isEmpty()) { + int w = fm.width(ch); + int h = fm.height(); + r = QRect(layoutPosition.x() + x, layoutPosition.y() + line.y(), + w, h); + r.moveTo(viewport()->mapToGlobal(r.topLeft())); + } + r.translate(-scrollBarPosition()); + } + + return r; +} + +int QAccessibleTextWidget::offsetAtPoint(const QPoint &point) const +{ + QPoint p = viewport()->mapFromGlobal(point); + // convert to document coordinates + p += scrollBarPosition(); + return textDocument()->documentLayout()->hitTest(p, Qt::ExactHit); +} + +int QAccessibleTextWidget::selectionCount() const +{ + return textCursor().hasSelection() ? 1 : 0; +} + +QString QAccessibleTextWidget::attributes(int offset, int *startOffset, int *endOffset) const +{ + /* The list of attributes can be found at: + http://linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes + */ + + // IAccessible2 defines -1 as length and -2 as cursor position + if (offset == -2) + offset = cursorPosition(); + + const int charCount = characterCount(); + + // -1 doesn't make much sense here, but it's better to return something + // screen readers may ask for text attributes at the cursor pos which may be equal to length + if (offset == -1 || offset == charCount) + offset = charCount - 1; + + if (offset < 0 || offset > charCount) { + *startOffset = -1; + *endOffset = -1; + return QString(); + } + + + QTextCursor cursor = textCursor(); + cursor.setPosition(offset); + QTextBlock block = cursor.block(); + + int blockStart = block.position(); + int blockEnd = blockStart + block.length(); + + QTextBlock::iterator iter = block.begin(); + while (!iter.fragment().contains(offset)) + ++iter; + + QTextFragment fragment = iter.fragment(); + int pos = fragment.position(); + + // text block and fragment may overlap, use the smallest common range + *startOffset = qMax(pos, blockStart); + Q_ASSERT(*startOffset <= offset); + *endOffset = qMin(pos + fragment.length(), blockEnd); + Q_ASSERT(*endOffset >= offset); + + QTextCharFormat charFormat = fragment.charFormat(); + QTextBlockFormat blockFormat = cursor.blockFormat(); + + QMap<QByteArray, QString> attrs; + QString family = charFormat.fontFamily(); + if (!family.isEmpty()) { + family = family.replace('\\',QStringLiteral("\\\\")); + family = family.replace(':',QStringLiteral("\\:")); + family = family.replace(',',QStringLiteral("\\,")); + family = family.replace('=',QStringLiteral("\\=")); + family = family.replace(';',QStringLiteral("\\;")); + family = family.replace('\"',QStringLiteral("\\\"")); + attrs["font-family"] = QString::fromLatin1("\"%1\"").arg(family); + } + + int fontSize = int(charFormat.fontPointSize()); + if (fontSize) + attrs["font-size"] = QString::fromLatin1("%1pt").arg(fontSize); + + //Different weight values are not handled + attrs["font-weight"] = QString::fromLatin1(charFormat.fontWeight() > QFont::Normal ? "bold" : "normal"); + + QFont::Style style = charFormat.font().style(); + attrs["font-style"] = QString::fromLatin1((style == QFont::StyleItalic) ? "italic" : ((style == QFont::StyleOblique) ? "oblique": "normal")); + + attrs["text-underline-style"] = QString::fromLatin1(charFormat.font().underline() ? "solid" : "none"); + + QTextCharFormat::VerticalAlignment alignment = charFormat.verticalAlignment(); + attrs["text-position"] = QString::fromLatin1((alignment == QTextCharFormat::AlignSubScript) ? "sub" : ((alignment == QTextCharFormat::AlignSuperScript) ? "super" : "baseline" )); + + QBrush background = charFormat.background(); + if (background.style() == Qt::SolidPattern) { + attrs["background-color"] = QString::fromLatin1("rgb(%1,%2,%3)").arg(background.color().red()).arg(background.color().green()).arg(background.color().blue()); + } + + QBrush foreground = charFormat.foreground(); + if (foreground.style() == Qt::SolidPattern) { + attrs["color"] = QString::fromLatin1("rgb(%1,%2,%3)").arg(foreground.color().red()).arg(foreground.color().green()).arg(foreground.color().blue()); + } + + switch (blockFormat.alignment() & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter | Qt::AlignJustify)) { + case Qt::AlignLeft: + attrs["text-align"] = QStringLiteral("left"); + break; + case Qt::AlignRight: + attrs["text-align"] = QStringLiteral("right"); + break; + case Qt::AlignHCenter: + attrs["text-align"] = QStringLiteral("center"); + break; + case Qt::AlignJustify: + attrs["text-align"] = QStringLiteral("left"); + break; + } + + QString result; + foreach (const QByteArray &attributeName, attrs.keys()) { + result.append(QString::fromLatin1(attributeName)).append(':').append(attrs[attributeName]).append(';'); + } + + return result; +} + +int QAccessibleTextWidget::cursorPosition() const +{ + return textCursor().position(); +} + +void QAccessibleTextWidget::selection(int selectionIndex, int *startOffset, int *endOffset) const +{ + *startOffset = *endOffset = 0; + QTextCursor cursor = textCursor(); + + if (selectionIndex != 0 || !cursor.hasSelection()) + return; + + *startOffset = cursor.selectionStart(); + *endOffset = cursor.selectionEnd(); +} + +QString QAccessibleTextWidget::text(int startOffset, int endOffset) const +{ + QTextCursor cursor(textCursor()); + + cursor.setPosition(startOffset, QTextCursor::MoveAnchor); + cursor.setPosition(endOffset, QTextCursor::KeepAnchor); + + return cursor.selectedText().replace(QChar(QChar::ParagraphSeparator), QLatin1Char('\n')); +} + +QPoint QAccessibleTextWidget::scrollBarPosition() const +{ + return QPoint(0, 0); +} + + +QString QAccessibleTextWidget::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const +{ + Q_ASSERT(startOffset); + Q_ASSERT(endOffset); + + QTextCursor cursor = textCursor(); + cursor.setPosition(offset); + QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType); + cursor.setPosition(boundaries.first - 1); + boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType); + + *startOffset = boundaries.first; + *endOffset = boundaries.second; + + return text(boundaries.first, boundaries.second); + } + + +QString QAccessibleTextWidget::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const +{ + Q_ASSERT(startOffset); + Q_ASSERT(endOffset); + + QTextCursor cursor = textCursor(); + cursor.setPosition(offset); + QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType); + cursor.setPosition(boundaries.second); + boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType); + + *startOffset = boundaries.first; + *endOffset = boundaries.second; + + return text(boundaries.first, boundaries.second); +} + +QString QAccessibleTextWidget::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const +{ + Q_ASSERT(startOffset); + Q_ASSERT(endOffset); + + QTextCursor cursor = textCursor(); + cursor.setPosition(offset); + QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType); + + *startOffset = boundaries.first; + *endOffset = boundaries.second; + + return text(boundaries.first, boundaries.second); +} + +void QAccessibleTextWidget::setCursorPosition(int position) +{ + QTextCursor cursor = textCursor(); + cursor.setPosition(position); + setTextCursor(cursor); +} + +void QAccessibleTextWidget::addSelection(int startOffset, int endOffset) +{ + setSelection(0, startOffset, endOffset); +} + +void QAccessibleTextWidget::removeSelection(int selectionIndex) +{ + if (selectionIndex != 0) + return; + + QTextCursor cursor = textCursor(); + cursor.clearSelection(); + setTextCursor(cursor); +} + +void QAccessibleTextWidget::setSelection(int selectionIndex, int startOffset, int endOffset) +{ + if (selectionIndex != 0) + return; + + QTextCursor cursor = textCursor(); + cursor.setPosition(startOffset, QTextCursor::MoveAnchor); + cursor.setPosition(endOffset, QTextCursor::KeepAnchor); + setTextCursor(cursor); +} + +int QAccessibleTextWidget::characterCount() const +{ + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::End); + return cursor.position(); +} + +QTextCursor QAccessibleTextWidget::textCursorForRange(int startOffset, int endOffset) const +{ + QTextCursor cursor = textCursor(); + cursor.setPosition(startOffset, QTextCursor::MoveAnchor); + cursor.setPosition(endOffset, QTextCursor::KeepAnchor); + + return cursor; +} + +void QAccessibleTextWidget::deleteText(int startOffset, int endOffset) +{ + QTextCursor cursor = textCursorForRange(startOffset, endOffset); + cursor.removeSelectedText(); +} + +void QAccessibleTextWidget::insertText(int offset, const QString &text) +{ + QTextCursor cursor = textCursor(); + cursor.setPosition(offset); + cursor.insertText(text); +} + +void QAccessibleTextWidget::replaceText(int startOffset, int endOffset, const QString &text) +{ + QTextCursor cursor = textCursorForRange(startOffset, endOffset); + cursor.removeSelectedText(); + cursor.insertText(text); +} +#endif // QT_NO_CURSOR + + +#ifndef QT_NO_MAINWINDOW +QAccessibleMainWindow::QAccessibleMainWindow(QWidget *widget) + : QAccessibleWidget(widget, QAccessible::Window) { } + +QAccessibleInterface *QAccessibleMainWindow::child(int index) const +{ + QList<QWidget*> kids = childWidgets(mainWindow()); + if (index >= 0 && index < kids.count()) { + return QAccessible::queryAccessibleInterface(kids.at(index)); + } + return 0; +} + +int QAccessibleMainWindow::childCount() const +{ + QList<QWidget*> kids = childWidgets(mainWindow()); + return kids.count(); +} + +int QAccessibleMainWindow::indexOfChild(const QAccessibleInterface *iface) const +{ + QList<QWidget*> kids = childWidgets(mainWindow()); + return kids.indexOf(static_cast<QWidget*>(iface->object())); +} + +QAccessibleInterface *QAccessibleMainWindow::childAt(int x, int y) const +{ + QWidget *w = widget(); + if (!w->isVisible()) + return 0; + QPoint gp = w->mapToGlobal(QPoint(0, 0)); + if (!QRect(gp.x(), gp.y(), w->width(), w->height()).contains(x, y)) + return 0; + + QWidgetList kids = childWidgets(mainWindow()); + QPoint rp = mainWindow()->mapFromGlobal(QPoint(x, y)); + for (int i = 0; i < kids.size(); ++i) { + QWidget *child = kids.at(i); + if (!child->isWindow() && !child->isHidden() && child->geometry().contains(rp)) { + return QAccessible::queryAccessibleInterface(child); + } + } + return 0; +} + +QMainWindow *QAccessibleMainWindow::mainWindow() const +{ + return qobject_cast<QMainWindow *>(object()); +} + +#endif //QT_NO_MAINWINDOW + +QT_END_NAMESPACE + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/widgets/accessible/qaccessiblewidgets.h b/src/widgets/accessible/qaccessiblewidgets.h new file mode 100644 index 0000000000..27764cb0ac --- /dev/null +++ b/src/widgets/accessible/qaccessiblewidgets.h @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QACCESSIBLEWIDGETS_H +#define QACCESSIBLEWIDGETS_H + +#include <QtWidgets/qaccessiblewidget.h> + +#ifndef QT_NO_ACCESSIBILITY + +#include <QtCore/QPointer> +#include <QtCore/QPair> + +QT_BEGIN_NAMESPACE + +class QTextEdit; +class QStackedWidget; +class QToolBox; +class QMdiArea; +class QMdiSubWindow; +class QRubberBand; +class QTextBrowser; +class QCalendarWidget; +class QAbstractItemView; +class QDockWidget; +class QDockWidgetLayout; +class QMainWindow; +class QPlainTextEdit; +class QTextCursor; +class QTextDocument; + +#ifndef QT_NO_CURSOR +class QAccessibleTextWidget : public QAccessibleWidget, + public QAccessibleTextInterface, + public QAccessibleEditableTextInterface +{ +public: + QAccessibleTextWidget(QWidget *o, QAccessible::Role r = QAccessible::EditableText, const QString &name = QString()); + + QAccessible::State state() const; + + // QAccessibleTextInterface + // selection + void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + int selectionCount() const Q_DECL_OVERRIDE; + void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE; + void removeSelection(int selectionIndex) Q_DECL_OVERRIDE; + void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE; + + // cursor + int cursorPosition() const Q_DECL_OVERRIDE; + void setCursorPosition(int position) Q_DECL_OVERRIDE; + + // text + QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE; + QString textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + int characterCount() const; + + // character <-> geometry + QRect characterRect(int offset) const Q_DECL_OVERRIDE; + int offsetAtPoint(const QPoint &point) const Q_DECL_OVERRIDE; + + QString attributes(int offset, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + + // QAccessibleEditableTextInterface + void deleteText(int startOffset, int endOffset) Q_DECL_OVERRIDE; + void insertText(int offset, const QString &text) Q_DECL_OVERRIDE; + void replaceText(int startOffset, int endOffset, const QString &text) Q_DECL_OVERRIDE; + + using QAccessibleWidget::text; + +protected: + QTextCursor textCursorForRange(int startOffset, int endOffset) const; + virtual QPoint scrollBarPosition() const; + // return the current text cursor at the caret position including a potential selection + virtual QTextCursor textCursor() const = 0; + virtual void setTextCursor(const QTextCursor &) = 0; + virtual QTextDocument *textDocument() const = 0; + virtual QWidget *viewport() const = 0; +}; + +#ifndef QT_NO_TEXTEDIT +class QAccessiblePlainTextEdit : public QAccessibleTextWidget +{ +public: + explicit QAccessiblePlainTextEdit(QWidget *o); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleTextInterface + void scrollToSubstring(int startIndex, int endIndex) Q_DECL_OVERRIDE; + + using QAccessibleTextWidget::text; + +protected: + QPlainTextEdit *plainTextEdit() const; + + QPoint scrollBarPosition() const Q_DECL_OVERRIDE; + QTextCursor textCursor() const Q_DECL_OVERRIDE; + void setTextCursor(const QTextCursor &textCursor) Q_DECL_OVERRIDE; + QTextDocument *textDocument() const Q_DECL_OVERRIDE; + QWidget *viewport() const Q_DECL_OVERRIDE; +}; + +class QAccessibleTextEdit : public QAccessibleTextWidget +{ +public: + explicit QAccessibleTextEdit(QWidget *o); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleTextInterface + void scrollToSubstring(int startIndex, int endIndex) Q_DECL_OVERRIDE; + + using QAccessibleTextWidget::text; + +protected: + QTextEdit *textEdit() const; + + QPoint scrollBarPosition() const Q_DECL_OVERRIDE; + QTextCursor textCursor() const Q_DECL_OVERRIDE; + void setTextCursor(const QTextCursor &textCursor) Q_DECL_OVERRIDE; + QTextDocument *textDocument() const Q_DECL_OVERRIDE; + QWidget *viewport() const Q_DECL_OVERRIDE; +}; +#endif // QT_NO_TEXTEDIT +#endif //QT_NO_CURSOR + +class QAccessibleStackedWidget : public QAccessibleWidget +{ +public: + explicit QAccessibleStackedWidget(QWidget *widget); + + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + +protected: + QStackedWidget *stackedWidget() const; +}; + +class QAccessibleToolBox : public QAccessibleWidget +{ +public: + explicit QAccessibleToolBox(QWidget *widget); + +// FIXME we currently expose the toolbox but it is not keyboard navigatable +// and the accessible hierarchy is not exactly beautiful. +// int childCount() const; +// QAccessibleInterface *child(int index) const; +// int indexOfChild(const QAccessibleInterface *child) const; + +protected: + QToolBox *toolBox() const; +}; + +#ifndef QT_NO_MDIAREA +class QAccessibleMdiArea : public QAccessibleWidget +{ +public: + explicit QAccessibleMdiArea(QWidget *widget); + + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + +protected: + QMdiArea *mdiArea() const; +}; + +class QAccessibleMdiSubWindow : public QAccessibleWidget +{ +public: + explicit QAccessibleMdiSubWindow(QWidget *widget); + + QString text(QAccessible::Text textType) const Q_DECL_OVERRIDE; + void setText(QAccessible::Text textType, const QString &text) Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + QRect rect() const Q_DECL_OVERRIDE; + +protected: + QMdiSubWindow *mdiSubWindow() const; +}; +#endif // QT_NO_MDIAREA + +class QAccessibleDialogButtonBox : public QAccessibleWidget +{ +public: + explicit QAccessibleDialogButtonBox(QWidget *widget); +}; + +#if !defined(QT_NO_TEXTBROWSER) && !defined(QT_NO_CURSOR) +class QAccessibleTextBrowser : public QAccessibleTextEdit +{ +public: + explicit QAccessibleTextBrowser(QWidget *widget); + + QAccessible::Role role() const Q_DECL_OVERRIDE; +}; +#endif // QT_NO_TEXTBROWSER && QT_NO_CURSOR + +#ifndef QT_NO_CALENDARWIDGET +class QAccessibleCalendarWidget : public QAccessibleWidget +{ +public: + explicit QAccessibleCalendarWidget(QWidget *widget); + + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + +protected: + QCalendarWidget *calendarWidget() const; + +private: + QAbstractItemView *calendarView() const; + QWidget *navigationBar() const; +}; +#endif // QT_NO_CALENDARWIDGET + +#ifndef QT_NO_DOCKWIDGET +class QAccessibleDockWidget: public QAccessibleWidget +{ +public: + explicit QAccessibleDockWidget(QWidget *widget); + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + QRect rect () const Q_DECL_OVERRIDE; + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + + QDockWidget *dockWidget() const; +protected: + QDockWidgetLayout *dockWidgetLayout() const; +}; + +#endif // QT_NO_DOCKWIDGET + +#ifndef QT_NO_MAINWINDOW +class QAccessibleMainWindow : public QAccessibleWidget +{ +public: + explicit QAccessibleMainWindow(QWidget *widget); + + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *iface) const Q_DECL_OVERRIDE; + QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE; + QMainWindow *mainWindow() const; + +}; +#endif //QT_NO_MAINWINDOW + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +#endif // QACESSIBLEWIDGETS_H diff --git a/src/widgets/accessible/rangecontrols.cpp b/src/widgets/accessible/rangecontrols.cpp new file mode 100644 index 0000000000..d4dc74ea69 --- /dev/null +++ b/src/widgets/accessible/rangecontrols.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rangecontrols.h" + +#include <qslider.h> +#include <qdial.h> +#include <qspinbox.h> +#include <qscrollbar.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qdebug.h> +#include <qglobal.h> +#include <QDoubleSpinBox> +#include <QDial> +#include <qmath.h> +#include <private/qmath_p.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +#ifndef QT_NO_SPINBOX +QAccessibleAbstractSpinBox::QAccessibleAbstractSpinBox(QWidget *w) +: QAccessibleWidget(w, QAccessible::SpinBox) +{ + Q_ASSERT(abstractSpinBox()); +} + +/*! + Returns the underlying QAbstractSpinBox. +*/ +QAbstractSpinBox *QAccessibleAbstractSpinBox::abstractSpinBox() const +{ + return qobject_cast<QAbstractSpinBox*>(object()); +} + +QString QAccessibleAbstractSpinBox::text(QAccessible::Text t) const +{ + if (t == QAccessible::Value) + return abstractSpinBox()->text(); + return QAccessibleWidget::text(t); +} + +void *QAccessibleAbstractSpinBox::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ValueInterface) + return static_cast<QAccessibleValueInterface*>(this); + return QAccessibleWidget::interface_cast(t); +} + +QVariant QAccessibleAbstractSpinBox::currentValue() const +{ + return abstractSpinBox()->property("value"); +} + +void QAccessibleAbstractSpinBox::setCurrentValue(const QVariant &value) +{ + abstractSpinBox()->setProperty("value", value); +} + +QVariant QAccessibleAbstractSpinBox::maximumValue() const +{ + return abstractSpinBox()->property("maximum"); +} + +QVariant QAccessibleAbstractSpinBox::minimumValue() const +{ + return abstractSpinBox()->property("minimum"); +} + +QVariant QAccessibleAbstractSpinBox::minimumStepSize() const +{ + return abstractSpinBox()->property("stepSize"); +} + +/*! + \class QAccessibleSpinBox + \brief The QAccessibleSpinBox class implements the QAccessibleInterface for spinbox widgets. + \internal + + \ingroup accessibility +*/ + +/*! + Constructs a QAccessibleSpinWidget object for \a w. +*/ +QAccessibleSpinBox::QAccessibleSpinBox(QWidget *w) +: QAccessibleAbstractSpinBox(w) +{ + Q_ASSERT(spinBox()); + addControllingSignal(QLatin1String("valueChanged(int)")); + addControllingSignal(QLatin1String("valueChanged(QString)")); +} + +/*! + Returns the underlying QSpinBox. +*/ +QSpinBox *QAccessibleSpinBox::spinBox() const +{ + return qobject_cast<QSpinBox*>(object()); +} + + +// ================================== QAccessibleDoubleSpinBox ================================== +QAccessibleDoubleSpinBox::QAccessibleDoubleSpinBox(QWidget *widget) + : QAccessibleAbstractSpinBox(widget) +{ + Q_ASSERT(qobject_cast<QDoubleSpinBox *>(widget)); + addControllingSignal(QLatin1String("valueChanged(double)")); + addControllingSignal(QLatin1String("valueChanged(QString)")); +} + +/*! + Returns the underlying QDoubleSpinBox. +*/ +QDoubleSpinBox *QAccessibleDoubleSpinBox::doubleSpinBox() const +{ + return static_cast<QDoubleSpinBox*>(object()); +} + +QString QAccessibleDoubleSpinBox::text(QAccessible::Text textType) const +{ + if (textType == QAccessible::Value) + return doubleSpinBox()->textFromValue(doubleSpinBox()->value()); + return QAccessibleWidget::text(textType); +} + +#endif // QT_NO_SPINBOX + +#ifndef QT_NO_SCROLLBAR +/*! + \class QAccessibleScrollBar + \brief The QAccessibleScrollBar class implements the QAccessibleInterface for scroll bars. + \internal + + \ingroup accessibility +*/ + +/*! + Constructs a QAccessibleScrollBar object for \a w. + \a name is propagated to the QAccessibleWidget constructor. +*/ +QAccessibleScrollBar::QAccessibleScrollBar(QWidget *w) +: QAccessibleAbstractSlider(w, QAccessible::ScrollBar) +{ + Q_ASSERT(scrollBar()); + addControllingSignal(QLatin1String("valueChanged(int)")); +} + +/*! Returns the scroll bar. */ +QScrollBar *QAccessibleScrollBar::scrollBar() const +{ + return qobject_cast<QScrollBar*>(object()); +} + +QString QAccessibleScrollBar::text(QAccessible::Text t) const +{ + if (t == QAccessible::Value) + return QString::number(scrollBar()->value()); + return QAccessibleAbstractSlider::text(t); +} + +#endif // QT_NO_SCROLLBAR + +#ifndef QT_NO_SLIDER +/*! + \class QAccessibleSlider + \brief The QAccessibleSlider class implements the QAccessibleInterface for sliders. + \internal + + \ingroup accessibility +*/ + +/*! + Constructs a QAccessibleScrollBar object for \a w. + \a name is propagated to the QAccessibleWidget constructor. +*/ +QAccessibleSlider::QAccessibleSlider(QWidget *w) +: QAccessibleAbstractSlider(w) +{ + Q_ASSERT(slider()); + addControllingSignal(QLatin1String("valueChanged(int)")); +} + +/*! Returns the slider. */ +QSlider *QAccessibleSlider::slider() const +{ + return qobject_cast<QSlider*>(object()); +} + +QString QAccessibleSlider::text(QAccessible::Text t) const +{ + if (t == QAccessible::Value) + return QString::number(slider()->value()); + + return QAccessibleAbstractSlider::text(t); +} + +QAccessibleAbstractSlider::QAccessibleAbstractSlider(QWidget *w, QAccessible::Role r) + : QAccessibleWidget(w, r) +{ + Q_ASSERT(qobject_cast<QAbstractSlider *>(w)); +} + +void *QAccessibleAbstractSlider::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ValueInterface) + return static_cast<QAccessibleValueInterface*>(this); + return QAccessibleWidget::interface_cast(t); +} + +QVariant QAccessibleAbstractSlider::currentValue() const +{ + return abstractSlider()->value(); +} + +void QAccessibleAbstractSlider::setCurrentValue(const QVariant &value) +{ + abstractSlider()->setValue(value.toInt()); +} + +QVariant QAccessibleAbstractSlider::maximumValue() const +{ + return abstractSlider()->maximum(); +} + +QVariant QAccessibleAbstractSlider::minimumValue() const +{ + return abstractSlider()->minimum(); +} + +QVariant QAccessibleAbstractSlider::minimumStepSize() const +{ + return abstractSlider()->singleStep(); +} + +QAbstractSlider *QAccessibleAbstractSlider::abstractSlider() const +{ + return static_cast<QAbstractSlider *>(object()); +} + +#endif // QT_NO_SLIDER + +#ifndef QT_NO_DIAL +// ======================================= QAccessibleDial ====================================== +QAccessibleDial::QAccessibleDial(QWidget *widget) + : QAccessibleAbstractSlider(widget, QAccessible::Dial) +{ + Q_ASSERT(qobject_cast<QDial *>(widget)); + addControllingSignal(QLatin1String("valueChanged(int)")); +} + +QString QAccessibleDial::text(QAccessible::Text textType) const +{ + if (textType == QAccessible::Value) + return QString::number(dial()->value()); + + return QAccessibleAbstractSlider::text(textType); +} + +QDial *QAccessibleDial::dial() const +{ + return static_cast<QDial*>(object()); +} +#endif // QT_NO_DIAL + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE diff --git a/src/widgets/accessible/rangecontrols.h b/src/widgets/accessible/rangecontrols.h new file mode 100644 index 0000000000..98cef46c5c --- /dev/null +++ b/src/widgets/accessible/rangecontrols.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RANGECONTROLS_H +#define RANGECONTROLS_H + +#include <QtWidgets/qaccessiblewidget.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +class QAbstractSpinBox; +class QAbstractSlider; +class QScrollBar; +class QSlider; +class QSpinBox; +class QDoubleSpinBox; +class QDial; + +#ifndef QT_NO_SPINBOX +class QAccessibleAbstractSpinBox: public QAccessibleWidget, public QAccessibleValueInterface // TODO, public QAccessibleActionInterface +{ +public: + explicit QAccessibleAbstractSpinBox(QWidget *w); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleValueInterface + QVariant currentValue() const Q_DECL_OVERRIDE; + void setCurrentValue(const QVariant &value) Q_DECL_OVERRIDE; + QVariant maximumValue() const Q_DECL_OVERRIDE; + QVariant minimumValue() const Q_DECL_OVERRIDE; + QVariant minimumStepSize() const Q_DECL_OVERRIDE; + + // FIXME Action interface + +protected: + QAbstractSpinBox *abstractSpinBox() const; +}; + +class QAccessibleSpinBox : public QAccessibleAbstractSpinBox +{ +public: + explicit QAccessibleSpinBox(QWidget *w); + +protected: + QSpinBox *spinBox() const; +}; + +class QAccessibleDoubleSpinBox : public QAccessibleAbstractSpinBox +{ +public: + explicit QAccessibleDoubleSpinBox(QWidget *widget); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + +protected: + QDoubleSpinBox *doubleSpinBox() const; +}; +#endif // QT_NO_SPINBOX + +class QAccessibleAbstractSlider: public QAccessibleWidget, public QAccessibleValueInterface +{ +public: + explicit QAccessibleAbstractSlider(QWidget *w, QAccessible::Role r = QAccessible::Slider); + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleValueInterface + QVariant currentValue() const Q_DECL_OVERRIDE; + void setCurrentValue(const QVariant &value) Q_DECL_OVERRIDE; + QVariant maximumValue() const Q_DECL_OVERRIDE; + QVariant minimumValue() const Q_DECL_OVERRIDE; + QVariant minimumStepSize() const Q_DECL_OVERRIDE; + +protected: + QAbstractSlider *abstractSlider() const; +}; + +#ifndef QT_NO_SCROLLBAR +class QAccessibleScrollBar : public QAccessibleAbstractSlider +{ +public: + explicit QAccessibleScrollBar(QWidget *w); + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + +protected: + QScrollBar *scrollBar() const; +}; +#endif // QT_NO_SCROLLBAR + +#ifndef QT_NO_SLIDER +class QAccessibleSlider : public QAccessibleAbstractSlider +{ +public: + explicit QAccessibleSlider(QWidget *w); + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + +protected: + QSlider *slider() const; +}; +#endif // QT_NO_SLIDER + +#ifndef QT_NO_DIAL +class QAccessibleDial : public QAccessibleAbstractSlider +{ +public: + explicit QAccessibleDial(QWidget *w); + + QString text(QAccessible::Text textType) const Q_DECL_OVERRIDE; + +protected: + QDial *dial() const; +}; +#endif // QT_NO_DIAL + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +#endif // RANGECONTROLS_H diff --git a/src/widgets/accessible/simplewidgets.cpp b/src/widgets/accessible/simplewidgets.cpp new file mode 100644 index 0000000000..88d3981391 --- /dev/null +++ b/src/widgets/accessible/simplewidgets.cpp @@ -0,0 +1,904 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "simplewidgets.h" + +#include <qabstractbutton.h> +#include <qcheckbox.h> +#include <qpushbutton.h> +#include <qprogressbar.h> +#include <qstatusbar.h> +#include <qradiobutton.h> +#include <qtoolbutton.h> +#include <qmenu.h> +#include <qlabel.h> +#include <qgroupbox.h> +#include <qlcdnumber.h> +#include <qlineedit.h> +#include <private/qlineedit_p.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qtextdocument.h> +#include <qwindow.h> +#include <private/qwindowcontainer_p.h> +#include <QtCore/qvarlengtharray.h> + +#ifdef Q_OS_MAC +#include <qfocusframe.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +extern QList<QWidget*> childWidgets(const QWidget *widget); + +QString qt_accStripAmp(const QString &text); +QString qt_accHotKey(const QString &text); + +/*! + \class QAccessibleButton + \brief The QAccessibleButton class implements the QAccessibleInterface for button type widgets. + \internal + + \ingroup accessibility +*/ + +/*! + Creates a QAccessibleButton object for \a w. + \a role is propagated to the QAccessibleWidget constructor. +*/ +QAccessibleButton::QAccessibleButton(QWidget *w, QAccessible::Role role) +: QAccessibleWidget(w, role) +{ + Q_ASSERT(button()); + if (button()->isCheckable()) + addControllingSignal(QLatin1String("toggled(bool)")); + else + addControllingSignal(QLatin1String("clicked()")); +} + +/*! Returns the button. */ +QAbstractButton *QAccessibleButton::button() const +{ + return qobject_cast<QAbstractButton*>(object()); +} + +/*! \reimp */ +QString QAccessibleButton::text(QAccessible::Text t) const +{ + QString str; + switch (t) { + case QAccessible::Accelerator: + { +#ifndef QT_NO_SHORTCUT + QPushButton *pb = qobject_cast<QPushButton*>(object()); + if (pb && pb->isDefault()) + str = QKeySequence(Qt::Key_Enter).toString(QKeySequence::NativeText); +#endif + if (str.isEmpty()) + str = qt_accHotKey(button()->text()); + } + break; + case QAccessible::Name: + str = widget()->accessibleName(); + if (str.isEmpty()) + str = button()->text(); + break; + default: + break; + } + if (str.isEmpty()) + str = QAccessibleWidget::text(t); + return qt_accStripAmp(str); +} + +QAccessible::State QAccessibleButton::state() const +{ + QAccessible::State state = QAccessibleWidget::state(); + + QAbstractButton *b = button(); + QCheckBox *cb = qobject_cast<QCheckBox *>(b); + if (b->isCheckable()) + state.checkable = true; + if (b->isChecked()) + state.checked = true; + else if (cb && cb->checkState() == Qt::PartiallyChecked) + state.checkStateMixed = true; + if (b->isDown()) + state.pressed = true; + QPushButton *pb = qobject_cast<QPushButton*>(b); + if (pb) { + if (pb->isDefault()) + state.defaultButton = true; +#ifndef QT_NO_MENU + if (pb->menu()) + state.hasPopup = true; +#endif + } + + return state; +} + +QRect QAccessibleButton::rect() const +{ + QAbstractButton *ab = button(); + if (!ab->isVisible()) + return QRect(); + + if (QCheckBox *cb = qobject_cast<QCheckBox *>(ab)) { + QPoint wpos = cb->mapToGlobal(QPoint(0, 0)); + QStyleOptionButton opt; + cb->initStyleOption(&opt); + return cb->style()->subElementRect(QStyle::SE_CheckBoxClickRect, &opt, cb).translated(wpos); + } else if (QRadioButton *rb = qobject_cast<QRadioButton *>(ab)) { + QPoint wpos = rb->mapToGlobal(QPoint(0, 0)); + QStyleOptionButton opt; + rb->initStyleOption(&opt); + return rb->style()->subElementRect(QStyle::SE_RadioButtonClickRect, &opt, rb).translated(wpos); + } + return QAccessibleWidget::rect(); +} + +QStringList QAccessibleButton::actionNames() const +{ + QStringList names; + if (widget()->isEnabled()) { + switch (role()) { + case QAccessible::ButtonMenu: + names << showMenuAction(); + break; + case QAccessible::RadioButton: + names << toggleAction(); + break; + default: + if (button()->isCheckable()) { + names << toggleAction(); + } else { + names << pressAction(); + } + break; + } + } + names << QAccessibleWidget::actionNames(); + return names; +} + +void QAccessibleButton::doAction(const QString &actionName) +{ + if (!widget()->isEnabled()) + return; + if (actionName == pressAction() || + actionName == showMenuAction()) { +#ifndef QT_NO_MENU + QPushButton *pb = qobject_cast<QPushButton*>(object()); + if (pb && pb->menu()) + pb->showMenu(); + else +#endif + button()->animateClick(); + } else if (actionName == toggleAction()) { + button()->toggle(); + } else { + QAccessibleWidget::doAction(actionName); + } +} + +QStringList QAccessibleButton::keyBindingsForAction(const QString &actionName) const +{ + if (actionName == pressAction()) { +#ifndef QT_NO_SHORTCUT + return QStringList() << button()->shortcut().toString(); +#endif + } + return QStringList(); +} + + +#ifndef QT_NO_TOOLBUTTON +/*! + \class QAccessibleToolButton + \brief The QAccessibleToolButton class implements the QAccessibleInterface for tool buttons. + \internal + + \ingroup accessibility +*/ + +/*! + Creates a QAccessibleToolButton object for \a w. + \a role is propagated to the QAccessibleWidget constructor. +*/ +QAccessibleToolButton::QAccessibleToolButton(QWidget *w, QAccessible::Role role) +: QAccessibleButton(w, role) +{ + Q_ASSERT(toolButton()); +} + +/*! Returns the button. */ +QToolButton *QAccessibleToolButton::toolButton() const +{ + return qobject_cast<QToolButton*>(object()); +} + +/*! + Returns \c true if this tool button is a split button. +*/ +bool QAccessibleToolButton::isSplitButton() const +{ +#ifndef QT_NO_MENU + return toolButton()->menu() && toolButton()->popupMode() == QToolButton::MenuButtonPopup; +#else + return false; +#endif +} + +QAccessible::State QAccessibleToolButton::state() const +{ + QAccessible::State st = QAccessibleButton::state(); + if (toolButton()->autoRaise()) + st.hotTracked = true; +#ifndef QT_NO_MENU + if (toolButton()->menu()) + st.hasPopup = true; +#endif + return st; +} + +int QAccessibleToolButton::childCount() const +{ + return isSplitButton() ? 1 : 0; +} + +QAccessibleInterface *QAccessibleToolButton::child(int index) const +{ +#ifndef QT_NO_MENU + if (index == 0 && toolButton()->menu()) + { + return QAccessible::queryAccessibleInterface(toolButton()->menu()); + } +#endif + return 0; +} + +/*! + \internal + + Returns the button's text label, depending on the text \a t, and + the \a child. +*/ +QString QAccessibleToolButton::text(QAccessible::Text t) const +{ + QString str; + switch (t) { + case QAccessible::Name: + str = toolButton()->accessibleName(); + if (str.isEmpty()) + str = toolButton()->text(); + break; + default: + break; + } + if (str.isEmpty()) + str = QAccessibleButton::text(t); + return qt_accStripAmp(str); +} + +/* + The three different tool button types can have the following actions: +| DelayedPopup | ShowMenuAction + (PressedAction || CheckedAction) | +| MenuButtonPopup | ShowMenuAction + (PressedAction || CheckedAction) | +| InstantPopup | ShowMenuAction | +*/ +QStringList QAccessibleToolButton::actionNames() const +{ + QStringList names; + if (widget()->isEnabled()) { + if (toolButton()->menu()) + names << showMenuAction(); + if (toolButton()->popupMode() != QToolButton::InstantPopup) + names << QAccessibleButton::actionNames(); + } + return names; +} + +void QAccessibleToolButton::doAction(const QString &actionName) +{ + if (!widget()->isEnabled()) + return; + + if (actionName == pressAction()) { + button()->click(); + } else if (actionName == showMenuAction()) { + if (toolButton()->popupMode() != QToolButton::InstantPopup) { + toolButton()->setDown(true); +#ifndef QT_NO_MENU + toolButton()->showMenu(); +#endif + } + } else { + QAccessibleButton::doAction(actionName); + } + +} + +#endif // QT_NO_TOOLBUTTON + +/*! + \class QAccessibleDisplay + \brief The QAccessibleDisplay class implements the QAccessibleInterface for widgets that display information. + \internal + + \ingroup accessibility +*/ + +/*! + Constructs a QAccessibleDisplay object for \a w. + \a role is propagated to the QAccessibleWidget constructor. +*/ +QAccessibleDisplay::QAccessibleDisplay(QWidget *w, QAccessible::Role role) +: QAccessibleWidget(w, role) +{ +} + +QAccessible::Role QAccessibleDisplay::role() const +{ + QLabel *l = qobject_cast<QLabel*>(object()); + if (l) { + if (l->pixmap()) + return QAccessible::Graphic; +#ifndef QT_NO_PICTURE + if (l->picture()) + return QAccessible::Graphic; +#endif +#ifndef QT_NO_MOVIE + if (l->movie()) + return QAccessible::Animation; +#endif +#ifndef QT_NO_PROGRESSBAR + } else if (qobject_cast<QProgressBar*>(object())) { + return QAccessible::ProgressBar; +#endif + } else if (qobject_cast<QStatusBar*>(object())) { + return QAccessible::StatusBar; + } + return QAccessibleWidget::role(); +} + +QString QAccessibleDisplay::text(QAccessible::Text t) const +{ + QString str; + switch (t) { + case QAccessible::Name: + str = widget()->accessibleName(); + if (str.isEmpty()) { + if (qobject_cast<QLabel*>(object())) { + QLabel *label = qobject_cast<QLabel*>(object()); + str = label->text(); + if (label->textFormat() == Qt::RichText + || (label->textFormat() == Qt::AutoText && Qt::mightBeRichText(str))) { + QTextDocument doc; + doc.setHtml(str); + str = doc.toPlainText(); + } +#ifndef QT_NO_LCDNUMBER + } else if (qobject_cast<QLCDNumber*>(object())) { + QLCDNumber *l = qobject_cast<QLCDNumber*>(object()); + if (l->digitCount()) + str = QString::number(l->value()); + else + str = QString::number(l->intValue()); +#endif + } else if (qobject_cast<QStatusBar*>(object())) { + return qobject_cast<QStatusBar*>(object())->currentMessage(); + } + } + break; + case QAccessible::Value: +#ifndef QT_NO_PROGRESSBAR + if (qobject_cast<QProgressBar*>(object())) + str = QString::number(qobject_cast<QProgressBar*>(object())->value()); +#endif + break; + default: + break; + } + if (str.isEmpty()) + str = QAccessibleWidget::text(t); + return qt_accStripAmp(str); +} + +/*! \reimp */ +QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > +QAccessibleDisplay::relations(QAccessible::Relation match /* = QAccessible::AllRelations */) const +{ + QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > rels = QAccessibleWidget::relations(match); + if (match & QAccessible::Labelled) { + QVarLengthArray<QObject *, 4> relatedObjects; + +#ifndef QT_NO_SHORTCUT + if (QLabel *label = qobject_cast<QLabel*>(object())) { + relatedObjects.append(label->buddy()); + } +#endif + for (int i = 0; i < relatedObjects.count(); ++i) { + const QAccessible::Relation rel = QAccessible::Labelled; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(relatedObjects.at(i)); + if (iface) + rels.append(qMakePair(iface, rel)); + } + } + return rels; +} + +void *QAccessibleDisplay::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ImageInterface) + return static_cast<QAccessibleImageInterface*>(this); + return QAccessibleWidget::interface_cast(t); +} + +/*! \internal */ +QString QAccessibleDisplay::imageDescription() const +{ +#ifndef QT_NO_TOOLTIP + return widget()->toolTip(); +#else + return QString::null; +#endif +} + +/*! \internal */ +QSize QAccessibleDisplay::imageSize() const +{ + QLabel *label = qobject_cast<QLabel *>(widget()); + if (!label) + return QSize(); + const QPixmap *pixmap = label->pixmap(); + if (!pixmap) + return QSize(); + return pixmap->size(); +} + +/*! \internal */ +QPoint QAccessibleDisplay::imagePosition() const +{ + QLabel *label = qobject_cast<QLabel *>(widget()); + if (!label) + return QPoint(); + const QPixmap *pixmap = label->pixmap(); + if (!pixmap) + return QPoint(); + + return QPoint(label->mapToGlobal(label->pos())); +} + +#ifndef QT_NO_GROUPBOX +QAccessibleGroupBox::QAccessibleGroupBox(QWidget *w) +: QAccessibleWidget(w) +{ +} + +QGroupBox* QAccessibleGroupBox::groupBox() const +{ + return static_cast<QGroupBox *>(widget()); +} + +QString QAccessibleGroupBox::text(QAccessible::Text t) const +{ + QString txt = QAccessibleWidget::text(t); + + if (txt.isEmpty()) { + switch (t) { + case QAccessible::Name: + txt = qt_accStripAmp(groupBox()->title()); + break; + case QAccessible::Description: + txt = qt_accStripAmp(groupBox()->toolTip()); + break; + default: + break; + } + } + + return txt; +} + +QAccessible::State QAccessibleGroupBox::state() const +{ + QAccessible::State st = QAccessibleWidget::state(); + st.checkable = groupBox()->isCheckable(); + st.checked = groupBox()->isChecked(); + return st; +} + +QAccessible::Role QAccessibleGroupBox::role() const +{ + return groupBox()->isCheckable() ? QAccessible::CheckBox : QAccessible::Grouping; +} + +QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > +QAccessibleGroupBox::relations(QAccessible::Relation match /* = QAccessible::AllRelations */) const +{ + QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > rels = QAccessibleWidget::relations(match); + + if ((match & QAccessible::Labelled) && (!groupBox()->title().isEmpty())) { + const QList<QWidget*> kids = childWidgets(widget()); + for (int i = 0; i < kids.count(); ++i) { + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(kids.at(i)); + if (iface) + rels.append(qMakePair(iface, QAccessible::Relation(QAccessible::Labelled))); + } + } + return rels; +} + +QStringList QAccessibleGroupBox::actionNames() const +{ + QStringList actions = QAccessibleWidget::actionNames(); + + if (groupBox()->isCheckable()) { + actions.prepend(QAccessibleActionInterface::toggleAction()); + } + return actions; +} + +void QAccessibleGroupBox::doAction(const QString &actionName) +{ + if (actionName == QAccessibleActionInterface::toggleAction()) + groupBox()->setChecked(!groupBox()->isChecked()); +} + +QStringList QAccessibleGroupBox::keyBindingsForAction(const QString &) const +{ + return QStringList(); +} + +#endif + +#ifndef QT_NO_LINEEDIT +/*! + \class QAccessibleLineEdit + \brief The QAccessibleLineEdit class implements the QAccessibleInterface for widgets with editable text + \internal + + \ingroup accessibility +*/ + +/*! + Constructs a QAccessibleLineEdit object for \a w. + \a name is propagated to the QAccessibleWidget constructor. +*/ +QAccessibleLineEdit::QAccessibleLineEdit(QWidget *w, const QString &name) +: QAccessibleWidget(w, QAccessible::EditableText, name) +{ + addControllingSignal(QLatin1String("textChanged(const QString&)")); + addControllingSignal(QLatin1String("returnPressed()")); +} + +/*! Returns the line edit. */ +QLineEdit *QAccessibleLineEdit::lineEdit() const +{ + return qobject_cast<QLineEdit*>(object()); +} + +QString QAccessibleLineEdit::text(QAccessible::Text t) const +{ + QString str; + switch (t) { + case QAccessible::Value: + if (lineEdit()->echoMode() == QLineEdit::Normal) + str = lineEdit()->text(); + else if (lineEdit()->echoMode() != QLineEdit::NoEcho) + str = QString(lineEdit()->text().length(), QChar::fromLatin1('*')); + break; + default: + break; + } + if (str.isEmpty()) + str = QAccessibleWidget::text(t); + return qt_accStripAmp(str); +} + +void QAccessibleLineEdit::setText(QAccessible::Text t, const QString &text) +{ + if (t != QAccessible::Value) { + QAccessibleWidget::setText(t, text); + return; + } + + QString newText = text; + if (lineEdit()->validator()) { + int pos = 0; + if (lineEdit()->validator()->validate(newText, pos) != QValidator::Acceptable) + return; + } + lineEdit()->setText(newText); +} + +QAccessible::State QAccessibleLineEdit::state() const +{ + QAccessible::State state = QAccessibleWidget::state(); + + QLineEdit *l = lineEdit(); + if (l->isReadOnly()) + state.readOnly = true; + else + state.editable = true; + + if (l->echoMode() != QLineEdit::Normal) + state.passwordEdit = true; + + state.selectableText = true; + return state; +} + +void *QAccessibleLineEdit::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::TextInterface) + return static_cast<QAccessibleTextInterface*>(this); + if (t == QAccessible::EditableTextInterface) + return static_cast<QAccessibleEditableTextInterface*>(this); + return QAccessibleWidget::interface_cast(t); +} + +void QAccessibleLineEdit::addSelection(int startOffset, int endOffset) +{ + setSelection(0, startOffset, endOffset); +} + +QString QAccessibleLineEdit::attributes(int offset, int *startOffset, int *endOffset) const +{ + // QLineEdit doesn't have text attributes + *startOffset = *endOffset = offset; + return QString(); +} + +int QAccessibleLineEdit::cursorPosition() const +{ + return lineEdit()->cursorPosition(); +} + +QRect QAccessibleLineEdit::characterRect(int offset) const +{ + int x = lineEdit()->d_func()->control->cursorToX(offset); + int y; + lineEdit()->getTextMargins(0, &y, 0, 0); + QFontMetrics fm(lineEdit()->font()); + const QString ch = text(offset, offset + 1); + if (ch.isEmpty()) + return QRect(); + int w = fm.width(ch); + int h = fm.height(); + QRect r(x, y, w, h); + r.moveTo(lineEdit()->mapToGlobal(r.topLeft())); + return r; +} + +int QAccessibleLineEdit::selectionCount() const +{ + return lineEdit()->hasSelectedText() ? 1 : 0; +} + +int QAccessibleLineEdit::offsetAtPoint(const QPoint &point) const +{ + QPoint p = lineEdit()->mapFromGlobal(point); + + return lineEdit()->cursorPositionAt(p); +} + +void QAccessibleLineEdit::selection(int selectionIndex, int *startOffset, int *endOffset) const +{ + *startOffset = *endOffset = 0; + if (selectionIndex != 0) + return; + + *startOffset = lineEdit()->selectionStart(); + *endOffset = *startOffset + lineEdit()->selectedText().count(); +} + +QString QAccessibleLineEdit::text(int startOffset, int endOffset) const +{ + if (startOffset > endOffset) + return QString(); + + if (lineEdit()->echoMode() != QLineEdit::Normal) + return QString(); + + return lineEdit()->text().mid(startOffset, endOffset - startOffset); +} + +QString QAccessibleLineEdit::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const +{ + if (lineEdit()->echoMode() != QLineEdit::Normal) { + *startOffset = *endOffset = -1; + return QString(); + } + return QAccessibleTextInterface::textBeforeOffset(offset, boundaryType, startOffset, endOffset); +} + +QString QAccessibleLineEdit::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const +{ + if (lineEdit()->echoMode() != QLineEdit::Normal) { + *startOffset = *endOffset = -1; + return QString(); + } + return QAccessibleTextInterface::textAfterOffset(offset, boundaryType, startOffset, endOffset); +} + +QString QAccessibleLineEdit::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const +{ + if (lineEdit()->echoMode() != QLineEdit::Normal) { + *startOffset = *endOffset = -1; + return QString(); + } + return QAccessibleTextInterface::textAtOffset(offset, boundaryType, startOffset, endOffset); +} + +void QAccessibleLineEdit::removeSelection(int selectionIndex) +{ + if (selectionIndex != 0) + return; + + lineEdit()->deselect(); +} + +void QAccessibleLineEdit::setCursorPosition(int position) +{ + lineEdit()->setCursorPosition(position); +} + +void QAccessibleLineEdit::setSelection(int selectionIndex, int startOffset, int endOffset) +{ + if (selectionIndex != 0) + return; + + lineEdit()->setSelection(startOffset, endOffset - startOffset); +} + +int QAccessibleLineEdit::characterCount() const +{ + return lineEdit()->text().count(); +} + +void QAccessibleLineEdit::scrollToSubstring(int startIndex, int endIndex) +{ + lineEdit()->setCursorPosition(endIndex); + lineEdit()->setCursorPosition(startIndex); +} + +void QAccessibleLineEdit::deleteText(int startOffset, int endOffset) +{ + lineEdit()->setText(lineEdit()->text().remove(startOffset, endOffset - startOffset)); +} + +void QAccessibleLineEdit::insertText(int offset, const QString &text) +{ + lineEdit()->setText(lineEdit()->text().insert(offset, text)); +} + +void QAccessibleLineEdit::replaceText(int startOffset, int endOffset, const QString &text) +{ + lineEdit()->setText(lineEdit()->text().replace(startOffset, endOffset - startOffset, text)); +} + +#endif // QT_NO_LINEEDIT + +#ifndef QT_NO_PROGRESSBAR +QAccessibleProgressBar::QAccessibleProgressBar(QWidget *o) + : QAccessibleDisplay(o) +{ + Q_ASSERT(progressBar()); +} + +void *QAccessibleProgressBar::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ValueInterface) + return static_cast<QAccessibleValueInterface*>(this); + return QAccessibleDisplay::interface_cast(t); +} + +QVariant QAccessibleProgressBar::currentValue() const +{ + return progressBar()->value(); +} + +QVariant QAccessibleProgressBar::maximumValue() const +{ + return progressBar()->maximum(); +} + +QVariant QAccessibleProgressBar::minimumValue() const +{ + return progressBar()->minimum(); +} + +QVariant QAccessibleProgressBar::minimumStepSize() const +{ + // This is arbitrary since any value between min and max is valid. + // Some screen readers (orca use it to calculate how many digits to display though, + // so it makes sense to return a "sensible" value. Providing 100 increments seems ok. + return (progressBar()->maximum() - progressBar()->minimum()) / 100.0; +} + +QProgressBar *QAccessibleProgressBar::progressBar() const +{ + return qobject_cast<QProgressBar *>(object()); +} +#endif + + +QAccessibleWindowContainer::QAccessibleWindowContainer(QWidget *w) + : QAccessibleWidget(w) +{ +} + +int QAccessibleWindowContainer::childCount() const +{ + if (container()->containedWindow()) + return 1; + return 0; +} + +int QAccessibleWindowContainer::indexOfChild(const QAccessibleInterface *child) const +{ + if (child->object() == container()->containedWindow()) + return 0; + return -1; +} + +QAccessibleInterface *QAccessibleWindowContainer::child(int i) const +{ + if (i == 0) + return QAccessible::queryAccessibleInterface(container()->containedWindow()); + return 0; +} + +QWindowContainer *QAccessibleWindowContainer::container() const +{ + return static_cast<QWindowContainer *>(widget()); +} + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE diff --git a/src/widgets/accessible/simplewidgets.h b/src/widgets/accessible/simplewidgets.h new file mode 100644 index 0000000000..e4ce6150e2 --- /dev/null +++ b/src/widgets/accessible/simplewidgets.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMPLEWIDGETS_H +#define SIMPLEWIDGETS_H + +#include <QtCore/qcoreapplication.h> +#include <QtWidgets/qaccessiblewidget.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_ACCESSIBILITY + +class QAbstractButton; +class QLineEdit; +class QToolButton; +class QGroupBox; +class QProgressBar; + +class QAccessibleButton : public QAccessibleWidget +{ + Q_DECLARE_TR_FUNCTIONS(QAccessibleButton) +public: + QAccessibleButton(QWidget *w, QAccessible::Role r); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + QRect rect() const Q_DECL_OVERRIDE; + + QStringList actionNames() const Q_DECL_OVERRIDE; + void doAction(const QString &actionName) Q_DECL_OVERRIDE; + QStringList keyBindingsForAction(const QString &actionName) const Q_DECL_OVERRIDE; + +protected: + QAbstractButton *button() const; +}; + +#ifndef QT_NO_TOOLBUTTON +class QAccessibleToolButton : public QAccessibleButton +{ +public: + QAccessibleToolButton(QWidget *w, QAccessible::Role role); + + QAccessible::State state() const Q_DECL_OVERRIDE; + + int childCount() const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + + // QAccessibleActionInterface + QStringList actionNames() const Q_DECL_OVERRIDE; + void doAction(const QString &actionName) Q_DECL_OVERRIDE; + +protected: + QToolButton *toolButton() const; + + bool isSplitButton() const; +}; +#endif // QT_NO_TOOLBUTTON + +class QAccessibleDisplay : public QAccessibleWidget, public QAccessibleImageInterface +{ +public: + explicit QAccessibleDisplay(QWidget *w, QAccessible::Role role = QAccessible::StaticText); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + QAccessible::Role role() const Q_DECL_OVERRIDE; + + QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >relations(QAccessible::Relation match = QAccessible::AllRelations) const Q_DECL_OVERRIDE; + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleImageInterface + QString imageDescription() const Q_DECL_OVERRIDE; + QSize imageSize() const Q_DECL_OVERRIDE; + QPoint imagePosition() const Q_DECL_OVERRIDE; +}; + +#ifndef QT_NO_GROUPBOX +class QAccessibleGroupBox : public QAccessibleWidget +{ +public: + explicit QAccessibleGroupBox(QWidget *w); + + QAccessible::State state() const Q_DECL_OVERRIDE; + QAccessible::Role role() const Q_DECL_OVERRIDE; + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + + QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >relations(QAccessible::Relation match = QAccessible::AllRelations) const Q_DECL_OVERRIDE; + + //QAccessibleActionInterface + QStringList actionNames() const Q_DECL_OVERRIDE; + void doAction(const QString &actionName) Q_DECL_OVERRIDE; + QStringList keyBindingsForAction(const QString &) const Q_DECL_OVERRIDE; + +private: + QGroupBox *groupBox() const; +}; +#endif + +#ifndef QT_NO_LINEEDIT +class QAccessibleLineEdit : public QAccessibleWidget, public QAccessibleTextInterface, public QAccessibleEditableTextInterface +{ +public: + explicit QAccessibleLineEdit(QWidget *o, const QString &name = QString()); + + QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE; + QAccessible::State state() const Q_DECL_OVERRIDE; + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleTextInterface + void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE; + QString attributes(int offset, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + int cursorPosition() const Q_DECL_OVERRIDE; + QRect characterRect(int offset) const Q_DECL_OVERRIDE; + int selectionCount() const Q_DECL_OVERRIDE; + int offsetAtPoint(const QPoint &point) const Q_DECL_OVERRIDE; + void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE; + QString textBeforeOffset (int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + void removeSelection(int selectionIndex) Q_DECL_OVERRIDE; + void setCursorPosition(int position) Q_DECL_OVERRIDE; + void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE; + int characterCount() const Q_DECL_OVERRIDE; + void scrollToSubstring(int startIndex, int endIndex) Q_DECL_OVERRIDE; + + // QAccessibleEditableTextInterface + void deleteText(int startOffset, int endOffset) Q_DECL_OVERRIDE; + void insertText(int offset, const QString &text) Q_DECL_OVERRIDE; + void replaceText(int startOffset, int endOffset, const QString &text) Q_DECL_OVERRIDE; +protected: + QLineEdit *lineEdit() const; +}; +#endif // QT_NO_LINEEDIT + +#ifndef QT_NO_PROGRESSBAR +class QAccessibleProgressBar : public QAccessibleDisplay, public QAccessibleValueInterface +{ +public: + explicit QAccessibleProgressBar(QWidget *o); + void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; + + // QAccessibleValueInterface + QVariant currentValue() const Q_DECL_OVERRIDE; + QVariant maximumValue() const Q_DECL_OVERRIDE; + QVariant minimumValue() const Q_DECL_OVERRIDE; + QVariant minimumStepSize() const Q_DECL_OVERRIDE; + void setCurrentValue(const QVariant &) Q_DECL_OVERRIDE {} + +protected: + QProgressBar *progressBar() const; +}; +#endif + +class QWindowContainer; +class QAccessibleWindowContainer : public QAccessibleWidget +{ +public: + QAccessibleWindowContainer(QWidget *w); + int childCount() const Q_DECL_OVERRIDE; + int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; + QAccessibleInterface *child(int i) const Q_DECL_OVERRIDE; + +private: + QWindowContainer *container() const; +}; + +#endif // QT_NO_ACCESSIBILITY + +QT_END_NAMESPACE + +#endif // SIMPLEWIDGETS_H diff --git a/src/widgets/accessible/widgets.pro b/src/widgets/accessible/widgets.pro new file mode 100644 index 0000000000..c6af6d3f71 --- /dev/null +++ b/src/widgets/accessible/widgets.pro @@ -0,0 +1,27 @@ +TARGET = qtaccessiblewidgets + +PLUGIN_TYPE = accessible +PLUGIN_EXTENDS = widgets +PLUGIN_CLASS_NAME = AccessibleFactory +load(qt_plugin) + +QT += core-private gui-private widgets-private + +QTDIR_build:REQUIRES += "contains(QT_CONFIG, accessibility)" + +SOURCES += main.cpp \ + simplewidgets.cpp \ + rangecontrols.cpp \ + complexwidgets.cpp \ + qaccessiblewidgets.cpp \ + qaccessiblemenu.cpp \ + itemviews.cpp + +HEADERS += qaccessiblewidgets.h \ + simplewidgets.h \ + rangecontrols.h \ + complexwidgets.h \ + qaccessiblemenu.h \ + itemviews.h + + diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index 3889a589c7..14c1ce081f 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -1262,9 +1262,17 @@ QColorShower::QColorShower(QColorDialog *parent) lblHtml->setBuddy(htEd); #endif +#if !defined(QT_NO_REGULAREXPRESSION) QRegularExpression regExp(QStringLiteral("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})")); QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, this); htEd->setValidator(validator); +#elif !defined(QT_NO_REGEXP) + QRegExp regExp(QStringLiteral("#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})")); + QRegExpValidator *validator = new QRegExpValidator(regExp, this); + htEd->setValidator(validator); +#else + htEd->setReadOnly(true); +#endif lblHtml->setAlignment(Qt::AlignRight|Qt::AlignVCenter); gl->addWidget(lblHtml, 5, 1); @@ -1460,38 +1468,46 @@ void QColorDialogPrivate::setCurrentQColor(const QColor &color) } } +// size of standard and custom color selector +enum { + colorColumns = 8, + standardColorRows = 6, + customColorRows = 2 +}; + bool QColorDialogPrivate::selectColor(const QColor &col) { QRgb color = col.rgb(); - int i = 0, j = 0; // Check standard colors if (standard) { const QRgb *standardColors = QColorDialogOptions::standardColors(); - for (i = 0; i < 6; i++) { - for (j = 0; j < 8; j++) { - if (color == standardColors[i + j*6]) { - _q_newStandard(i, j); - standard->setCurrent(i, j); - standard->setSelected(i, j); - standard->setFocus(); - return true; - } - } + const QRgb *standardColorsEnd = standardColors + standardColorRows * colorColumns; + const QRgb *match = std::find(standardColors, standardColorsEnd, color); + if (match != standardColorsEnd) { + const int index = int(match - standardColors); + const int row = index / standardColorRows; + const int column = index % standardColorRows; + _q_newStandard(row, column); + standard->setCurrent(row, column); + standard->setSelected(row, column); + standard->setFocus(); + return true; } } // Check custom colors if (custom) { const QRgb *customColors = QColorDialogOptions::customColors(); - for (i = 0; i < 2; i++) { - for (j = 0; j < 8; j++) { - if (color == customColors[i + j*2]) { - _q_newCustom(i, j); - custom->setCurrent(i, j); - custom->setSelected(i, j); - custom->setFocus(); - return true; - } - } + const QRgb *customColorsEnd = customColors + customColorRows * colorColumns; + const QRgb *match = std::find(customColors, customColorsEnd, color); + if (match != customColorsEnd) { + const int index = int(match - customColors); + const int row = index / customColorRows; + const int column = index % customColorRows; + _q_newCustom(row, column); + custom->setCurrent(row, column); + custom->setSelected(row, column); + custom->setFocus(); + return true; } } return false; @@ -1519,12 +1535,12 @@ void QColorDialogPrivate::_q_newColorTypedIn(QRgb rgb) void QColorDialogPrivate::_q_nextCustom(int r, int c) { - nextCust = r + 2 * c; + nextCust = r + customColorRows * c; } void QColorDialogPrivate::_q_newCustom(int r, int c) { - const int i = r + 2 * c; + const int i = r + customColorRows * c; setCurrentRgbColor(QColorDialogOptions::customColor(i)); if (standard) standard->setSelected(-1,-1); @@ -1545,12 +1561,10 @@ void QColorDialogPrivate::_q_pickScreenColor() q->installEventFilter(colorPickingEventFilter); // If user pushes Escape, the last color before picking will be restored. beforeScreenColorPicking = cs->currentColor(); - /*For some reason, q->grabMouse(Qt::CrossCursor) doesn't change - * the cursor, and therefore I have to change it manually. - */ - q->grabMouse(); #ifndef QT_NO_CURSOR - q->setCursor(Qt::CrossCursor); + q->grabMouse(Qt::CrossCursor); +#else + q->grabMouse(); #endif q->grabKeyboard(); /* With setMouseTracking(true) the desired color can be more precisedly picked up, @@ -1575,9 +1589,6 @@ void QColorDialogPrivate::releaseColorPicking() q->removeEventFilter(colorPickingEventFilter); q->releaseMouse(); q->releaseKeyboard(); -#ifndef QT_NO_CURSOR - q->setCursor(Qt::ArrowCursor); -#endif q->setMouseTracking(false); lblScreenColorInfo->setText(QLatin1String("\n")); addCusBt->setDisabled(false); @@ -1635,7 +1646,7 @@ void QColorDialogPrivate::initWidgets() } if (!smallDisplay) { - standard = new QColorWell(q, 6, 8, QColorDialogOptions::standardColors()); + standard = new QColorWell(q, standardColorRows, colorColumns, QColorDialogOptions::standardColors()); lblBasicColors = new QLabel(q); #ifndef QT_NO_SHORTCUT lblBasicColors->setBuddy(standard); @@ -1657,7 +1668,7 @@ void QColorDialogPrivate::initWidgets() leftLay->addStretch(); #endif - custom = new QColorWell(q, 2, 8, QColorDialogOptions::customColors()); + custom = new QColorWell(q, customColorRows, colorColumns, QColorDialogOptions::customColors()); custom->setAcceptDrops(true); q->connect(custom, SIGNAL(selected(int,int)), SLOT(_q_newCustom(int,int))); diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index 6349bdc301..605d430edf 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#define QT_NO_URL_CAST_FROM_STRING + #include <qvariant.h> #include <private/qwidgetitemdata_p.h> #include "qfiledialog.h" @@ -579,8 +581,8 @@ QFileDialogPrivate::~QFileDialogPrivate() void QFileDialogPrivate::initHelper(QPlatformDialogHelper *h) { QFileDialog *d = q_func(); - QObject::connect(h, SIGNAL(fileSelected(QUrl)), d, SLOT(_q_nativeFileSelected(QUrl))); - QObject::connect(h, SIGNAL(filesSelected(QList<QUrl>)), d, SLOT(_q_nativeFilesSelected(QList<QUrl>))); + QObject::connect(h, SIGNAL(fileSelected(QUrl)), d, SLOT(_q_emitUrlSelected(QUrl))); + QObject::connect(h, SIGNAL(filesSelected(QList<QUrl>)), d, SLOT(_q_emitUrlsSelected(QList<QUrl>))); QObject::connect(h, SIGNAL(currentChanged(QUrl)), d, SLOT(_q_nativeCurrentChanged(QUrl))); QObject::connect(h, SIGNAL(directoryEntered(QUrl)), d, SLOT(_q_nativeEnterDirectory(QUrl))); QObject::connect(h, SIGNAL(filterSelected(QString)), d, SIGNAL(filterSelected(QString))); @@ -2028,26 +2030,6 @@ QString QFileDialog::labelText(DialogLabel label) const return QString(); } -/* - For the native file dialogs -*/ - -#if defined(Q_WS_WIN) -extern QString qt_win_get_open_file_name(const QFileDialogArgs &args, - QString *initialDirectory, - QString *selectedFilter); - -extern QString qt_win_get_save_file_name(const QFileDialogArgs &args, - QString *initialDirectory, - QString *selectedFilter); - -extern QStringList qt_win_get_open_file_names(const QFileDialogArgs &args, - QString *initialDirectory, - QString *selectedFilter); - -extern QString qt_win_get_existing_directory(const QFileDialogArgs &args); -#endif - /*! This is a convenience static function that returns an existing file selected by the user. If the user presses Cancel, it returns a null string. @@ -2110,13 +2092,7 @@ QString QFileDialog::getOpenFileName(QWidget *parent, args.filter = filter; args.mode = ExistingFile; args.options = options; -#if defined(Q_WS_WIN) - if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog)) { - return qt_win_get_open_file_name(args, &(args.directory), selectedFilter); - } -#endif - // create a qt dialog QFileDialog dialog(args); if (selectedFilter && !selectedFilter->isEmpty()) dialog.selectNameFilter(*selectedFilter); @@ -2239,13 +2215,6 @@ QStringList QFileDialog::getOpenFileNames(QWidget *parent, args.mode = ExistingFiles; args.options = options; -#if defined(Q_WS_WIN) - if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog)) { - return qt_win_get_open_file_names(args, &(args.directory), selectedFilter); - } -#endif - - // create a qt dialog QFileDialog dialog(args); if (selectedFilter && !selectedFilter->isEmpty()) dialog.selectNameFilter(*selectedFilter); @@ -2371,13 +2340,6 @@ QString QFileDialog::getSaveFileName(QWidget *parent, args.mode = AnyFile; args.options = options; -#if defined(Q_WS_WIN) - if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog)) { - return qt_win_get_save_file_name(args, &(args.directory), selectedFilter); - } -#endif - - // create a qt dialog QFileDialog dialog(args); dialog.setAcceptMode(AcceptSave); if (selectedFilter && !selectedFilter->isEmpty()) @@ -2483,17 +2445,6 @@ QString QFileDialog::getExistingDirectory(QWidget *parent, args.mode = (options & ShowDirsOnly ? DirectoryOnly : Directory); args.options = options; -#if defined(Q_WS_WIN) - if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly) -#if defined(Q_OS_WINCE) - && qt_priv_ptr_valid -#endif - ) { - return qt_win_get_existing_directory(args); - } -#endif - - // create a qt dialog QFileDialog dialog(args); if (dialog.exec() == QDialog::Accepted) { return dialog.selectedFiles().value(0); @@ -2609,15 +2560,20 @@ void QFileDialog::done(int result) void QFileDialog::accept() { Q_D(QFileDialog); - QStringList files = selectedFiles(); - if (files.isEmpty()) - return; if (!d->usingWidgets()) { - d->emitFilesSelected(files); + const QList<QUrl> urls = selectedUrls(); + if (urls.isEmpty()) + return; + d->_q_emitUrlsSelected(urls); + if (urls.count() == 1) + d->_q_emitUrlSelected(urls.first()); QDialog::accept(); return; } + QStringList files = selectedFiles(); + if (files.isEmpty()) + return; QString lineEditText = d->lineEdit()->text(); // "hidden feature" type .. and then enter, and it will move up a dir // special case for ".." @@ -2780,7 +2736,7 @@ void QFileDialogPrivate::createWidgets() qFileDialogUi->setupUi(q); QList<QUrl> initialBookmarks; - initialBookmarks << QUrl::fromLocalFile(QLatin1String("")) + initialBookmarks << QUrl(QLatin1String("file:")) << QUrl::fromLocalFile(QDir::homePath()); qFileDialogUi->sidebar->setModelAndUrls(model, initialBookmarks); QFileDialog::connect(qFileDialogUi->sidebar, SIGNAL(goToUrl(QUrl)), @@ -3648,7 +3604,7 @@ void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString oldNa } } -void QFileDialogPrivate::_q_nativeFileSelected(const QUrl &file) +void QFileDialogPrivate::_q_emitUrlSelected(const QUrl &file) { Q_Q(QFileDialog); emit q->urlSelected(file); @@ -3656,7 +3612,7 @@ void QFileDialogPrivate::_q_nativeFileSelected(const QUrl &file) emit q->fileSelected(file.toLocalFile()); } -void QFileDialogPrivate::_q_nativeFilesSelected(const QList<QUrl> &files) +void QFileDialogPrivate::_q_emitUrlsSelected(const QList<QUrl> &files) { Q_Q(QFileDialog); emit q->urlsSelected(files); @@ -3757,7 +3713,7 @@ void QFileDialogComboBox::showPopup() idx = idx.parent(); } // add "my computer" - list.append(QUrl::fromLocalFile(QLatin1String(""))); + list.append(QUrl(QLatin1String("file:"))); urlModel->addUrls(list, 0); idx = model()->index(model()->rowCount() - 1, 0); diff --git a/src/widgets/dialogs/qfiledialog.h b/src/widgets/dialogs/qfiledialog.h index 404e16cde1..184e07261a 100644 --- a/src/widgets/dialogs/qfiledialog.h +++ b/src/widgets/dialogs/qfiledialog.h @@ -287,8 +287,8 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_updateOkButton()) Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QModelIndex &index)) Q_PRIVATE_SLOT(d_func(), void _q_enterDirectory(const QModelIndex &index)) - Q_PRIVATE_SLOT(d_func(), void _q_nativeFileSelected(const QUrl &)) - Q_PRIVATE_SLOT(d_func(), void _q_nativeFilesSelected(const QList<QUrl> &)) + Q_PRIVATE_SLOT(d_func(), void _q_emitUrlSelected(const QUrl &)) + Q_PRIVATE_SLOT(d_func(), void _q_emitUrlsSelected(const QList<QUrl> &)) Q_PRIVATE_SLOT(d_func(), void _q_nativeCurrentChanged(const QUrl &)) Q_PRIVATE_SLOT(d_func(), void _q_nativeEnterDirectory(const QUrl&)) Q_PRIVATE_SLOT(d_func(), void _q_goToDirectory(const QString &path)) diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h index 632bbca5a4..24969b1179 100644 --- a/src/widgets/dialogs/qfiledialog_p.h +++ b/src/widgets/dialogs/qfiledialog_p.h @@ -210,8 +210,8 @@ public: void _q_updateOkButton(); void _q_currentChanged(const QModelIndex &index); void _q_enterDirectory(const QModelIndex &index); - void _q_nativeFileSelected(const QUrl &file); - void _q_nativeFilesSelected(const QList<QUrl> &files); + void _q_emitUrlSelected(const QUrl &file); + void _q_emitUrlsSelected(const QList<QUrl> &files); void _q_nativeCurrentChanged(const QUrl &file); void _q_nativeEnterDirectory(const QUrl &directory); void _q_goToDirectory(const QString &); @@ -369,7 +369,7 @@ inline QUrl QFileDialogPrivate::directory_sys() const { if (QPlatformFileDialogHelper *helper = platformFileDialogHelper()) return helper->directory(); - return QString(); + return QUrl(); } inline void QFileDialogPrivate::selectFile_sys(const QUrl &filename) diff --git a/src/widgets/dialogs/qinputdialog.cpp b/src/widgets/dialogs/qinputdialog.cpp index 10d693b4a3..99344b3dd8 100644 --- a/src/widgets/dialogs/qinputdialog.cpp +++ b/src/widgets/dialogs/qinputdialog.cpp @@ -60,25 +60,37 @@ QT_USE_NAMESPACE -static const char *signalForMember(const char *member) +enum CandidateSignal { + TextValueSelectedSignal, + IntValueSelectedSignal, + DoubleValueSelectedSignal, + + NumCandidateSignals +}; + +static const char *candidateSignal(int which) { - static const int NumCandidates = 4; - static const char * const candidateSignals[NumCandidates] = { - SIGNAL(textValueSelected(QString)), - SIGNAL(intValueSelected(int)), - SIGNAL(doubleValueSelected(double)), - SIGNAL(accepted()) + switch (CandidateSignal(which)) { + case TextValueSelectedSignal: return SIGNAL(textValueSelected(QString)); + case IntValueSelectedSignal: return SIGNAL(intValueSelected(int)); + case DoubleValueSelectedSignal: return SIGNAL(doubleValueSelected(double)); + + case NumCandidateSignals: ; // fall through }; + Q_UNREACHABLE(); + return Q_NULLPTR; +} +static const char *signalForMember(const char *member) +{ QByteArray normalizedMember(QMetaObject::normalizedSignature(member)); - int i = 0; - while (i < NumCandidates - 1) { // sic - if (QMetaObject::checkConnectArgs(candidateSignals[i], normalizedMember)) - break; - ++i; - } - return candidateSignals[i]; + for (int i = 0; i < NumCandidateSignals; ++i) + if (QMetaObject::checkConnectArgs(candidateSignal(i), normalizedMember)) + return candidateSignal(i); + + // otherwise, use fit-all accepted signal: + return SIGNAL(accepted()); } QT_BEGIN_NAMESPACE diff --git a/src/widgets/dialogs/qprogressdialog.cpp b/src/widgets/dialogs/qprogressdialog.cpp index 662504fc25..35a75adea2 100644 --- a/src/widgets/dialogs/qprogressdialog.cpp +++ b/src/widgets/dialogs/qprogressdialog.cpp @@ -84,6 +84,7 @@ public: void init(const QString &labelText, const QString &cancelText, int min, int max); void layout(); void retranslateStrings(); + void setCancelButtonText(const QString &cancelButtonText); void _q_disconnectOnClose(); QLabel *label; @@ -178,9 +179,8 @@ void QProgressDialogPrivate::layout() void QProgressDialogPrivate::retranslateStrings() { - Q_Q(QProgressDialog); if (useDefaultCancelText) - q->setCancelButtonText(QProgressDialog::tr("Cancel")); + setCancelButtonText(QProgressDialog::tr("Cancel")); } void QProgressDialogPrivate::_q_disconnectOnClose() @@ -354,6 +354,11 @@ QProgressDialog::~QProgressDialog() void QProgressDialog::setLabel(QLabel *label) { Q_D(QProgressDialog); + if (label == d->label) { + if (label) + qWarning("QProgressDialog::setLabel: Attempt to set the same label again"); + return; + } delete d->label; d->label = label; if (label) { @@ -411,6 +416,11 @@ void QProgressDialog::setLabelText(const QString &text) void QProgressDialog::setCancelButton(QPushButton *cancelButton) { Q_D(QProgressDialog); + if (d->cancel == cancelButton) { + if (cancelButton) + qWarning("QProgressDialog::setCancelButton: Attempt to set the same button again"); + return; + } delete d->cancel; d->cancel = cancelButton; if (cancelButton) { @@ -448,19 +458,25 @@ void QProgressDialog::setCancelButtonText(const QString &cancelButtonText) { Q_D(QProgressDialog); d->useDefaultCancelText = false; + d->setCancelButtonText(cancelButtonText); +} + +void QProgressDialogPrivate::setCancelButtonText(const QString &cancelButtonText) +{ + Q_Q(QProgressDialog); if (!cancelButtonText.isNull()) { - if (d->cancel) { - d->cancel->setText(cancelButtonText); + if (cancel) { + cancel->setText(cancelButtonText); } else { - setCancelButton(new QPushButton(cancelButtonText, this)); + q->setCancelButton(new QPushButton(cancelButtonText, q)); } } else { - setCancelButton(0); + q->setCancelButton(0); } - int w = qMax(isVisible() ? width() : 0, sizeHint().width()); - int h = qMax(isVisible() ? height() : 0, sizeHint().height()); - resize(w, h); + int w = qMax(q->isVisible() ? q->width() : 0, q->sizeHint().width()); + int h = qMax(q->isVisible() ? q->height() : 0, q->sizeHint().height()); + q->resize(w, h); } @@ -483,6 +499,10 @@ void QProgressDialog::setBar(QProgressBar *bar) qWarning("QProgressDialog::setBar: Cannot set a new progress bar " "while the old one is active"); #endif + if (bar == d->bar) { + qWarning("QProgressDialog::setBar: Attempt to set the same progress bar again"); + return; + } delete d->bar; d->bar = bar; int w = qMax(isVisible() ? width() : 0, sizeHint().width()); @@ -507,7 +527,7 @@ bool QProgressDialog::wasCanceled() const \property QProgressDialog::maximum \brief the highest value represented by the progress bar - The default is 0. + The default is 100. \sa minimum, setRange() */ diff --git a/src/widgets/dialogs/qsidebar.cpp b/src/widgets/dialogs/qsidebar.cpp index 3d22992e3a..7b691dc609 100644 --- a/src/widgets/dialogs/qsidebar.cpp +++ b/src/widgets/dialogs/qsidebar.cpp @@ -249,7 +249,8 @@ void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move) continue; //this makes sure the url is clean const QString cleanUrl = QDir::cleanPath(url.toLocalFile()); - url = QUrl::fromLocalFile(cleanUrl); + if (!cleanUrl.isEmpty()) + url = QUrl::fromLocalFile(cleanUrl); for (int j = 0; move && j < rowCount(); ++j) { QString local = index(j, 0).data(UrlRole).toUrl().toLocalFile(); diff --git a/src/widgets/dialogs/qwizard.cpp b/src/widgets/dialogs/qwizard.cpp index b294e98c30..15f922a5b8 100644 --- a/src/widgets/dialogs/qwizard.cpp +++ b/src/widgets/dialogs/qwizard.cpp @@ -1475,7 +1475,7 @@ void QWizardPrivate::updateButtonTexts() void QWizardPrivate::updateButtonLayout() { if (buttonsHaveCustomLayout) { - QVarLengthArray<QWizard::WizardButton> array(buttonsCustomLayout.count()); + QVarLengthArray<QWizard::WizardButton, QWizard::NButtons> array(buttonsCustomLayout.count()); for (int i = 0; i < buttonsCustomLayout.count(); ++i) array[i] = buttonsCustomLayout.at(i); setButtonLayout(array.constData(), array.count()); diff --git a/src/widgets/doc/qtwidgets.qdocconf b/src/widgets/doc/qtwidgets.qdocconf index 508a257fa5..4b19702208 100644 --- a/src/widgets/doc/qtwidgets.qdocconf +++ b/src/widgets/doc/qtwidgets.qdocconf @@ -2,7 +2,6 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtWidgets description = Qt Widgets Reference Documentation -url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION examplesinstallpath = widgets diff --git a/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp b/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp new file mode 100644 index 0000000000..bc279e4406 --- /dev/null +++ b/src/widgets/doc/snippets/code/doc_gui_widgets_qopenglwidget.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +class MyGLWidget : public QOpenGLWidget +{ +public: + MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { } + +protected: + void initializeGL() + { + // Set up the rendering context, load shaders and other resources, etc.: + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + ... + } + + void resizeGL(int w, int h) + { + // Update projection matrix and other size related settings: + m_projection.setToIdentity(); + m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f); + ... + } + + void paintGL() + { + // Draw the scene: + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClear(GL_COLOR_BUFFER_BIT); + ... + } + +}; +//! [0] + +//! [1] +class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions +{ + ... + void initializeGL() + { + initializeOpenGLFunctions(); + glClearColor(...); + ... + } + ... +}; +//! [1] + +//! [2] +QOpenGLWidget *widget = new QOpenGLWidget(parent); +QSurfaceFormat format; +format.setDepthBufferSize(24); +format.setStencilBufferSize(8); +format.setVersion(3, 2); +format.setProfile(QSurfaceFormat::CoreProfile); +widget->setFormat(format); // must be called before the widget or its parent window gets shown +//! [2] + +//! [3] + ... + void paintGL() + { + QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>(); + ... + f->glDrawArraysInstanced(...); + ... + } + ... +//! [3] diff --git a/src/widgets/doc/snippets/code/doc_src_stylesheet.qdoc b/src/widgets/doc/snippets/code/doc_src_stylesheet.qdoc index 36d3dc6af4..702c17bc26 100644 --- a/src/widgets/doc/snippets/code/doc_src_stylesheet.qdoc +++ b/src/widgets/doc/snippets/code/doc_src_stylesheet.qdoc @@ -1864,3 +1864,8 @@ QTabBar::close-button:hover { } //! [159] + +//! [160] +* { lineedit-password-mask-delay: 1000 } +//! [160] + diff --git a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc index e8c1be4c46..32fe384c75 100644 --- a/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc +++ b/src/widgets/doc/src/widgets-and-layouts/stylesheet.qdoc @@ -862,6 +862,9 @@ \l{#lineedit-password-character-prop}{lineedit-password-character} property. + The password mask delay can be changed using the + \l{#lineedit-password-mask-delay-prop}{lineedit-password-mask-delay} + See \l{Qt Style Sheets Examples#Customizing QLineEdit}{Customizing QLineEdit} for an example. @@ -2009,6 +2012,23 @@ \snippet code/doc_src_stylesheet.qdoc 61 \row + \li \b{\c lineedit-password-mask-delay*} \target lineedit-password-mask-delay-prop + \li \l{#Number}{Number} + \li The QLineEdit password mask delay in milliseconds before + \l{#lineedit-password-character-prop}{lineedit-password-character} is applied to visible character. + + If this property is not specified, it defaults to the + value specified by the current style for the + \l{QStyle::}{SH_LineEdit_PasswordMaskDelay} style hint. + + \b{This property was added in Qt 5.4.} + + Example: + + \snippet code/doc_src_stylesheet.qdoc 160 + + + \row \li \b{\c margin} \target margin-prop \li \l {Box Lengths} \li The widget's margins. Equivalent to specifying \c diff --git a/src/widgets/graphicsview/qgraphicsitem.cpp b/src/widgets/graphicsview/qgraphicsitem.cpp index e327a8737a..551f229611 100644 --- a/src/widgets/graphicsview/qgraphicsitem.cpp +++ b/src/widgets/graphicsview/qgraphicsitem.cpp @@ -332,6 +332,8 @@ this flag is disabled; children can draw anywhere. This behavior is enforced by QGraphicsView::drawItems() or QGraphicsScene::drawItems(). This flag was introduced in Qt 4.3. + \note This flag is similar to ItemContainsChildrenInShape but in addition + enforces the containment by clipping the children. \value ItemIgnoresTransformations The item ignores inherited transformations (i.e., its position is still anchored to its parent, but @@ -423,6 +425,19 @@ ItemStopsClickFocusPropagation, but also suppresses focus-out. This flag allows you to completely take over focus handling. This flag was introduced in Qt 4.7. \endomit + + \value ItemContainsChildrenInShape This flag indicates that all of the + item's direct or indirect children only draw within the item's shape. + Unlike ItemClipsChildrenToShape, this restriction is not enforced. Set + ItemContainsChildrenInShape when you manually assure that drawing + is bound to the item's shape and want to avoid the cost associated with + enforcing the clip. Setting this flag enables more efficient drawing and + collision detection. The flag is disabled by default. + \note If both this flag and ItemClipsChildrenToShape are set, the clip + will be enforced. This is equivalent to just setting + ItemClipsChildrenToShape. + . + This flag was introduced in Qt 5.4. */ /*! @@ -836,6 +851,10 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch flag = AncestorIgnoresTransformations; enabled = flags & QGraphicsItem::ItemIgnoresTransformations; break; + case QGraphicsItem::ItemContainsChildrenInShape: + flag = AncestorContainsChildren; + enabled = flags & QGraphicsItem::ItemContainsChildrenInShape; + break; default: return; } @@ -895,6 +914,8 @@ void QGraphicsItemPrivate::updateAncestorFlags() flags |= AncestorClipsChildren; if (pd->flags & QGraphicsItem::ItemIgnoresTransformations) flags |= AncestorIgnoresTransformations; + if (pd->flags & QGraphicsItem::ItemContainsChildrenInShape) + flags |= AncestorContainsChildren; } if (ancestorFlags == flags) @@ -1831,6 +1852,11 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) d_ptr->markParentDirty(true); } + if ((flags & ItemContainsChildrenInShape) != (oldFlags & ItemContainsChildrenInShape)) { + // Item children containtment changes. Propagate the ancestor flag to all children. + d_ptr->updateAncestorFlag(ItemContainsChildrenInShape); + } + if ((flags & ItemIgnoresTransformations) != (oldFlags & ItemIgnoresTransformations)) { // Item children clipping changes. Propagate the ancestor flag to // all children. @@ -2322,7 +2348,8 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, } // Update children with explicitly = false. - const bool updateChildren = update && !((flags & QGraphicsItem::ItemClipsChildrenToShape) + const bool updateChildren = update && !((flags & QGraphicsItem::ItemClipsChildrenToShape + || flags & QGraphicsItem::ItemContainsChildrenInShape) && !(flags & QGraphicsItem::ItemHasNoContents)); foreach (QGraphicsItem *child, children) { if (!newVisible || !child->d_ptr->explicitlyHidden) @@ -2835,7 +2862,9 @@ QRectF QGraphicsItemPrivate::effectiveBoundingRect(QGraphicsItem *topMostEffectI #ifndef QT_NO_GRAPHICSEFFECT Q_Q(const QGraphicsItem); QRectF brect = effectiveBoundingRect(q_ptr->boundingRect()); - if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren || topMostEffectItem == q) + if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren + || topMostEffectItem == q) return brect; const QGraphicsItem *effectParent = parent; @@ -2847,6 +2876,7 @@ QRectF QGraphicsItemPrivate::effectiveBoundingRect(QGraphicsItem *topMostEffectI brect = effectParent->mapRectToItem(q, effectRectInParentSpace); } if (effectParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || effectParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren || topMostEffectItem == effectParent) { return brect; } @@ -7442,7 +7472,8 @@ QVariant QGraphicsItem::extension(const QVariant &variant) const */ void QGraphicsItem::addToIndex() { - if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { + if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren) { // ### add to child index only if applicable return; } @@ -7459,7 +7490,8 @@ void QGraphicsItem::addToIndex() */ void QGraphicsItem::removeFromIndex() { - if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { + if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren) { // ### remove from child index only if applicable return; } @@ -11451,6 +11483,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemStopsFocusHandling: str = "ItemStopsFocusHandling"; break; + case QGraphicsItem::ItemContainsChildrenInShape: + str = "ItemContainsChildrenInShape"; + break; } debug << str; return debug; diff --git a/src/widgets/graphicsview/qgraphicsitem.h b/src/widgets/graphicsview/qgraphicsitem.h index 4283deb5b8..dfc06fdaea 100644 --- a/src/widgets/graphicsview/qgraphicsitem.h +++ b/src/widgets/graphicsview/qgraphicsitem.h @@ -105,7 +105,8 @@ public: ItemIsFocusScope = 0x8000, // internal ItemSendsScenePositionChanges = 0x10000, ItemStopsClickFocusPropagation = 0x20000, - ItemStopsFocusHandling = 0x40000 + ItemStopsFocusHandling = 0x40000, + ItemContainsChildrenInShape = 0x80000 // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) diff --git a/src/widgets/graphicsview/qgraphicsitem_p.h b/src/widgets/graphicsview/qgraphicsitem_p.h index 3968d89a13..4d1835f178 100644 --- a/src/widgets/graphicsview/qgraphicsitem_p.h +++ b/src/widgets/graphicsview/qgraphicsitem_p.h @@ -172,7 +172,8 @@ public: AncestorHandlesChildEvents = 0x1, AncestorClipsChildren = 0x2, AncestorIgnoresTransformations = 0x4, - AncestorFiltersChildEvents = 0x8 + AncestorFiltersChildEvents = 0x8, + AncestorContainsChildren = 0x10 }; inline QGraphicsItemPrivate() @@ -213,7 +214,6 @@ public: needSortChildren(0), allChildrenDirty(0), fullUpdatePending(0), - dirtyChildrenBoundingRect(1), flags(0), paintedViewBoundingRectsNeedRepaint(0), dirtySceneTransform(1), @@ -239,6 +239,7 @@ public: mayHaveChildWithGraphicsEffect(0), isDeclarativeItem(0), sendParentChangeNotification(0), + dirtyChildrenBoundingRect(1), globalStackingOrder(-1), q_ptr(0) { @@ -544,7 +545,7 @@ public: quint32 handlesChildEvents : 1; quint32 itemDiscovered : 1; quint32 hasCursor : 1; - quint32 ancestorFlags : 4; + quint32 ancestorFlags : 5; quint32 cacheMode : 2; quint32 hasBoundingRegionGranularity : 1; quint32 isWidget : 1; @@ -555,10 +556,9 @@ public: quint32 needSortChildren : 1; quint32 allChildrenDirty : 1; quint32 fullUpdatePending : 1; - quint32 dirtyChildrenBoundingRect : 1; // Packed 32 bits - quint32 flags : 19; + quint32 flags : 20; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; quint32 geometryChanged : 1; @@ -571,9 +571,9 @@ public: quint32 filtersDescendantEvents : 1; quint32 sceneTransformTranslateOnly : 1; quint32 notifyBoundingRectChanged : 1; - quint32 notifyInvalidated : 1; // New 32 bits + quint32 notifyInvalidated : 1; quint32 mouseSetsFocus : 1; quint32 explicitActivate : 1; quint32 wantsActive : 1; @@ -585,7 +585,8 @@ public: quint32 mayHaveChildWithGraphicsEffect : 1; quint32 isDeclarativeItem : 1; quint32 sendParentChangeNotification : 1; - quint32 padding : 21; + quint32 dirtyChildrenBoundingRect : 1; + quint32 padding : 19; // Optional stacking order int globalStackingOrder; diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index bccdb1fbaa..1cd162e6bb 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -299,6 +299,7 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() painterStateProtection(true), sortCacheEnabled(false), allItemsIgnoreTouchEvents(true), + minimumRenderSize(0.0), selectionChanging(0), rectAdjust(2), focusItem(0), @@ -4716,19 +4717,40 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * wasDirtyParentSceneTransform = true; } - const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape + || item->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape); bool drawItem = itemHasContents && !itemIsFullyTransparent; - if (drawItem) { + if (drawItem || minimumRenderSize > 0.0) { const QRectF brect = adjustedItemEffectiveBoundingRect(item); ENSURE_TRANSFORM_PTR - QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toAlignedRect() - : transformPtr->mapRect(brect).toAlignedRect(); - viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust); - if (widget) - item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); - drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) - : !viewBoundingRect.normalized().isEmpty(); - if (!drawItem) { + QRectF preciseViewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()) + : transformPtr->mapRect(brect); + + bool itemIsTooSmallToRender = false; + if (minimumRenderSize > 0.0 + && (preciseViewBoundingRect.width() < minimumRenderSize + || preciseViewBoundingRect.height() < minimumRenderSize)) { + itemIsTooSmallToRender = true; + drawItem = false; + } + + bool itemIsOutsideVisibleRect = false; + if (drawItem) { + QRect viewBoundingRect = preciseViewBoundingRect.toAlignedRect(); + viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust); + if (widget) + item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); + drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) + : !viewBoundingRect.normalized().isEmpty(); + itemIsOutsideVisibleRect = !drawItem; + } + + if (itemIsTooSmallToRender || itemIsOutsideVisibleRect) { + // We cannot simply use !drawItem here. If we did it is possible + // to enter the outter if statement with drawItem == false and minimumRenderSize > 0 + // and finally end up inside this inner if, even though none of the above two + // conditions are met. In that case we should not return from this function + // but call draw() instead. if (!itemHasChildren) return; if (itemClipsChildrenToShape) { @@ -5200,7 +5222,8 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // Process children. if (itemHasChildren && item->d_ptr->dirtyChildren) { - const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape; + const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape + || item->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape; // Items with no content are threated as 'dummy' items which means they are never drawn and // 'processed', so the painted view bounding rect is never up-to-date. This means that whenever // such an item changes geometry, its children have to take care of the update regardless @@ -5729,6 +5752,49 @@ bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event) return d->sendEvent(item, event); } +/*! + \property QGraphicsScene::minimumRenderSize + \since 5.4 + \brief the minimal view-transformed size an item must have to be drawn + + When the scene is rendered, any item whose width or height, transformed + to the target view, is smaller that minimumRenderSize(), will not be + rendered. If an item is not rendered and it clips its children items + they will also not be rendered. Set this value to speed up rendering + of scenes with many objects rendered on a zoomed out view. + + The default value is 0. If unset, or if set to 0 or a negative value, + all items will always be rendered. + + For example, setting this property can be especially useful if a scene + is rendered by multiple views, one of which serves as an overview which + always displays all items. In scenes with many items, such a view will + use a high scaling factor so that all items can be shown. Due to the + scaling, smaller items will only make an insignificant contribution to + the final rendered scene. To avoid drawing these items and reduce the + time necessary to render the scene, you can call setMinimumRenderSize() + with a non-negative value. + + \note Items that are not drawn as a result of being too small, are still + returned by methods such as items() and itemAt(), and participate in + collision detection and interactions. It is recommended that you set + minimumRenderSize() to a value less than or equal to 1 in order to + avoid large unrendered items that are interactive. + + \sa QStyleOptionGraphicsItem::levelOfDetailFromTransform() +*/ +qreal QGraphicsScene::minimumRenderSize() const +{ + Q_D(const QGraphicsScene); + return d->minimumRenderSize; +} +void QGraphicsScene::setMinimumRenderSize(qreal minSize) +{ + Q_D(QGraphicsScene); + d->minimumRenderSize = minSize; + update(); +} + void QGraphicsScenePrivate::addView(QGraphicsView *view) { views << view; diff --git a/src/widgets/graphicsview/qgraphicsscene.h b/src/widgets/graphicsview/qgraphicsscene.h index cde0eda125..d95237baef 100644 --- a/src/widgets/graphicsview/qgraphicsscene.h +++ b/src/widgets/graphicsview/qgraphicsscene.h @@ -107,6 +107,7 @@ class Q_WIDGETS_EXPORT QGraphicsScene : public QObject Q_PROPERTY(QFont font READ font WRITE setFont) Q_PROPERTY(bool sortCacheEnabled READ isSortCacheEnabled WRITE setSortCacheEnabled) Q_PROPERTY(bool stickyFocus READ stickyFocus WRITE setStickyFocus) + Q_PROPERTY(qreal minimumRenderSize READ minimumRenderSize WRITE setMinimumRenderSize) public: enum ItemIndexMethod { @@ -249,6 +250,9 @@ public: bool sendEvent(QGraphicsItem *item, QEvent *event); + qreal minimumRenderSize() const; + void setMinimumRenderSize(qreal minSize); + public Q_SLOTS: void update(const QRectF &rect = QRectF()); void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers); @@ -309,6 +313,7 @@ private: friend class QGraphicsEffect; friend class QGraphicsSceneIndex; friend class QGraphicsSceneIndexPrivate; + friend class QGraphicsSceneLinearIndex; friend class QGraphicsSceneBspTreeIndex; friend class QGraphicsSceneBspTreeIndexPrivate; friend class QGraphicsItemEffectSourcePrivate; diff --git a/src/widgets/graphicsview/qgraphicsscene_p.h b/src/widgets/graphicsview/qgraphicsscene_p.h index 9e5bcec488..9f50dcfea7 100644 --- a/src/widgets/graphicsview/qgraphicsscene_p.h +++ b/src/widgets/graphicsview/qgraphicsscene_p.h @@ -117,6 +117,8 @@ public: quint32 allItemsIgnoreTouchEvents : 1; quint32 padding : 15; + qreal minimumRenderSize; + QRectF growingItemsBoundingRect; void _q_emitUpdated(); diff --git a/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp index 7598163f2d..50f17ab73f 100644 --- a/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/widgets/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -169,7 +169,8 @@ void QGraphicsSceneBspTreeIndexPrivate::_q_updateIndex() untransformableItems << item; continue; } - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren) continue; bsp.insertItem(item, item->d_ptr->sceneEffectiveBoundingRect()); @@ -351,7 +352,8 @@ void QGraphicsSceneBspTreeIndexPrivate::removeItem(QGraphicsItem *item, bool rec // Avoid virtual function calls from the destructor. purgePending = true; removedItems << item; - } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + } else if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren)) { bsp.removeItem(item, item->d_ptr->sceneEffectiveBoundingRect()); } } else { @@ -510,7 +512,8 @@ void QGraphicsSceneBspTreeIndex::prepareBoundingRectChange(const QGraphicsItem * return; if (item->d_ptr->index == -1 || item->d_ptr->itemIsUntransformable() - || (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + || (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren)) { return; // Item is not in BSP tree; nothing to do. } @@ -641,8 +644,10 @@ void QGraphicsSceneBspTreeIndex::itemChange(const QGraphicsItem *item, QGraphics QGraphicsItem::GraphicsItemFlags newFlags = *static_cast<const QGraphicsItem::GraphicsItemFlags *>(value); bool ignoredTransform = item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations; bool willIgnoreTransform = newFlags & QGraphicsItem::ItemIgnoresTransformations; - bool clipsChildren = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape; - bool willClipChildren = newFlags & QGraphicsItem::ItemClipsChildrenToShape; + bool clipsChildren = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape + || item->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape; + bool willClipChildren = newFlags & QGraphicsItem::ItemClipsChildrenToShape + || newFlags & QGraphicsItem::ItemContainsChildrenInShape; if ((ignoredTransform != willIgnoreTransform) || (clipsChildren != willClipChildren)) { QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); // Remove item and its descendants from the index and append @@ -663,10 +668,13 @@ void QGraphicsSceneBspTreeIndex::itemChange(const QGraphicsItem *item, QGraphics bool ignoredTransform = item->d_ptr->itemIsUntransformable(); bool willIgnoreTransform = (item->d_ptr->flags & QGraphicsItem::ItemIgnoresTransformations) || (newParent && newParent->d_ptr->itemIsUntransformable()); - bool ancestorClippedChildren = item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren; + bool ancestorClippedChildren = item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren; bool ancestorWillClipChildren = newParent - && ((newParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) - || (newParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)); + && ((newParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape + || newParent->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape) + || (newParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren + || newParent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorContainsChildren)); if ((ignoredTransform != willIgnoreTransform) || (ancestorClippedChildren != ancestorWillClipChildren)) { QGraphicsItem *thatItem = const_cast<QGraphicsItem *>(item); // Remove item and its descendants from the index and append diff --git a/src/widgets/graphicsview/qgraphicssceneevent.cpp b/src/widgets/graphicsview/qgraphicssceneevent.cpp index 631aa7bf19..be91c63fad 100644 --- a/src/widgets/graphicsview/qgraphicssceneevent.cpp +++ b/src/widgets/graphicsview/qgraphicssceneevent.cpp @@ -348,7 +348,7 @@ class QGraphicsSceneMouseEventPrivate : public QGraphicsSceneEventPrivate public: inline QGraphicsSceneMouseEventPrivate() : button(Qt::NoButton), - buttons(0), modifiers(0) + buttons(0), modifiers(0), source(Qt::MouseEventNotSynthesized), flags(0) { } QPointF pos; @@ -363,6 +363,8 @@ public: Qt::MouseButton button; Qt::MouseButtons buttons; Qt::KeyboardModifiers modifiers; + Qt::MouseEventSource source; + Qt::MouseEventFlags flags; }; /*! @@ -626,6 +628,60 @@ Qt::KeyboardModifiers QGraphicsSceneMouseEvent::modifiers() const } /*! + \since 5.4 + + Returns information about the mouse event source. + + The mouse event source can be used to distinguish between genuine + and artificial mouse events. The latter are events that are + synthesized from touch events by the operating system or Qt itself. + + \sa Qt::MouseEventSource + \sa QMouseEvent::source() + */ +Qt::MouseEventSource QGraphicsSceneMouseEvent::source() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->source; +} + +/*! + \since 5.4 + \internal + */ +void QGraphicsSceneMouseEvent::setSource(Qt::MouseEventSource source) +{ + Q_D(QGraphicsSceneMouseEvent); + d->source = source; +} + +/*! + \since 5.4 + + Returns the mouse event flags. + + The mouse event flags provide additional information about a mouse event. + + \sa Qt::MouseEventFlag + \sa QMouseEvent::flags() + */ +Qt::MouseEventFlags QGraphicsSceneMouseEvent::flags() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->flags; +} + +/*! + \since 5.4 + \internal + */ +void QGraphicsSceneMouseEvent::setFlags(Qt::MouseEventFlags flags) +{ + Q_D(QGraphicsSceneMouseEvent); + d->flags = flags; +} + +/*! \internal */ void QGraphicsSceneMouseEvent::setModifiers(Qt::KeyboardModifiers modifiers) diff --git a/src/widgets/graphicsview/qgraphicssceneevent.h b/src/widgets/graphicsview/qgraphicssceneevent.h index e744d08edf..2c9c1f3f50 100644 --- a/src/widgets/graphicsview/qgraphicssceneevent.h +++ b/src/widgets/graphicsview/qgraphicssceneevent.h @@ -121,6 +121,12 @@ public: Qt::KeyboardModifiers modifiers() const; void setModifiers(Qt::KeyboardModifiers modifiers); + Qt::MouseEventSource source() const; + void setSource(Qt::MouseEventSource source); + + Qt::MouseEventFlags flags() const; + void setFlags(Qt::MouseEventFlags); + private: Q_DECLARE_PRIVATE(QGraphicsSceneMouseEvent) Q_DISABLE_COPY(QGraphicsSceneMouseEvent) diff --git a/src/widgets/graphicsview/qgraphicssceneindex.cpp b/src/widgets/graphicsview/qgraphicssceneindex.cpp index 40f63937f4..d06c5523ca 100644 --- a/src/widgets/graphicsview/qgraphicssceneindex.cpp +++ b/src/widgets/graphicsview/qgraphicssceneindex.cpp @@ -278,7 +278,8 @@ void QGraphicsSceneIndexPrivate::recursive_items_helper(QGraphicsItem *item, QRe Q_ASSERT(!item->d_ptr->dirtySceneTransform); } - const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape); + const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape + || item->d_ptr->flags & QGraphicsItem::ItemContainsChildrenInShape); bool processItem = !itemIsFullyTransparent; if (processItem) { processItem = intersect(item, exposeRect, mode, viewTransform, intersectData); diff --git a/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h b/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h index 7debcfb501..baf3de3755 100644 --- a/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h +++ b/src/widgets/graphicsview/qgraphicsscenelinearindex_p.h @@ -70,31 +70,62 @@ class Q_AUTOTEST_EXPORT QGraphicsSceneLinearIndex : public QGraphicsSceneIndex Q_OBJECT public: - QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0) : QGraphicsSceneIndex(scene) + QGraphicsSceneLinearIndex(QGraphicsScene *scene = 0) : QGraphicsSceneIndex(scene), m_itemsIsValid(true) { } QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::DescendingOrder) const - { Q_UNUSED(order); return m_items; } + { + Q_UNUSED(order); + return validList(); + } virtual QList<QGraphicsItem *> estimateItems(const QRectF &rect, Qt::SortOrder order) const { Q_UNUSED(rect); Q_UNUSED(order); - return m_items; + return validList(); } protected : virtual void clear() - { m_items.clear(); } + { + m_items.clear(); + m_itemsIsValid = true; + } virtual void addItem(QGraphicsItem *item) - { m_items << item; } + { + if (m_itemsIsValid) + m_items << item; + } virtual void removeItem(QGraphicsItem *item) - { m_items.removeOne(item); } + { + Q_UNUSED(item); + m_itemsIsValid = false; + } private: - QList<QGraphicsItem*> m_items; + mutable QList<QGraphicsItem*> m_items; + mutable bool m_itemsIsValid; + + QList<QGraphicsItem*>& validList() const + { + if (!m_itemsIsValid) + { + m_items.clear(); + + QList<QGraphicsItem*> stack = scene()->d_func()->topLevelItems; + m_items << stack; + while (!stack.isEmpty()) + { + m_items << stack.last()->childItems(); + stack << stack.takeLast()->childItems(); + } + m_itemsIsValid = true; + } + return m_items; + } }; #endif // QT_NO_GRAPHICSVIEW diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index 5bcf52b6a3..7bdfae5fec 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -655,6 +655,8 @@ void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) mouseEvent.setButtons(event->buttons()); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); + mouseEvent.setSource(event->source()); + mouseEvent.setFlags(event->flags()); lastMouseMoveScenePoint = mouseEvent.scenePos(); lastMouseMoveScreenPoint = mouseEvent.screenPos(); mouseEvent.setAccepted(false); @@ -2760,7 +2762,7 @@ void QGraphicsView::setupViewport(QWidget *widget) return; } - const bool isGLWidget = widget->inherits("QGLWidget"); + const bool isGLWidget = widget->inherits("QGLWidget") || widget->inherits("QOpenGLWidget"); d->accelerateScrolling = !(isGLWidget); @@ -3217,6 +3219,8 @@ void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) mouseEvent.setAccepted(false); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); + mouseEvent.setSource(event->source()); + mouseEvent.setFlags(event->flags()); if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); else @@ -3265,6 +3269,8 @@ void QGraphicsView::mousePressEvent(QMouseEvent *event) mouseEvent.setButtons(event->buttons()); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); + mouseEvent.setSource(event->source()); + mouseEvent.setFlags(event->flags()); mouseEvent.setAccepted(false); if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); @@ -3392,6 +3398,8 @@ void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) mouseEvent.setButtons(event->buttons()); mouseEvent.setButton(event->button()); mouseEvent.setModifiers(event->modifiers()); + mouseEvent.setSource(event->source()); + mouseEvent.setFlags(event->flags()); mouseEvent.setAccepted(false); if (event->spontaneous()) qt_sendSpontaneousEvent(d->scene, &mouseEvent); diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index b2dc614b2b..4b59e72545 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -1949,7 +1949,7 @@ void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) case AboveItem: if (d->isIndexDropEnabled(index.parent())) { d->dropIndicatorRect = QRect(rect.left(), rect.top(), rect.width(), 0); - event->accept(); + event->acceptProposedAction(); } else { d->dropIndicatorRect = QRect(); } @@ -1957,7 +1957,7 @@ void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) case BelowItem: if (d->isIndexDropEnabled(index.parent())) { d->dropIndicatorRect = QRect(rect.left(), rect.bottom(), rect.width(), 0); - event->accept(); + event->acceptProposedAction(); } else { d->dropIndicatorRect = QRect(); } @@ -1965,7 +1965,7 @@ void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) case OnItem: if (d->isIndexDropEnabled(index)) { d->dropIndicatorRect = rect; - event->accept(); + event->acceptProposedAction(); } else { d->dropIndicatorRect = QRect(); } @@ -1973,7 +1973,7 @@ void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) case OnViewport: d->dropIndicatorRect = QRect(); if (d->isIndexDropEnabled(rootIndex())) { - event->accept(); // allow dropping in empty areas + event->acceptProposedAction(); // allow dropping in empty areas } break; } @@ -1981,7 +1981,7 @@ void QAbstractItemView::dragMoveEvent(QDragMoveEvent *event) d->dropIndicatorRect = QRect(); d->dropIndicatorPosition = OnViewport; if (d->isIndexDropEnabled(rootIndex())) { - event->accept(); // allow dropping in empty areas + event->acceptProposedAction(); // allow dropping in empty areas } } d->viewport->update(); @@ -2050,11 +2050,14 @@ void QAbstractItemView::dropEvent(QDropEvent *event) int col = -1; int row = -1; if (d->dropOn(event, &row, &col, &index)) { - if (d->model->dropMimeData(event->mimeData(), - dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction(), row, col, index)) { - if (dragDropMode() == InternalMove) - event->setDropAction(Qt::MoveAction); + const Qt::DropAction action = dragDropMode() == InternalMove ? Qt::MoveAction : event->dropAction(); + if (d->model->dropMimeData(event->mimeData(), action, row, col, index)) { + if (action != event->dropAction()) { + event->setDropAction(action); event->accept(); + } else { + event->acceptProposedAction(); + } } } stopAutoScroll(); @@ -3126,6 +3129,8 @@ void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget Q_D(QAbstractItemView); if (!d->isIndexValid(index)) return; + if (indexWidget(index) == widget) + return; if (QWidget *oldWidget = indexWidget(index)) { d->persistent.remove(oldWidget); d->removeEditor(oldWidget); diff --git a/src/widgets/itemviews/qfileiconprovider.cpp b/src/widgets/itemviews/qfileiconprovider.cpp index b12ab736f4..b6b5c63f84 100644 --- a/src/widgets/itemviews/qfileiconprovider.cpp +++ b/src/widgets/itemviews/qfileiconprovider.cpp @@ -368,8 +368,10 @@ QString QFileIconProvider::type(const QFileInfo &info) const if (info.isRoot()) return QApplication::translate("QFileDialog", "Drive"); if (info.isFile()) { - if (!info.suffix().isEmpty()) - return info.suffix() + QLatin1Char(' ') + QApplication::translate("QFileDialog", "File"); + if (!info.suffix().isEmpty()) { + //: %1 is a file name suffix, for example txt + return QApplication::translate("QFileDialog", "%1 File").arg(info.suffix()); + } return QApplication::translate("QFileDialog", "File"); } diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index a252428a81..2bf4c62593 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -1821,8 +1821,15 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi return QModelIndex(); QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root); - if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result)) + if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result)) { + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(result.row(), result.column()); + if (span.width() > 1 || span.height() > 1) { + result = d->model->sibling(span.top(), span.left(), result); + } + } return result; + } return QModelIndex(); } diff --git a/src/widgets/kernel/kernel.pri b/src/widgets/kernel/kernel.pri index 857fe4ac91..88c1e2595b 100644 --- a/src/widgets/kernel/kernel.pri +++ b/src/widgets/kernel/kernel.pri @@ -33,8 +33,8 @@ HEADERS += \ kernel/qstandardgestures_p.h \ kernel/qgesturerecognizer.h \ kernel/qgesturemanager_p.h \ - kernel/qdesktopwidget_qpa_p.h \ - kernel/qwidgetwindow_qpa_p.h \ + kernel/qdesktopwidget_p.h \ + kernel/qwidgetwindow_p.h \ kernel/qwindowcontainer_p.h SOURCES += \ @@ -60,9 +60,6 @@ SOURCES += \ kernel/qgesturemanager.cpp \ kernel/qdesktopwidget.cpp \ kernel/qwidgetsvariant.cpp \ - kernel/qapplication_qpa.cpp \ - kernel/qdesktopwidget_qpa.cpp \ - kernel/qwidget_qpa.cpp \ kernel/qwidgetwindow.cpp \ kernel/qwindowcontainer.cpp @@ -81,6 +78,6 @@ wince*: { } contains(QT_CONFIG, opengl) { - HEADERS += kernel/qopenglwidget_p.h + HEADERS += kernel/qopenglwidget.h SOURCES += kernel/qopenglwidget.cpp } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 04d398206b..2b90971398 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -57,6 +57,7 @@ #include "qstyleoption.h" #include "qstylefactory.h" #include "qtextcodec.h" +#include "qtooltip.h" #include "qtranslator.h" #include "qvariant.h" #include "qwidget.h" @@ -67,7 +68,7 @@ #include "private/qstylesheetstyle_p.h" #include "private/qstyle_p.h" #include "qmessagebox.h" -#include "qwidgetwindow_qpa_p.h" +#include "qwidgetwindow_p.h" #include <QtWidgets/qgraphicsproxywidget.h> #include <QtGui/qstylehints.h> #include <QtGui/qinputmethod.h> @@ -77,6 +78,7 @@ #endif #include "private/qkeymapper_p.h" +#include "private/qaccessiblewidgetfactory_p.h" #include <qthread.h> #include <private/qthread_p.h> @@ -93,6 +95,10 @@ #include "private/qgesturemanager_p.h" #include <qpa/qplatformfontdatabase.h> +#ifdef Q_OS_WIN +#include <QtCore/qt_windows.h> // for qt_win_display_dc() +#endif + #include "qdatetime.h" #ifdef Q_OS_WINCE @@ -184,6 +190,11 @@ QApplicationPrivate::~QApplicationPrivate() self = 0; } +void QApplicationPrivate::createEventDispatcher() +{ + QGuiApplicationPrivate::createEventDispatcher(); +} + /*! \class QApplication \brief The QApplication class manages the GUI application's control @@ -361,6 +372,14 @@ QApplicationPrivate::~QApplicationPrivate() Returns the top-level widget at the given \a point; returns 0 if there is no such widget. */ +QWidget *QApplication::topLevelAt(const QPoint &pos) +{ + if (const QWindow *window = QGuiApplication::topLevelAt(pos)) { + if (const QWidgetWindow *widgetWindow = qobject_cast<const QWidgetWindow *>(window)) + return widgetWindow->widget(); + } + return 0; +} /*! \fn QWidget *QApplication::topLevelAt(int x, int y) @@ -371,12 +390,6 @@ QApplicationPrivate::~QApplicationPrivate() 0 if there is no such widget. */ - -/* - The qt_init() and qt_cleanup() functions are implemented in the - qapplication_xyz.cpp file. -*/ - void qt_init(QApplicationPrivate *priv, int type ); void qt_init_tooltip_palette(); @@ -576,6 +589,31 @@ void QApplicationPrivate::construct() extern void qt_gui_eval_init(QCoreApplicationPrivate::Type); qt_gui_eval_init(application_type); #endif +#ifndef QT_NO_ACCESSIBILITY + // factory for accessible interfaces for widgets shipped with Qt + QAccessible::installFactory(&qAccessibleFactory); +#endif + +} + +void qt_init(QApplicationPrivate *priv, int type) +{ + Q_UNUSED(priv); + Q_UNUSED(type); + + QColormap::initialize(); + + qt_init_tooltip_palette(); + + QApplicationPrivate::initializeWidgetFontHash(); +} + +void qt_init_tooltip_palette() +{ +#ifndef QT_NO_TOOLTIP + if (const QPalette *toolTipPalette = QGuiApplicationPrivate::platformTheme()->palette(QPlatformTheme::ToolTipPalette)) + QToolTip::setPalette(*toolTipPalette); +#endif } #ifndef QT_NO_STATEMACHINE @@ -634,6 +672,91 @@ void QApplicationPrivate::initialize() is_app_running = true; // no longer starting up } +static void setPossiblePalette(const QPalette *palette, const char *className) +{ + if (palette == 0) + return; + QApplicationPrivate::setPalette_helper(*palette, className, false); +} + +void QApplicationPrivate::initializeWidgetPaletteHash() +{ + QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme(); + if (!platformTheme) + return; + qt_app_palettes_hash()->clear(); + + setPossiblePalette(platformTheme->palette(QPlatformTheme::ToolButtonPalette), "QToolButton"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::ButtonPalette), "QAbstractButton"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::CheckBoxPalette), "QCheckBox"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::RadioButtonPalette), "QRadioButton"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::HeaderPalette), "QHeaderView"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::ItemViewPalette), "QAbstractItemView"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::MessageBoxLabelPalette), "QMessageBoxLabel"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TabBarPalette), "QTabBar"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::LabelPalette), "QLabel"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::GroupBoxPalette), "QGroupBox"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuPalette), "QMenu"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuBarPalette), "QMenuBar"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextEdit"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextControl"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TextLineEditPalette), "QLineEdit"); +} + +void QApplicationPrivate::initializeWidgetFontHash() +{ + const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); + if (!theme) + return; + FontHash *fontHash = qt_app_fonts_hash(); + fontHash->clear(); + + if (const QFont *font = theme->font(QPlatformTheme::MenuFont)) + fontHash->insert(QByteArrayLiteral("QMenu"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MenuBarFont)) + fontHash->insert(QByteArrayLiteral("QMenuBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MenuItemFont)) + fontHash->insert(QByteArrayLiteral("QMenuItem"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MessageBoxFont)) + fontHash->insert(QByteArrayLiteral("QMessageBox"), *font); + if (const QFont *font = theme->font(QPlatformTheme::LabelFont)) + fontHash->insert(QByteArrayLiteral("QLabel"), *font); + if (const QFont *font = theme->font(QPlatformTheme::TipLabelFont)) + fontHash->insert(QByteArrayLiteral("QTipLabel"), *font); + if (const QFont *font = theme->font(QPlatformTheme::TitleBarFont)) + fontHash->insert(QByteArrayLiteral("QTitleBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::StatusBarFont)) + fontHash->insert(QByteArrayLiteral("QStatusBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MdiSubWindowTitleFont)) + fontHash->insert(QByteArrayLiteral("QMdiSubWindowTitleBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::DockWidgetTitleFont)) + fontHash->insert(QByteArrayLiteral("QDockWidgetTitle"), *font); + if (const QFont *font = theme->font(QPlatformTheme::PushButtonFont)) + fontHash->insert(QByteArrayLiteral("QPushButton"), *font); + if (const QFont *font = theme->font(QPlatformTheme::CheckBoxFont)) + fontHash->insert(QByteArrayLiteral("QCheckBox"), *font); + if (const QFont *font = theme->font(QPlatformTheme::RadioButtonFont)) + fontHash->insert(QByteArrayLiteral("QRadioButton"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ToolButtonFont)) + fontHash->insert(QByteArrayLiteral("QToolButton"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ItemViewFont)) + fontHash->insert(QByteArrayLiteral("QAbstractItemView"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ListViewFont)) + fontHash->insert(QByteArrayLiteral("QListViewFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::HeaderViewFont)) + fontHash->insert(QByteArrayLiteral("QHeaderViewFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ListBoxFont)) + fontHash->insert(QByteArrayLiteral("QListBox"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ComboMenuItemFont)) + fontHash->insert(QByteArrayLiteral("QComboMenuItemFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ComboLineEditFont)) + fontHash->insert(QByteArrayLiteral("QComboLineEditFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::SmallFont)) + fontHash->insert(QByteArrayLiteral("QSmallFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MiniFont)) + fontHash->insert(QByteArrayLiteral("QMiniFont"), *font); +} + /***************************************************************************** Functions returning the active popup and modal widgets. *****************************************************************************/ @@ -757,6 +880,32 @@ QApplication::~QApplication() #endif } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) +// #fixme: Remove. +static HDC displayDC = 0; // display device context + +Q_WIDGETS_EXPORT HDC qt_win_display_dc() // get display DC +{ + Q_ASSERT(qApp && qApp->thread() == QThread::currentThread()); + if (!displayDC) + displayDC = GetDC(0); + return displayDC; +} +#endif + +void qt_cleanup() +{ + QPixmapCache::clear(); + QColormap::cleanup(); + + QApplicationPrivate::active_window = 0; //### this should not be necessary +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + if (displayDC) { + ReleaseDC(0, displayDC); + displayDC = 0; + } +#endif +} /*! \fn QWidget *QApplication::widgetAt(const QPoint &point) @@ -2063,6 +2212,39 @@ void QApplication::setActiveWindow(QWidget* act) } } +QWidget *qt_tlw_for_window(QWindow *wnd) +{ + // QTBUG-32177, wnd might be a QQuickView embedded via window container. + while (wnd && !wnd->isTopLevel()) { + QWindow *parent = wnd->parent(); + // Don't end up in windows not belonging to this application + if (parent && parent->type() != Qt::ForeignWindow) + wnd = wnd->parent(); + else + break; + } + if (wnd) + foreach (QWidget *tlw, qApp->topLevelWidgets()) + if (tlw->windowHandle() == wnd) + return tlw; + return 0; +} + +void QApplicationPrivate::notifyActiveWindowChange(QWindow *previous) +{ + Q_UNUSED(previous); + QWindow *wnd = QGuiApplicationPrivate::focus_window; + if (inPopupMode()) // some delayed focus event to ignore + return; + QWidget *tlw = qt_tlw_for_window(wnd); + QApplication::setActiveWindow(tlw); + // QTBUG-37126, Active X controls may set the focus on native child widgets. + if (wnd && tlw && wnd != tlw->windowHandle()) { + if (QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(wnd)) + widgetWindow->widget()->setFocus(Qt::ActiveWindowFocusReason); + } +} + /*!internal * Helper function that returns the new focus widget, but does not set the focus reason. * Returns 0 if a new focus widget could not be found. @@ -2431,6 +2613,44 @@ bool QApplicationPrivate::tryModalHelper(QWidget *widget, QWidget **rettop) return !isBlockedByModal(widget->window()); } +bool qt_try_modal(QWidget *widget, QEvent::Type type) +{ + QWidget * top = 0; + + if (QApplicationPrivate::tryModalHelper(widget, &top)) + return true; + + bool block_event = false; + + switch (type) { +#if 0 + case QEvent::Focus: + if (!static_cast<QWSFocusEvent*>(event)->simpleData.get_focus) + break; + // drop through +#endif + case QEvent::MouseButtonPress: // disallow mouse/key events + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::KeyPress: + case QEvent::KeyRelease: + block_event = true; + break; + default: + break; + } + + if (block_event && top && top->parentWidget() == 0) + top->raise(); + + return !block_event; +} + +bool QApplicationPrivate::modalState() +{ + return !self->modalWindowList.isEmpty(); +} + /* \internal */ @@ -3168,7 +3388,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) tablet->device(), tablet->pointerType(), tablet->pressure(), tablet->xTilt(), tablet->yTilt(), tablet->tangentialPressure(), tablet->rotation(), tablet->z(), - tablet->modifiers(), tablet->uniqueId()); + tablet->modifiers(), tablet->uniqueId(), tablet->button(), tablet->buttons()); te.spont = e->spontaneous(); res = d->notify_helper(w, w == receiver ? tablet : &te); eventAccepted = ((w == receiver) ? tablet : &te)->isAccepted(); @@ -3497,7 +3717,7 @@ bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) // deliver the event bool consumed = receiver->event(e); - e->spont = false; + QCoreApplicationPrivate::setEventSpontaneous(e, false); return consumed; } @@ -3506,6 +3726,123 @@ bool QApplicationPrivate::inPopupMode() return QApplicationPrivate::popupWidgets != 0; } +static void ungrabKeyboardForPopup(QWidget *popup) +{ + if (QWidget::keyboardGrabber()) + qt_widget_private(QWidget::keyboardGrabber())->stealKeyboardGrab(true); + else + qt_widget_private(popup)->stealKeyboardGrab(false); +} + +static void ungrabMouseForPopup(QWidget *popup) +{ + if (QWidget::mouseGrabber()) + qt_widget_private(QWidget::mouseGrabber())->stealMouseGrab(true); + else + qt_widget_private(popup)->stealMouseGrab(false); +} + +static bool popupGrabOk; + +static void grabForPopup(QWidget *popup) +{ + Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created)); + popupGrabOk = qt_widget_private(popup)->stealKeyboardGrab(true); + if (popupGrabOk) { + popupGrabOk = qt_widget_private(popup)->stealMouseGrab(true); + if (!popupGrabOk) { + // transfer grab back to the keyboard grabber if any + ungrabKeyboardForPopup(popup); + } + } +} + +extern QWidget *qt_button_down; +extern QWidget *qt_popup_down; +extern bool qt_replay_popup_mouse_event; + +void QApplicationPrivate::closePopup(QWidget *popup) +{ + if (!popupWidgets) + return; + popupWidgets->removeAll(popup); + + if (popup == qt_popup_down) { + qt_button_down = 0; + qt_popup_down = 0; + } + + if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup + delete QApplicationPrivate::popupWidgets; + QApplicationPrivate::popupWidgets = 0; + + if (popupGrabOk) { + popupGrabOk = false; + + if (popup->geometry().contains(QPoint(QGuiApplicationPrivate::mousePressX, + QGuiApplicationPrivate::mousePressY)) + || popup->testAttribute(Qt::WA_NoMouseReplay)) { + // mouse release event or inside + qt_replay_popup_mouse_event = false; + } else { // mouse press event + qt_replay_popup_mouse_event = true; + } + + // transfer grab back to mouse grabber if any, otherwise release the grab + ungrabMouseForPopup(popup); + + // transfer grab back to keyboard grabber if any, otherwise release the grab + ungrabKeyboardForPopup(popup); + } + + if (active_window) { + if (QWidget *fw = active_window->focusWidget()) { + if (fw != QApplication::focusWidget()) { + fw->setFocus(Qt::PopupFocusReason); + } else { + QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason); + QCoreApplication::sendEvent(fw, &e); + } + } + } + + } else { + // A popup was closed, so the previous popup gets the focus. + QWidget* aw = QApplicationPrivate::popupWidgets->last(); + if (QWidget *fw = aw->focusWidget()) + fw->setFocus(Qt::PopupFocusReason); + + if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard + grabForPopup(aw); + } + +} + +int openPopupCount = 0; + +void QApplicationPrivate::openPopup(QWidget *popup) +{ + openPopupCount++; + if (!popupWidgets) // create list + popupWidgets = new QWidgetList; + popupWidgets->append(popup); // add to end of list + + if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard + grabForPopup(popup); + + // popups are not focus-handled by the window system (the first + // popup grabbed the keyboard), so we have to do that manually: A + // new popup gets the focus + if (popup->focusWidget()) { + popup->focusWidget()->setFocus(Qt::PopupFocusReason); + } else if (popupWidgets->count() == 1) { // this was the first popup + if (QWidget *fw = QApplication::focusWidget()) { + QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); + QApplication::sendEvent(fw, &e); + } + } +} + #ifdef QT_KEYPAD_NAVIGATION /*! Sets the kind of focus navigation Qt should use to \a mode. @@ -3610,6 +3947,18 @@ bool QApplication::keypadNavigationEnabled() window must not be hidden (i.e. not have hide() called on it, but be visible in some sort of way) in order for this to work. */ +void QApplication::alert(QWidget *widget, int duration) +{ + if (widget) { + if (widget->window()->isActiveWindow() && !(widget->window()->windowState() & Qt::WindowMinimized)) + return; + if (QWindow *window= QApplicationPrivate::windowForWidget(widget)) + window->alert(duration); + } else { + foreach (QWidget *topLevel, topLevelWidgets()) + QApplication::alert(topLevel, duration); + } +} /*! \property QApplication::cursorFlashTime @@ -3700,6 +4049,38 @@ int QApplication::keyboardInputInterval() By default, this property has a value of 3. */ +#ifndef QT_NO_WHEELEVENT +int QApplication::wheelScrollLines() +{ + return QApplicationPrivate::wheel_scroll_lines; +} + +void QApplication::setWheelScrollLines(int lines) +{ + QApplicationPrivate::wheel_scroll_lines = lines; +} +#endif + +static inline int uiEffectToFlag(Qt::UIEffect effect) +{ + switch (effect) { + case Qt::UI_General: + return QPlatformTheme::GeneralUiEffect; + case Qt::UI_AnimateMenu: + return QPlatformTheme::AnimateMenuUiEffect; + case Qt::UI_FadeMenu: + return QPlatformTheme::FadeMenuUiEffect; + case Qt::UI_AnimateCombo: + return QPlatformTheme::AnimateComboUiEffect; + case Qt::UI_AnimateTooltip: + return QPlatformTheme::AnimateTooltipUiEffect; + case Qt::UI_FadeTooltip: + return QPlatformTheme::FadeTooltipUiEffect; + case Qt::UI_AnimateToolBox: + return QPlatformTheme::AnimateToolBoxUiEffect; + } + return 0; +} /*! \fn void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) @@ -3712,6 +4093,19 @@ int QApplication::keyboardInputInterval() \sa isEffectEnabled(), Qt::UIEffect, setDesktopSettingsAware() */ +void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) +{ + int effectFlags = uiEffectToFlag(effect); + if (enable) { + if (effectFlags & QPlatformTheme::FadeMenuUiEffect) + effectFlags |= QPlatformTheme::AnimateMenuUiEffect; + if (effectFlags & QPlatformTheme::FadeTooltipUiEffect) + effectFlags |= QPlatformTheme::AnimateTooltipUiEffect; + QApplicationPrivate::enabledAnimations |= effectFlags; + } else { + QApplicationPrivate::enabledAnimations &= ~effectFlags; + } +} /*! \fn bool QApplication::isEffectEnabled(Qt::UIEffect effect) @@ -3726,6 +4120,12 @@ int QApplication::keyboardInputInterval() \sa setEffectEnabled(), Qt::UIEffect */ +bool QApplication::isEffectEnabled(Qt::UIEffect effect) +{ + return QColormap::instance().depth() >= 16 + && (QApplicationPrivate::enabledAnimations & QPlatformTheme::GeneralUiEffect) + && (QApplicationPrivate::enabledAnimations & uiEffectToFlag(effect)); +} /*! \fn void QApplication::beep() @@ -3733,6 +4133,10 @@ int QApplication::keyboardInputInterval() Sounds the bell, using the default volume and sound. The function is \e not available in Qt for Embedded Linux. */ +void QApplication::beep() +{ + QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(), "beep"); +} /*! \macro qApp @@ -3845,11 +4249,19 @@ void QApplicationPrivate::initializeMultitouch() initializeMultitouch_sys(); } +void QApplicationPrivate::initializeMultitouch_sys() +{ +} + void QApplicationPrivate::cleanupMultitouch() { cleanupMultitouch_sys(); } +void QApplicationPrivate::cleanupMultitouch_sys() +{ +} + QWidget *QApplicationPrivate::findClosestTouchPointTarget(QTouchDevice *device, const QPointF &screenPos) { int closestTouchPointId = -1; diff --git a/src/widgets/kernel/qapplication.h b/src/widgets/kernel/qapplication.h index e72fe29bdb..08a450ab32 100644 --- a/src/widgets/kernel/qapplication.h +++ b/src/widgets/kernel/qapplication.h @@ -228,7 +228,6 @@ private: friend class QWidget; friend class QWidgetPrivate; friend class QWidgetWindow; - friend class QETWidget; friend class QTranslator; friend class QWidgetAnimator; #ifndef QT_NO_SHORTCUT diff --git a/src/widgets/kernel/qapplication_qpa.cpp b/src/widgets/kernel/qapplication_qpa.cpp deleted file mode 100644 index 5893c52e1b..0000000000 --- a/src/widgets/kernel/qapplication_qpa.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qapplication_p.h" -#include "qcolormap.h" -#include "qpalette.h" -#include "qpixmapcache.h" -#ifndef QT_NO_CURSOR -#include "private/qcursor_p.h" -#endif -#include "qscreen.h" - -#include "private/qwidget_p.h" -#include "private/qevent_p.h" - -#include <qpa/qplatformintegrationfactory_p.h> -#include <qdesktopwidget.h> -#include <qpa/qplatformcursor.h> -#include <qpa/qplatformtheme.h> -#include <qpa/qplatformwindow.h> - -#include <qdebug.h> -#include <qpa/qwindowsysteminterface.h> -#include <qpa/qwindowsysteminterface_p.h> -#include <qpa/qplatformintegration.h> - -#include "qdesktopwidget_qpa_p.h" -#include "qwidgetwindow_qpa_p.h" -#include "qtooltip.h" - -#ifdef Q_OS_WIN -# include <QtCore/qt_windows.h> // for qt_win_display_dc() -#endif - -QT_BEGIN_NAMESPACE - -static QString appFont; -static bool popupGrabOk; -extern QWidget *qt_button_down; -extern QWidget *qt_popup_down; -extern bool qt_replay_popup_mouse_event; -int openPopupCount = 0; -extern QPointer<QWidget> qt_last_mouse_receiver; - -void QApplicationPrivate::createEventDispatcher() -{ - QGuiApplicationPrivate::createEventDispatcher(); -} - -bool qt_try_modal(QWidget *widget, QEvent::Type type) -{ - QWidget * top = 0; - - if (QApplicationPrivate::tryModalHelper(widget, &top)) - return true; - - bool block_event = false; - - switch (type) { -#if 0 - case QEvent::Focus: - if (!static_cast<QWSFocusEvent*>(event)->simpleData.get_focus) - break; - // drop through -#endif - case QEvent::MouseButtonPress: // disallow mouse/key events - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - case QEvent::KeyPress: - case QEvent::KeyRelease: - block_event = true; - break; - default: - break; - } - - if (block_event && top && top->parentWidget() == 0) - top->raise(); - - return !block_event; -} - -bool QApplicationPrivate::modalState() -{ - return !self->modalWindowList.isEmpty(); -} - -QWidget *qt_tlw_for_window(QWindow *wnd) -{ - // QTBUG-32177, wnd might be a QQuickView embedded via window container. - while (wnd && !wnd->isTopLevel()) { - QWindow *parent = wnd->parent(); - // Don't end up in windows not belonging to this application - if (parent && parent->type() != Qt::ForeignWindow) - wnd = wnd->parent(); - else - break; - } - if (wnd) - foreach (QWidget *tlw, qApp->topLevelWidgets()) - if (tlw->windowHandle() == wnd) - return tlw; - return 0; -} - -void QApplicationPrivate::notifyActiveWindowChange(QWindow *previous) -{ - Q_UNUSED(previous); - QWindow *wnd = QGuiApplicationPrivate::focus_window; - if (inPopupMode()) // some delayed focus event to ignore - return; - QWidget *tlw = qt_tlw_for_window(wnd); - QApplication::setActiveWindow(tlw); -} - -static void ungrabKeyboardForPopup(QWidget *popup) -{ - if (QWidget::keyboardGrabber()) - qt_widget_private(QWidget::keyboardGrabber())->stealKeyboardGrab(true); - else - qt_widget_private(popup)->stealKeyboardGrab(false); -} - -static void ungrabMouseForPopup(QWidget *popup) -{ - if (QWidget::mouseGrabber()) - qt_widget_private(QWidget::mouseGrabber())->stealMouseGrab(true); - else - qt_widget_private(popup)->stealMouseGrab(false); -} - -static void grabForPopup(QWidget *popup) -{ - Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created)); - popupGrabOk = qt_widget_private(popup)->stealKeyboardGrab(true); - if (popupGrabOk) { - popupGrabOk = qt_widget_private(popup)->stealMouseGrab(true); - if (!popupGrabOk) { - // transfer grab back to the keyboard grabber if any - ungrabKeyboardForPopup(popup); - } - } -} - -void QApplicationPrivate::closePopup(QWidget *popup) -{ - if (!popupWidgets) - return; - popupWidgets->removeAll(popup); - - if (popup == qt_popup_down) { - qt_button_down = 0; - qt_popup_down = 0; - } - - if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup - delete QApplicationPrivate::popupWidgets; - QApplicationPrivate::popupWidgets = 0; - - if (popupGrabOk) { - popupGrabOk = false; - - if (popup->geometry().contains(QPoint(QGuiApplicationPrivate::mousePressX, - QGuiApplicationPrivate::mousePressY)) - || popup->testAttribute(Qt::WA_NoMouseReplay)) { - // mouse release event or inside - qt_replay_popup_mouse_event = false; - } else { // mouse press event - qt_replay_popup_mouse_event = true; - } - - // transfer grab back to mouse grabber if any, otherwise release the grab - ungrabMouseForPopup(popup); - - // transfer grab back to keyboard grabber if any, otherwise release the grab - ungrabKeyboardForPopup(popup); - } - - if (active_window) { - if (QWidget *fw = active_window->focusWidget()) { - if (fw != QApplication::focusWidget()) { - fw->setFocus(Qt::PopupFocusReason); - } else { - QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason); - QCoreApplication::sendEvent(fw, &e); - } - } - } - - } else { - // A popup was closed, so the previous popup gets the focus. - QWidget* aw = QApplicationPrivate::popupWidgets->last(); - if (QWidget *fw = aw->focusWidget()) - fw->setFocus(Qt::PopupFocusReason); - - if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard - grabForPopup(aw); - } - -} - -void QApplicationPrivate::openPopup(QWidget *popup) -{ - openPopupCount++; - if (!popupWidgets) // create list - popupWidgets = new QWidgetList; - popupWidgets->append(popup); // add to end of list - - if (QApplicationPrivate::popupWidgets->count() == 1) // grab mouse/keyboard - grabForPopup(popup); - - // popups are not focus-handled by the window system (the first - // popup grabbed the keyboard), so we have to do that manually: A - // new popup gets the focus - if (popup->focusWidget()) { - popup->focusWidget()->setFocus(Qt::PopupFocusReason); - } else if (popupWidgets->count() == 1) { // this was the first popup - if (QWidget *fw = QApplication::focusWidget()) { - QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); - QApplication::sendEvent(fw, &e); - } - } -} - -void QApplicationPrivate::initializeMultitouch_sys() -{ -} - -void QApplicationPrivate::cleanupMultitouch_sys() -{ -} - -static void setPossiblePalette(const QPalette *palette, const char *className) -{ - if (palette == 0) - return; - QApplicationPrivate::setPalette_helper(*palette, className, false); -} - - -void QApplicationPrivate::initializeWidgetPaletteHash() -{ - QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme(); - if (!platformTheme) - return; - qt_app_palettes_hash()->clear(); - - setPossiblePalette(platformTheme->palette(QPlatformTheme::ToolButtonPalette), "QToolButton"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::ButtonPalette), "QAbstractButton"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::CheckBoxPalette), "QCheckBox"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::RadioButtonPalette), "QRadioButton"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::HeaderPalette), "QHeaderView"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::ItemViewPalette), "QAbstractItemView"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::MessageBoxLabelPalette), "QMessageBoxLabel"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::TabBarPalette), "QTabBar"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::LabelPalette), "QLabel"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::GroupBoxPalette), "QGroupBox"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuPalette), "QMenu"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuBarPalette), "QMenuBar"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextEdit"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextControl"); - setPossiblePalette(platformTheme->palette(QPlatformTheme::TextLineEditPalette), "QLineEdit"); -} - -void QApplicationPrivate::initializeWidgetFontHash() -{ - const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); - if (!theme) - return; - FontHash *fontHash = qt_app_fonts_hash(); - fontHash->clear(); - - if (const QFont *font = theme->font(QPlatformTheme::MenuFont)) - fontHash->insert(QByteArrayLiteral("QMenu"), *font); - if (const QFont *font = theme->font(QPlatformTheme::MenuBarFont)) - fontHash->insert(QByteArrayLiteral("QMenuBar"), *font); - if (const QFont *font = theme->font(QPlatformTheme::MenuItemFont)) - fontHash->insert(QByteArrayLiteral("QMenuItem"), *font); - if (const QFont *font = theme->font(QPlatformTheme::MessageBoxFont)) - fontHash->insert(QByteArrayLiteral("QMessageBox"), *font); - if (const QFont *font = theme->font(QPlatformTheme::LabelFont)) - fontHash->insert(QByteArrayLiteral("QLabel"), *font); - if (const QFont *font = theme->font(QPlatformTheme::TipLabelFont)) - fontHash->insert(QByteArrayLiteral("QTipLabel"), *font); - if (const QFont *font = theme->font(QPlatformTheme::TitleBarFont)) - fontHash->insert(QByteArrayLiteral("QTitleBar"), *font); - if (const QFont *font = theme->font(QPlatformTheme::StatusBarFont)) - fontHash->insert(QByteArrayLiteral("QStatusBar"), *font); - if (const QFont *font = theme->font(QPlatformTheme::MdiSubWindowTitleFont)) - fontHash->insert(QByteArrayLiteral("QMdiSubWindowTitleBar"), *font); - if (const QFont *font = theme->font(QPlatformTheme::DockWidgetTitleFont)) - fontHash->insert(QByteArrayLiteral("QDockWidgetTitle"), *font); - if (const QFont *font = theme->font(QPlatformTheme::PushButtonFont)) - fontHash->insert(QByteArrayLiteral("QPushButton"), *font); - if (const QFont *font = theme->font(QPlatformTheme::CheckBoxFont)) - fontHash->insert(QByteArrayLiteral("QCheckBox"), *font); - if (const QFont *font = theme->font(QPlatformTheme::RadioButtonFont)) - fontHash->insert(QByteArrayLiteral("QRadioButton"), *font); - if (const QFont *font = theme->font(QPlatformTheme::ToolButtonFont)) - fontHash->insert(QByteArrayLiteral("QToolButton"), *font); - if (const QFont *font = theme->font(QPlatformTheme::ItemViewFont)) - fontHash->insert(QByteArrayLiteral("QAbstractItemView"), *font); - if (const QFont *font = theme->font(QPlatformTheme::ListViewFont)) - fontHash->insert(QByteArrayLiteral("QListViewFont"), *font); - if (const QFont *font = theme->font(QPlatformTheme::HeaderViewFont)) - fontHash->insert(QByteArrayLiteral("QHeaderViewFont"), *font); - if (const QFont *font = theme->font(QPlatformTheme::ListBoxFont)) - fontHash->insert(QByteArrayLiteral("QListBox"), *font); - if (const QFont *font = theme->font(QPlatformTheme::ComboMenuItemFont)) - fontHash->insert(QByteArrayLiteral("QComboMenuItemFont"), *font); - if (const QFont *font = theme->font(QPlatformTheme::ComboLineEditFont)) - fontHash->insert(QByteArrayLiteral("QComboLineEditFont"), *font); - if (const QFont *font = theme->font(QPlatformTheme::SmallFont)) - fontHash->insert(QByteArrayLiteral("QSmallFont"), *font); - if (const QFont *font = theme->font(QPlatformTheme::MiniFont)) - fontHash->insert(QByteArrayLiteral("QMiniFont"), *font); -} - -#ifndef QT_NO_WHEELEVENT -void QApplication::setWheelScrollLines(int lines) -{ - QApplicationPrivate::wheel_scroll_lines = lines; -} - -int QApplication::wheelScrollLines() -{ - return QApplicationPrivate::wheel_scroll_lines; -} -#endif - -static inline int uiEffectToFlag(Qt::UIEffect effect) -{ - switch (effect) { - case Qt::UI_General: - return QPlatformTheme::GeneralUiEffect; - case Qt::UI_AnimateMenu: - return QPlatformTheme::AnimateMenuUiEffect; - case Qt::UI_FadeMenu: - return QPlatformTheme::FadeMenuUiEffect; - case Qt::UI_AnimateCombo: - return QPlatformTheme::AnimateComboUiEffect; - case Qt::UI_AnimateTooltip: - return QPlatformTheme::AnimateTooltipUiEffect; - case Qt::UI_FadeTooltip: - return QPlatformTheme::FadeTooltipUiEffect; - case Qt::UI_AnimateToolBox: - return QPlatformTheme::AnimateToolBoxUiEffect; - } - return 0; -} - -void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) -{ - int effectFlags = uiEffectToFlag(effect); - if (enable) { - if (effectFlags & QPlatformTheme::FadeMenuUiEffect) - effectFlags |= QPlatformTheme::AnimateMenuUiEffect; - if (effectFlags & QPlatformTheme::FadeTooltipUiEffect) - effectFlags |= QPlatformTheme::AnimateTooltipUiEffect; - QApplicationPrivate::enabledAnimations |= effectFlags; - } else { - QApplicationPrivate::enabledAnimations &= ~effectFlags; - } -} - -bool QApplication::isEffectEnabled(Qt::UIEffect effect) -{ - return QColormap::instance().depth() >= 16 - && (QApplicationPrivate::enabledAnimations & QPlatformTheme::GeneralUiEffect) - && (QApplicationPrivate::enabledAnimations & uiEffectToFlag(effect)); -} - -QWidget *QApplication::topLevelAt(const QPoint &pos) -{ - QList<QScreen *> screens = QGuiApplication::screens(); - QList<QScreen *>::const_iterator screen = screens.constBegin(); - QList<QScreen *>::const_iterator end = screens.constEnd(); - - while (screen != end) { - if ((*screen)->geometry().contains(pos)) { - QWidgetWindow *w = qobject_cast<QWidgetWindow *>((*screen)->handle()->topLevelAt(pos)); - return w ? w->widget() : 0; - } - ++screen; - } - return 0; -} - -void QApplication::beep() -{ - QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(), "beep"); -} - -void QApplication::alert(QWidget *widget, int duration) -{ - if (widget) { - if (widget->window()->isActiveWindow() && !(widget->window()->windowState() & Qt::WindowMinimized)) - return; - if (QWindow *window= QApplicationPrivate::windowForWidget(widget)) - window->alert(duration); - } else { - foreach (QWidget *topLevel, topLevelWidgets()) - QApplication::alert(topLevel, duration); - } -} - -void qt_init_tooltip_palette() -{ -#ifndef QT_NO_TOOLTIP - if (const QPalette *toolTipPalette = QGuiApplicationPrivate::platformTheme()->palette(QPlatformTheme::ToolTipPalette)) - QToolTip::setPalette(*toolTipPalette); -#endif -} - -void qt_init(QApplicationPrivate *priv, int type) -{ - Q_UNUSED(priv); - Q_UNUSED(type); - - QColormap::initialize(); - - qt_init_tooltip_palette(); - - QApplicationPrivate::initializeWidgetFontHash(); -} - -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) -// #fixme: Remove. -static HDC displayDC = 0; // display device context - -Q_WIDGETS_EXPORT HDC qt_win_display_dc() // get display DC -{ - Q_ASSERT(qApp && qApp->thread() == QThread::currentThread()); - if (!displayDC) - displayDC = GetDC(0); - return displayDC; -} -#endif - -void qt_cleanup() -{ - QPixmapCache::clear(); - QColormap::cleanup(); - - QApplicationPrivate::active_window = 0; //### this should not be necessary -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - if (displayDC) { - ReleaseDC(0, displayDC); - displayDC = 0; - } -#endif -} - - -QT_END_NAMESPACE diff --git a/src/widgets/kernel/qdesktopwidget.cpp b/src/widgets/kernel/qdesktopwidget.cpp index 649978a912..3975e423a0 100644 --- a/src/widgets/kernel/qdesktopwidget.cpp +++ b/src/widgets/kernel/qdesktopwidget.cpp @@ -41,6 +41,8 @@ #include "qglobal.h" #include "qdesktopwidget.h" +#include "qdesktopwidget_p.h" +#include "qscreen.h" #include "qwidget_p.h" QT_BEGIN_NAMESPACE @@ -72,5 +74,145 @@ const QRect QDesktopWidget::availableGeometry(const QWidget *widget) const return rect; } +void QDesktopWidgetPrivate::_q_updateScreens() +{ + Q_Q(QDesktopWidget); + const QList<QScreen *> screenList = QGuiApplication::screens(); + const int targetLength = screenList.length(); + const int oldLength = screens.length(); + int currentLength = oldLength; + + // Add or remove screen widgets as necessary + if(currentLength > targetLength) { + QDesktopScreenWidget *screen; + while (currentLength-- > targetLength) { + screen = screens.takeLast(); + delete screen; + } + } + else if (currentLength < targetLength) { + while (currentLength < targetLength) { + QScreen *qScreen = screenList.at(currentLength); + QDesktopScreenWidget *screenWidget = new QDesktopScreenWidget(currentLength++); + screenWidget->setGeometry(qScreen->geometry()); + QObject::connect(qScreen, SIGNAL(geometryChanged(QRect)), + q, SLOT(_q_updateScreens()), Qt::QueuedConnection); + QObject::connect(qScreen, SIGNAL(destroyed()), + q, SLOT(_q_updateScreens()), Qt::QueuedConnection); + screens.append(screenWidget); + } + } + + QRegion virtualGeometry; + + // update the geometry of each screen widget, determine virtual geometry + // and emit change signals afterwards. + QList<int> changedScreens; + for (int i = 0; i < screens.length(); i++) { + const QRect screenGeometry = screenList.at(i)->geometry(); + if (screenGeometry != screens.at(i)->geometry()) { + screens.at(i)->setGeometry(screenGeometry); + changedScreens.push_back(i); + } + virtualGeometry += screenGeometry; + } + + q->setGeometry(virtualGeometry.boundingRect()); + + if (oldLength != targetLength) + emit q->screenCountChanged(targetLength); + + foreach (int changedScreen, changedScreens) { + emit q->resized(changedScreen); + emit q->workAreaResized(changedScreen); + } +} + +QDesktopWidget::QDesktopWidget() + : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop) +{ + Q_D(QDesktopWidget); + setObjectName(QLatin1String("desktop")); + d->_q_updateScreens(); + connect(qApp, SIGNAL(screenAdded(QScreen*)), this, SLOT(_q_updateScreens())); +} + +QDesktopWidget::~QDesktopWidget() +{ +} + +bool QDesktopWidget::isVirtualDesktop() const +{ + return QGuiApplication::primaryScreen()->virtualSiblings().size() > 1; +} + +int QDesktopWidget::primaryScreen() const +{ + return 0; +} + +int QDesktopWidget::numScreens() const +{ + return qMax(QGuiApplication::screens().size(), 1); +} + +QWidget *QDesktopWidget::screen(int screen) +{ + Q_D(QDesktopWidget); + if (screen < 0 || screen >= d->screens.length()) + return d->screens.at(0); + return d->screens.at(screen); +} + +const QRect QDesktopWidget::availableGeometry(int screenNo) const +{ + QList<QScreen *> screens = QGuiApplication::screens(); + if (screenNo == -1) + screenNo = 0; + if (screenNo < 0 || screenNo >= screens.size()) + return QRect(); + else + return screens.at(screenNo)->availableGeometry(); +} + +const QRect QDesktopWidget::screenGeometry(int screenNo) const +{ + QList<QScreen *> screens = QGuiApplication::screens(); + if (screenNo == -1) + screenNo = 0; + if (screenNo < 0 || screenNo >= screens.size()) + return QRect(); + else + return screens.at(screenNo)->geometry(); +} + +int QDesktopWidget::screenNumber(const QWidget *w) const +{ + if (!w) + return 0; + + QRect frame = w->frameGeometry(); + if (!w->isWindow()) + frame.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); + const QPoint midpoint = (frame.topLeft() + frame.bottomRight()) / 2; + return screenNumber(midpoint); +} + +int QDesktopWidget::screenNumber(const QPoint &p) const +{ + QList<QScreen *> screens = QGuiApplication::screens(); + + for (int i = 0; i < screens.size(); ++i) + if (screens.at(i)->geometry().contains(p)) + return i; + + return primaryScreen(); //even better would be closest screen +} + +void QDesktopWidget::resizeEvent(QResizeEvent *) +{ +} + QT_END_NAMESPACE +#include "moc_qdesktopwidget.cpp" diff --git a/src/widgets/kernel/qdesktopwidget_qpa_p.h b/src/widgets/kernel/qdesktopwidget_p.h index f461869f1b..160807cf23 100644 --- a/src/widgets/kernel/qdesktopwidget_qpa_p.h +++ b/src/widgets/kernel/qdesktopwidget_p.h @@ -50,8 +50,8 @@ // We mean it. // -#ifndef QDESKTOPWIDGET_QPA_P_H -#define QDESKTOPWIDGET_QPA_P_H +#ifndef QDESKTOPWIDGET_P_H +#define QDESKTOPWIDGET_P_H #include "QDesktopWidget" #include "private/qwidget_p.h" diff --git a/src/widgets/kernel/qdesktopwidget_qpa.cpp b/src/widgets/kernel/qdesktopwidget_qpa.cpp deleted file mode 100644 index 015573dfbe..0000000000 --- a/src/widgets/kernel/qdesktopwidget_qpa.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdesktopwidget.h" -#include "qscreen.h" -#include "private/qapplication_p.h" -#include <QWidget> -#include "private/qwidget_p.h" -#include "private/qdesktopwidget_qpa_p.h" -QT_BEGIN_NAMESPACE - -QT_USE_NAMESPACE - -void QDesktopWidgetPrivate::_q_updateScreens() -{ - Q_Q(QDesktopWidget); - const QList<QScreen *> screenList = QGuiApplication::screens(); - const int targetLength = screenList.length(); - const int oldLength = screens.length(); - int currentLength = oldLength; - - // Add or remove screen widgets as necessary - if(currentLength > targetLength) { - QDesktopScreenWidget *screen; - while (currentLength-- > targetLength) { - screen = screens.takeLast(); - delete screen; - } - } - else if (currentLength < targetLength) { - while (currentLength < targetLength) { - QScreen *qScreen = screenList.at(currentLength); - QDesktopScreenWidget *screenWidget = new QDesktopScreenWidget(currentLength++); - screenWidget->setGeometry(qScreen->geometry()); - QObject::connect(qScreen, SIGNAL(geometryChanged(QRect)), - q, SLOT(_q_updateScreens()), Qt::QueuedConnection); - QObject::connect(qScreen, SIGNAL(destroyed()), - q, SLOT(_q_updateScreens()), Qt::QueuedConnection); - screens.append(screenWidget); - } - } - - QRegion virtualGeometry; - - // update the geometry of each screen widget, determine virtual geometry - // and emit change signals afterwards. - QList<int> changedScreens; - for (int i = 0; i < screens.length(); i++) { - const QRect screenGeometry = screenList.at(i)->geometry(); - if (screenGeometry != screens.at(i)->geometry()) { - screens.at(i)->setGeometry(screenGeometry); - changedScreens.push_back(i); - } - virtualGeometry += screenGeometry; - } - - q->setGeometry(virtualGeometry.boundingRect()); - - if (oldLength != targetLength) - emit q->screenCountChanged(targetLength); - - foreach (int changedScreen, changedScreens) { - emit q->resized(changedScreen); - emit q->workAreaResized(changedScreen); - } -} - -QDesktopWidget::QDesktopWidget() - : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop) -{ - Q_D(QDesktopWidget); - setObjectName(QLatin1String("desktop")); - d->_q_updateScreens(); - connect(qApp, SIGNAL(screenAdded(QScreen*)), this, SLOT(_q_updateScreens())); -} - -QDesktopWidget::~QDesktopWidget() -{ -} - -bool QDesktopWidget::isVirtualDesktop() const -{ - return QGuiApplication::primaryScreen()->virtualSiblings().size() > 1; -} - -int QDesktopWidget::primaryScreen() const -{ - return 0; -} - -int QDesktopWidget::numScreens() const -{ - return qMax(QGuiApplication::screens().size(), 1); -} - -QWidget *QDesktopWidget::screen(int screen) -{ - Q_D(QDesktopWidget); - if (screen < 0 || screen >= d->screens.length()) - return d->screens.at(0); - return d->screens.at(screen); -} - -const QRect QDesktopWidget::availableGeometry(int screenNo) const -{ - QList<QScreen *> screens = QGuiApplication::screens(); - if (screenNo == -1) - screenNo = 0; - if (screenNo < 0 || screenNo >= screens.size()) - return QRect(); - else - return screens.at(screenNo)->availableGeometry(); -} - -const QRect QDesktopWidget::screenGeometry(int screenNo) const -{ - QList<QScreen *> screens = QGuiApplication::screens(); - if (screenNo == -1) - screenNo = 0; - if (screenNo < 0 || screenNo >= screens.size()) - return QRect(); - else - return screens.at(screenNo)->geometry(); -} - -int QDesktopWidget::screenNumber(const QWidget *w) const -{ - if (!w) - return 0; - - QRect frame = w->frameGeometry(); - if (!w->isWindow()) - frame.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); - const QPoint midpoint = (frame.topLeft() + frame.bottomRight()) / 2; - return screenNumber(midpoint); -} - -int QDesktopWidget::screenNumber(const QPoint &p) const -{ - QList<QScreen *> screens = QGuiApplication::screens(); - - for (int i = 0; i < screens.size(); ++i) - if (screens.at(i)->geometry().contains(p)) - return i; - - return primaryScreen(); //even better would be closest screen -} - -void QDesktopWidget::resizeEvent(QResizeEvent *) -{ -} - -QT_END_NAMESPACE - -#include "moc_qdesktopwidget.cpp" diff --git a/src/widgets/kernel/qgesturemanager.cpp b/src/widgets/kernel/qgesturemanager.cpp index 929e5e2fcf..532cea1f67 100644 --- a/src/widgets/kernel/qgesturemanager.cpp +++ b/src/widgets/kernel/qgesturemanager.cpp @@ -46,7 +46,7 @@ #include "private/qgraphicsitem_p.h" #include "private/qevent_p.h" #include "private/qapplication_p.h" -#include "private/qwidgetwindow_qpa_p.h" +#include "private/qwidgetwindow_p.h" #include "qgesture.h" #include "qevent.h" #include "qgraphicsitem.h" diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp index 307d0bb909..907f69dfbd 100644 --- a/src/widgets/kernel/qopenglwidget.cpp +++ b/src/widgets/kernel/qopenglwidget.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -39,146 +39,892 @@ ** ****************************************************************************/ -#include "qopenglwidget_p.h" -#include <QOpenGLContext> -#include <QtWidgets/private/qwidget_p.h> - -#include <QOpenGLFramebufferObject> -#include <QOpenGLFunctions> -#include <QWindow> -#include <qpa/qplatformwindow.h> -#include <QDebug> +#include "qopenglwidget.h" +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLFramebufferObject> +#include <QtGui/QOffscreenSurface> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QWindow> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/QOpenGLPaintDevice> +#include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformintegration.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qopenglextensions_p.h> +#include <QtGui/private/qfont_p.h> +#include <QtWidgets/private/qwidget_p.h> QT_BEGIN_NAMESPACE +/*! + \class QOpenGLWidget + \inmodule QtWidgets + \since 5.4 + + \brief The QOpenGLWidget class is a widget for rendering OpenGL graphics. + + QOpenGLWidget provides functionality for displaying OpenGL graphics + integrated into a Qt application. It is very simple to use: Make + your class inherit from it and use the subclass like any other + QWidget, except that you have the choice between using QPainter and + standard OpenGL rendering commands. + + QOpenGLWidget provides three convenient virtual functions that you + can reimplement in your subclass to perform the typical OpenGL + tasks: + + \list + \li paintGL() - Renders the OpenGL scene. Gets called whenever the widget + needs to be updated. + \li resizeGL() - Sets up the OpenGL viewport, projection, etc. Gets + called whenever the widget has been resized (and also when it + is shown for the first time because all newly created widgets get a + resize event automatically). + \li initializeGL() - Sets up the OpenGL resources and state. Gets called + once before the first time resizeGL() or paintGL() is called. + \endlist + + If you need to trigger a repaint from places other than paintGL() (a + typical example is when using \l{QTimer}{timers} to animate scenes), + you should call the widget's update() function to schedule an update. + + Your widget's OpenGL rendering context is made current when + paintGL(), resizeGL(), or initializeGL() is called. If you need to + call the standard OpenGL API functions from other places (e.g. in + your widget's constructor or in your own paint functions), you + must call makeCurrent() first. + + All rendering happens into an OpenGL framebuffer + object. makeCurrent() ensure that it is bound in the context. Keep + this in mind when creating and binding additional framebuffer + objects in the rendering code in paintGL(). Never re-bind the + framebuffer with ID 0. Instead, call defaultFramebufferObject() to + get the ID that should be bound. + + QOpenGLWidget allows using different OpenGL versions and profiles + when the platform supports it. Just set the requested format via + setFormat(). Keep in mind however that having multiple QOpenGLWidget + instances in the same window requires that they all use the same + format, or at least formats that do not make the contexts + non-sharable. + + \section1 Painting Techniques + + As described above, subclass QOpenGLWidget to render pure 3D content in the + following way: + + \list + + \li Reimplement the initializeGL() and resizeGL() functions to + set up the OpenGL state and provide a perspective transformation. + + \li Reimplement paintGL() to paint the 3D scene, calling only + OpenGL functions. + + \endlist + + It is also possible to draw 2D graphics onto a QOpenGLWidget subclass using QPainter: + + \list + + \li In paintGL(), instead of issuing OpenGL commands, construct a QPainter + object for use on the widget. + + \li Draw primitives using QPainter's member functions. + + \li Direct OpenGL commands can still be issued. However, you must make sure + these are enclosed by a call to the painter's beginNativePainting() and + endNativePainting(). + + \endlist + + When performing drawing using QPainter only, it is also possible to perform + the painting like it is done for ordinary widgets: by reimplementing paintEvent(). + + \list + + \li Reimplement the paintEvent() function. + + \li Construct a QPainter object targeting the widget. Either pass the widget to the + constructor or the QPainter::begin() function. + + \li Draw primitives using QPainter's member functions. + + \li Painting finishes then the QPainter instance is destroyed. Alternatively, + call QPainter::end() explicitly. + + \endlist + + \section1 OpenGL function calls, headers and QOpenGLFunctions + + When making OpenGL function calls, it is strongly recommended to avoid calling + the functions directly. Instead, prefer using QOpenGLFunctions (when making + portable applications) or the versioned variants (for example, + QOpenGLFunctions_3_2_Core and similar, when targeting modern, desktop-only + OpenGL). This way the application will work correctly in all Qt build + configurations, including the ones that perform dynamic OpenGL implementation + loading which means applications are not directly linking to an GL + implementation and thus direct function calls are not feasible. + + In paintGL() the current context is always accessible by caling + QOpenGLContext::currentContext(). From this context an already initialized, + ready-to-be-used QOpenGLFunctions instance is retrievable by calling + QOpenGLContext::functions(). An alternative to prefixing every GL call is to + inherit from QOpenGLFunctions and call + QOpenGLFunctions::initializeOpenGLFunctions() in initializeGL(). + + As for the OpenGL headers, note that in most cases there will be no need to + directly include any headers like GL.h. The OpenGL-related Qt headers will + include qopengl.h which will in turn include an appropriate header for the + system. This might be an OpenGL ES 3.x or 2.0 header, the highest version that + is available, or a system-provided gl.h. In addition, a copy of the extension + headers (called glext.h on some systems) is provided as part of Qt both for + OpenGL and OpenGL ES. These will get included automatically on platforms where + feasible. This means that constants and function pointer typedefs from ARB, + EXT, OES extensions are automatically available. + + \section1 Code examples + + To get started, the simplest QOpenGLWidget subclass could like like the following: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 0 + + Alternatively, the prefixing of each and every OpenGL call can be avoidided by deriving + from QOpenGLFunctions instead: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 1 + + To get a context compatible with a given OpenGL version or profile, or to + request depth and stencil buffers, call setFormat(): + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 2 + + With OpenGL 3.0+ contexts, when portability is not important, the versioned + QOpenGLFunctions variants give easy access to all the modern OpenGL functions + available in a given version: + + \snippet code/doc_gui_widgets_qopenglwidget.cpp 3 + + \section1 Relation to QGLWidget + + The legacy QtOpenGL module (classes prefixed with QGL) provides a widget + called QGLWidget. QOpenGLWidget is intended to be a modern replacement for + it. Therefore, especially in new applications, the general recommendation is + to use QOpenGLWidget. + + While the API is very similar, there is an important difference between the + two: QOpenGLWidget always renders offscreen, using framebuffer + objects. QGLWidget on the other hand uses a native window and surface. The + latter causes issues when using it in complex user interfaces since, depending + on the platform, such native child widgets may have various limitations, + regarding stacking orders for example. QOpenGLWidget avoids this by not + creating a separate native window. + + \section1 Threading + + Performing offscreen rendering on worker threads, for example to generate + textures that are then used in the GUI/main thread in paintGL(), are supported + by exposing the widget's QOpenGLContext so that additional contexts sharing + with it can be created on each thread. + + Drawing directly to the QOpenGLWidget's framebuffer outside the GUI/main + thread is possible by reimplementing paintEvent() to do nothing. The context's + thread affinity has to be changed via QObject::moveToThread(). After that, + makeCurrent() and doneCurrent() are usable on the worker thread. Be careful to + move the context back to the GUI/main thread afterwards. + + Unlike QGLWidget, triggering a buffer swap just for the QOpenGLWidget is not + possible since there is no real, onscreen native surface for it. Instead, it + is up to the widget stack to manage composition and buffer swaps on the gui + thread. When a thread is done updating the framebuffer, call update() \b{on + the GUI/main thread} to schedule composition. + + Extra care has to be taken to avoid using the framebuffer when the GUI/main + thread is performing compositing. The signals aboutToCompose() and + frameSwapped() will be emitted when the composition is starting and + ending. They are emitted on the GUI/main thread. This means that by using a + direct connection aboutToCompose() can block the GUI/main thread until the + worker thread has finished its rendering. After that, the worker thread must + perform no further rendering until the frameSwapped() signal is emitted. If + this is not acceptable, the worker thread has to implement a double buffering + mechanism. This involves drawing using an alternative render target, that is + fully controlled by the thread, e.g. an additional framebuffer object, and + blitting to the QOpenGLWidget's framebuffer at a suitable time. + + \section1 Context sharing + + When multiple QOpenGLWidgets are added as children to the same top-level + widget, their contexts will share with each other. This does not apply for + QOpenGLWidget instances that belong to different windows. + + This means that all QOpenGLWidgets in the same window can access each other's + sharable resources, like textures, and there is no need for an extra "global + share" context, as was the case with QGLWidget. + + Note that QOpenGLWidget expects a standard conformant implementation of + resource sharing when it comes to the underlying graphics drivers. For + example, some drivers, in particular for mobile and embedded hardware, have + issues with setting up sharing between an existing context and others that are + created later. Some other drivers may behave in unexpected ways when trying to + utilize shared resources between different threads. + + \section1 Limitations + + Putting other widgets underneath and making the QOpenGLWidget transparent will + not lead to the expected results: The widgets underneath will not be + visible. This is because in practice the QOpenGLWidget is drawn before all + other regular, non-OpenGL widgets, and so see-through type of solutions are + not feasible. Other type of layouts, like having widgets on top of the + QOpenGLWidget, will function as expected. + + When absolutely necessary, this limitation can be overcome by setting the + Qt::WA_AlwaysStackOnTop attribute on the QOpenGLWidget. Be aware however that + this breaks stacking order, for example it will not be possible to have other + widgets on top of the QOpenGLWidget, so it should only be used in situations + where a semi-transparent QOpenGLWidget with other widgets visible underneath + is required. + + \e{OpenGL is a trademark of Silicon Graphics, Inc. in the United States and other + countries.} + + \sa QOpenGLFunctions +*/ + +/*! + \fn void QOpenGLWidget::aboutToCompose() + + This signal is emitted when the widget's top-level window is about to begin + composing the textures of its QOpenGLWidget children and the other widgets. +*/ + +/*! + \fn void QOpenGLWidget::frameSwapped() + + This signal is emitted after the widget's top-level window has finished + composition and returned from its potentially blocking + QOpenGLContext::swapBuffers() call. +*/ + +/*! + \fn void QOpenGLWidget::aboutToResize() + + This signal is emitted when the widget's size is changed and therefore the + framebuffer object is going to be recreated. +*/ + +/*! + \fn void QOpenGLWidget::resized() + + This signal is emitted right after the framebuffer object has been recreated + due to resizing the widget. +*/ + +class QOpenGLWidgetPaintDevice : public QOpenGLPaintDevice +{ +public: + QOpenGLWidgetPaintDevice(QOpenGLWidget *widget) : w(widget) { } + void ensureActiveTarget() Q_DECL_OVERRIDE; + +private: + QOpenGLWidget *w; +}; + class QOpenGLWidgetPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QOpenGLWidget) public: QOpenGLWidgetPrivate() - : fbo(0), uninitialized(true) + : context(0), + fbo(0), + resolvedFbo(0), + surface(0), + initialized(false), + fakeHidden(false), + paintDevice(0), + inBackingStorePaint(false) + { + } + + ~QOpenGLWidgetPrivate() { + reset(); } - GLuint textureId() const { return fbo ? fbo->texture() : 0; } - const QSurface *surface() const { return q_func()->window()->windowHandle(); } - QSurface *surface() { return q_func()->window()->windowHandle(); } + void reset(); + void recreateFbo(); + + GLuint textureId() const Q_DECL_OVERRIDE; + void initialize(); + void invokeUserPaint(); + void render(); - QOpenGLContext context; - QOpenGLFramebufferObject *fbo; - bool uninitialized; + QImage grabFramebuffer() Q_DECL_OVERRIDE; + void beginBackingStorePainting() Q_DECL_OVERRIDE { inBackingStorePaint = true; } + void endBackingStorePainting() Q_DECL_OVERRIDE { inBackingStorePaint = false; } + void beginCompose() Q_DECL_OVERRIDE; + void endCompose() Q_DECL_OVERRIDE; + void resizeViewportFramebuffer() Q_DECL_OVERRIDE; - int w,h; + QOpenGLContext *context; + QOpenGLFramebufferObject *fbo; + QOpenGLFramebufferObject *resolvedFbo; + QOffscreenSurface *surface; + bool initialized; + bool fakeHidden; + QOpenGLPaintDevice *paintDevice; + bool inBackingStorePaint; + QSurfaceFormat requestedFormat; }; +void QOpenGLWidgetPaintDevice::ensureActiveTarget() +{ + QOpenGLWidgetPrivate *d = static_cast<QOpenGLWidgetPrivate *>(QWidgetPrivate::get(w)); + if (!d->initialized) + return; + + if (QOpenGLContext::currentContext() != d->context) + w->makeCurrent(); + else + d->fbo->bind(); +} + +GLuint QOpenGLWidgetPrivate::textureId() const +{ + return resolvedFbo ? resolvedFbo->texture() : (fbo ? fbo->texture() : 0); +} + +void QOpenGLWidgetPrivate::reset() +{ + delete paintDevice; + paintDevice = 0; + delete fbo; + fbo = 0; + delete resolvedFbo; + resolvedFbo = 0; + delete context; + context = 0; + delete surface; + surface = 0; + initialized = fakeHidden = inBackingStorePaint = false; +} + +void QOpenGLWidgetPrivate::recreateFbo() +{ + Q_Q(QOpenGLWidget); + + emit q->aboutToResize(); + + context->makeCurrent(surface); + + delete fbo; + delete resolvedFbo; + + int samples = get(q->window())->shareContext()->format().samples(); + QOpenGLExtensions *extfuncs = static_cast<QOpenGLExtensions *>(context->functions()); + if (!extfuncs->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)) + samples = 0; + + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + format.setSamples(samples); + + const QSize deviceSize = q->size() * q->devicePixelRatio(); + fbo = new QOpenGLFramebufferObject(deviceSize, format); + if (samples > 0) + resolvedFbo = new QOpenGLFramebufferObject(deviceSize); + + fbo->bind(); + + paintDevice->setSize(deviceSize); + + emit q->resized(); +} + +void QOpenGLWidgetPrivate::beginCompose() +{ + Q_Q(QOpenGLWidget); + emit q->aboutToCompose(); +} + +void QOpenGLWidgetPrivate::endCompose() +{ + Q_Q(QOpenGLWidget); + emit q->frameSwapped(); +} + void QOpenGLWidgetPrivate::initialize() { Q_Q(QOpenGLWidget); - if (!uninitialized) + if (initialized) + return; + + // Get our toplevel's context with which we will share in order to make the + // texture usable by the underlying window's backingstore. + QOpenGLContext *shareContext = get(q->window())->shareContext(); + if (!shareContext) { + qWarning("QOpenGLWidget: Cannot be used without a context shared with the toplevel."); return; - context.setShareContext(get(q->window())->shareContext()); - context.setFormat(surface()->format()); - context.create(); - context.makeCurrent(surface()); + } + + QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext); + ctx->setShareContext(shareContext); + ctx->setFormat(requestedFormat); + if (!ctx->create()) { + qWarning("QOpenGLWidget: Failed to create context"); + return; + } + + // The top-level window's surface is not good enough since it causes way too + // much trouble with regards to the QSurfaceFormat for example. So just like + // in QQuickWidget, use a dedicated QOffscreenSurface. + surface = new QOffscreenSurface; + surface->setFormat(ctx->format()); + surface->create(); + + if (!ctx->makeCurrent(surface)) { + qWarning("QOpenGLWidget: Failed to make context current"); + return; + } + + paintDevice = new QOpenGLWidgetPaintDevice(q); + paintDevice->setSize(q->size() * q->devicePixelRatio()); + paintDevice->setDevicePixelRatio(q->devicePixelRatio()); + + context = ctx.take(); + initialized = true; + q->initializeGL(); - uninitialized = false; } +void QOpenGLWidgetPrivate::invokeUserPaint() +{ + Q_Q(QOpenGLWidget); + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glViewport(0, 0, q->width() * q->devicePixelRatio(), q->height() * q->devicePixelRatio()); + + q->paintGL(); + + if (resolvedFbo) { + QRect rect(QPoint(0, 0), fbo->size()); + QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect); + } +} + +void QOpenGLWidgetPrivate::render() +{ + Q_Q(QOpenGLWidget); + + if (fakeHidden || !initialized) + return; + + q->makeCurrent(); + invokeUserPaint(); + context->functions()->glFlush(); +} + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +QImage QOpenGLWidgetPrivate::grabFramebuffer() +{ + Q_Q(QOpenGLWidget); + if (!initialized) + return QImage(); + + render(); + q->makeCurrent(); + QImage res = qt_gl_read_framebuffer(q->size() * q->devicePixelRatio(), false, false); + + return res; +} + +void QOpenGLWidgetPrivate::resizeViewportFramebuffer() +{ + Q_Q(QOpenGLWidget); + if (!initialized) + return; + + if (!fbo || q->size() != fbo->size()) + recreateFbo(); +} + +/*! + Constructs a widget which is a child of \a parent, with widget flags set to \a f. + */ QOpenGLWidget::QOpenGLWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(*(new QOpenGLWidgetPrivate), parent, f) { Q_D(QOpenGLWidget); - d->setRenderToTexture(); + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface)) + d->setRenderToTexture(); + else + qWarning("QOpenGLWidget is not supported on this platform."); } +/*! + Destroys the widget + */ QOpenGLWidget::~QOpenGLWidget() { } +/*! + Sets the requested surface \a format. + + \note Requesting an alpha buffer via this function will not lead to the desired results + and should be avoided. Instead, use Qt::WA_AlwaysStackOnTop to enable semi-transparent + QOpenGLWidget instances with other widgets visible underneath. Keep in mind however that + this breaks the stacking order, so it will no longer be possible to have other widgets + on top of the QOpenGLWidget. + + \sa format(), Qt::WA_AlwaysStackOnTop + */ +void QOpenGLWidget::setFormat(const QSurfaceFormat &format) +{ + Q_UNUSED(format); + Q_D(QOpenGLWidget); + if (d->initialized) { + qWarning("QOpenGLWidget: Already initialized, setting the format has no effect"); + return; + } + + d->requestedFormat = format; +} + +/*! + Returns the context and surface format used by this widget and its toplevel + window. + + After the widget and its toplevel have both been created, resized and shown, + this function will return the actual format of the context. This may differ + from the requested format if the request could not be fulfilled by the + platform. It is also possible to get larger color buffer sizes than + requested. + + When the widget's window and the related OpenGL resources are not yet + initialized, the return value is the format that has been set via + setFormat(). + + \sa setFormat(), context() + */ +QSurfaceFormat QOpenGLWidget::format() const +{ + Q_D(const QOpenGLWidget); + return d->initialized ? d->context->format() : d->requestedFormat; +} + +/*! + \return \e true if the widget and OpenGL resources, like the context, have + been successfully initialized. Note that the return value is always false + until the widget is shown. +*/ bool QOpenGLWidget::isValid() const { Q_D(const QOpenGLWidget); - return d->context.isValid(); + return d->initialized && d->context->isValid(); } +/*! + Prepares for rendering OpenGL content for this widget by making the + corresponding context current and binding the framebuffer object in that + context. + + It is not necessary to call this function in most cases, because it + is called automatically before invoking paintGL(). + + \sa context(), paintGL(), doneCurrent() + */ void QOpenGLWidget::makeCurrent() { Q_D(QOpenGLWidget); - d->context.makeCurrent(d->surface()); + if (!d->initialized) { + qWarning("QOpenGLWidget: Cannot make uninitialized widget current"); + return; + } + + d->context->makeCurrent(d->surface); d->fbo->bind(); } +/*! + Releases the context. + + It is not necessary to call this function in most cases, since the + widget will make sure the context is bound and released properly + when invoking paintGL(). + */ void QOpenGLWidget::doneCurrent() { Q_D(QOpenGLWidget); - d->context.doneCurrent(); + if (!d->initialized) + return; + + d->context->doneCurrent(); } -QSurfaceFormat QOpenGLWidget::format() const +/*! + \return The QOpenGLContext used by this widget or \c 0 if not yet initialized. + + \note The context and the framebuffer object used by the widget changes when + reparenting the widget via setParent(). + + \sa QOpenGLContext::setShareContext(), defaultFramebufferObject() + */ +QOpenGLContext *QOpenGLWidget::context() const { Q_D(const QOpenGLWidget); - return d->surface()->format(); + return d->context; } +/*! + \return The framebuffer object handle or \c 0 if not yet initialized. + + \note The framebuffer object belongs to the context returned by context() + and may not be accessible from other contexts. + + \note The context and the framebuffer object used by the widget changes when + reparenting the widget via setParent(). In addition, the framebuffer object + changes on each resize. + + \sa context() + */ GLuint QOpenGLWidget::defaultFramebufferObject() const { Q_D(const QOpenGLWidget); return d->fbo ? d->fbo->handle() : 0; } +/*! + This virtual function is called once before the first call to + paintGL() or resizeGL(). Reimplement it in a subclass. + + This function should set up any required OpenGL resources and state. + + There is no need to call makeCurrent() because this has already been + done when this function is called. Note however that the framebuffer + is not yet available at this stage, so avoid issuing draw calls from + here. Defer such calls to paintGL() instead. + + \sa paintGL(), resizeGL() +*/ void QOpenGLWidget::initializeGL() { - } +/*! + This virtual function is called whenever the widget has been + resized. Reimplement it in a subclass. The new size is passed in + \a w and \a h. + + There is no need to call makeCurrent() because this has already been + done when this function is called. Additionally, the framebuffer is + also bound. + + \sa initializeGL(), paintGL() +*/ void QOpenGLWidget::resizeGL(int w, int h) { Q_UNUSED(w); Q_UNUSED(h); } +/*! + This virtual function is called whenever the widget needs to be + painted. Reimplement it in a subclass. + + There is no need to call makeCurrent() because this has already + been done when this function is called. + + Before invoking this function, the context and the framebuffer are + bound, and the viewport is set up by a call to glViewport(). No + other state is set and no clearing or drawing is performed by the + framework. + + \sa initializeGL(), resizeGL() +*/ void QOpenGLWidget::paintGL() { } -void QOpenGLWidget::updateGL() +/*! + \internal + + Handles resize events that are passed in the \a event parameter. + Calls the virtual function resizeGL(). +*/ +void QOpenGLWidget::resizeEvent(QResizeEvent *e) { Q_D(QOpenGLWidget); - if (d->uninitialized || !d->surface()) + + if (e->size().isEmpty()) { + d->fakeHidden = true; + return; + } + d->fakeHidden = false; + + d->initialize(); + if (!d->initialized) return; - makeCurrent(); - paintGL(); - d->context.functions()->glFlush(); - doneCurrent(); - update(); + d->recreateFbo(); + resizeGL(width(), height()); + d->invokeUserPaint(); + d->context->functions()->glFlush(); } +/*! + Handles paint events. -void QOpenGLWidget::resizeEvent(QResizeEvent *) + Calling QWidget::update() will lead to sending a paint event \a e, + and thus invoking this function. (NB this is asynchronous and will + happen at some point after returning from update()). This function + will then, after some preparation, call the virtual paintGL() to + update the contents of the QOpenGLWidget's framebuffer. The widget's + top-level window will then composite the framebuffer's texture with + the rest of the window. +*/ +void QOpenGLWidget::paintEvent(QPaintEvent *e) { + Q_UNUSED(e); Q_D(QOpenGLWidget); - d->w = width(); - d->h = height(); - d->initialize(); + if (!d->initialized) + return; - d->context.makeCurrent(d->surface()); - delete d->fbo; // recreate when resized - d->fbo = new QOpenGLFramebufferObject(size() * devicePixelRatio(), QOpenGLFramebufferObject::CombinedDepthStencil); - d->fbo->bind(); - QOpenGLFunctions *funcs = d->context.functions(); - resizeGL(width(), height()); - paintGL(); - funcs->glFlush(); + if (updatesEnabled()) + d->render(); +} + +/*! + Renders and returns a 32-bit RGB image of the framebuffer. + + \note This is a potentially expensive operation because it relies on glReadPixels() + to read back the pixels. This may be slow and can stall the GPU pipeline. +*/ +QImage QOpenGLWidget::grabFramebuffer() +{ + Q_D(QOpenGLWidget); + return d->grabFramebuffer(); +} + +/*! + \internal +*/ +int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + Q_D(const QOpenGLWidget); + if (d->inBackingStorePaint) + return QWidget::metric(metric); + + QScreen *screen = window()->windowHandle()->screen(); + if (!screen && QGuiApplication::primaryScreen()) + screen = QGuiApplication::primaryScreen(); + + const float dpmx = qt_defaultDpiX() * 100. / 2.54; + const float dpmy = qt_defaultDpiY() * 100. / 2.54; + + switch (metric) { + case PdmWidth: + return width(); + case PdmHeight: + return height(); + case PdmDepth: + return 32; + case PdmWidthMM: + if (screen) + return width() * screen->physicalSize().width() / screen->geometry().width(); + else + return width() * 1000 / dpmx; + case PdmHeightMM: + if (screen) + return height() * screen->physicalSize().height() / screen->geometry().height(); + else + return height() * 1000 / dpmy; + case PdmNumColors: + return 0; + case PdmDpiX: + if (screen) + return qRound(screen->logicalDotsPerInchX()); + else + return qRound(dpmx * 0.0254); + case PdmDpiY: + if (screen) + return qRound(screen->logicalDotsPerInchY()); + else + return qRound(dpmy * 0.0254); + case PdmPhysicalDpiX: + if (screen) + return qRound(screen->physicalDotsPerInchX()); + else + return qRound(dpmx * 0.0254); + case PdmPhysicalDpiY: + if (screen) + return qRound(screen->physicalDotsPerInchY()); + else + return qRound(dpmy * 0.0254); + case PdmDevicePixelRatio: + if (screen) + return screen->devicePixelRatio(); + else + return 1.0; + default: + qWarning("QOpenGLWidget::metric(): unknown metric %d", metric); + return 0; + } +} + +/*! + \internal +*/ +QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const +{ + Q_D(const QOpenGLWidget); + if (d->inBackingStorePaint) + return QWidget::redirected(p); + + return d->paintDevice; +} + +/*! + \internal +*/ +QPaintEngine *QOpenGLWidget::paintEngine() const +{ + Q_D(const QOpenGLWidget); + // QWidget needs to "punch a hole" into the backingstore. This needs the + // normal paint engine and device, not the GL one. So in this mode, behave + // like a normal widget. + if (d->inBackingStorePaint) + return QWidget::paintEngine(); + + if (!d->initialized) + return 0; + + return d->paintDevice->paintEngine(); } -void QOpenGLWidget::paintEvent(QPaintEvent *) +/*! + \internal +*/ +bool QOpenGLWidget::event(QEvent *e) { - qWarning("QOpenGLWidget does not support paintEvent() yet."); - return; + Q_D(QOpenGLWidget); + switch (e->type()) { + case QEvent::WindowChangeInternal: + if (d->initialized) + d->reset(); + // FALLTHROUGH + case QEvent::Show: // reparenting may not lead to a resize so reinitalize on Show too + if (!d->initialized && !size().isEmpty() && window() && window()->windowHandle()) { + d->initialize(); + if (d->initialized) + d->recreateFbo(); + } + break; + default: + break; + } + return QWidget::event(e); } QT_END_NAMESPACE diff --git a/src/widgets/kernel/qopenglwidget_p.h b/src/widgets/kernel/qopenglwidget.h index 1c7f0bfeec..28d802e1f4 100644 --- a/src/widgets/kernel/qopenglwidget_p.h +++ b/src/widgets/kernel/qopenglwidget.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -39,21 +39,11 @@ ** ****************************************************************************/ -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It may change from version to version -// without notice, or even be removed. -// -// We mean it. -// #ifndef QOPENGLWIDGET_H #define QOPENGLWIDGET_H -#include <QWidget> -#include <QSurfaceFormat> - +#include <QtWidgets/QWidget> +#include <QtGui/QSurfaceFormat> #include <QtGui/qopengl.h> QT_BEGIN_NAMESPACE @@ -66,68 +56,43 @@ class Q_WIDGETS_EXPORT QOpenGLWidget : public QWidget Q_DECLARE_PRIVATE(QOpenGLWidget) public: - explicit QOpenGLWidget(QWidget* parent=0, - Qt::WindowFlags f=0); - -// This API is not finalized yet. The commented-out functions below are -// QGLWidget functions that have not been implemented for QOpenGLWidget. -// Some of them may not end up in the final version (which is planned for a -// future release of Qt). - -// explicit QOpenGLWidget(const QSurfaceFormat& format, QWidget* parent=0, -// Qt::WindowFlags f=0); + explicit QOpenGLWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); ~QOpenGLWidget(); -// void qglClearColor(const QColor& c) const; + void setFormat(const QSurfaceFormat &format); + QSurfaceFormat format() const; bool isValid() const; -// bool isSharing() const; void makeCurrent(); void doneCurrent(); -// void swapBuffers(); - - QSurfaceFormat format() const; + QOpenGLContext *context() const; GLuint defaultFramebufferObject() const; -// QPixmap renderPixmap(int w = 0, int h = 0, bool useContext = false); - QImage grabFrameBuffer(bool withAlpha = false); + QImage grabFramebuffer(); -// static QImage convertToGLFormat(const QImage& img); - -// QPaintEngine *paintEngine() const; - -// void drawTexture(const QRectF &target, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); -// void drawTexture(const QPointF &point, GLuint textureId, GLenum textureTarget = GL_TEXTURE_2D); - -public Q_SLOTS: - void updateGL(); +Q_SIGNALS: + void aboutToCompose(); + void frameSwapped(); + void aboutToResize(); + void resized(); protected: -// bool event(QEvent *); virtual void initializeGL(); virtual void resizeGL(int w, int h); virtual void paintGL(); -// void setAutoBufferSwap(bool on); -// bool autoBufferSwap() const; + void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; + bool event(QEvent *e) Q_DECL_OVERRIDE; - void paintEvent(QPaintEvent*); - void resizeEvent(QResizeEvent*); + int metric(QPaintDevice::PaintDeviceMetric metric) const Q_DECL_OVERRIDE; + QPaintDevice *redirected(QPoint *p) const Q_DECL_OVERRIDE; + QPaintEngine *paintEngine() const Q_DECL_OVERRIDE; -// virtual void glInit(); -// virtual void glDraw(); - -// QOpenGLWidget(QOpenGLWidgetPrivate &dd, -// const QGLFormat &format = QGLFormat(), -// QWidget *parent = 0, -// const QOpenGLWidget* shareWidget = 0, -// Qt::WindowFlags f = 0); private: Q_DISABLE_COPY(QOpenGLWidget) - - }; QT_END_NAMESPACE diff --git a/src/widgets/kernel/qshortcut.cpp b/src/widgets/kernel/qshortcut.cpp index dcf6aed591..217f0386cb 100644 --- a/src/widgets/kernel/qshortcut.cpp +++ b/src/widgets/kernel/qshortcut.cpp @@ -51,7 +51,7 @@ #include <private/qapplication_p.h> #include <private/qshortcutmap_p.h> #include <private/qaction_p.h> -#include <private/qwidgetwindow_qpa_p.h> +#include <private/qwidgetwindow_p.h> QT_BEGIN_NAMESPACE diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 7396808442..43ce9a82f7 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -69,7 +69,7 @@ # include <private/qmainwindowlayout_p.h> #endif #include <qpa/qplatformwindow.h> -#include "private/qwidgetwindow_qpa_p.h" +#include "private/qwidgetwindow_p.h" #include "qpainter.h" #include "qtooltip.h" #include "qwhatsthis.h" @@ -357,6 +357,13 @@ void QWidgetPrivate::scrollChildren(int dx, int dy) } } +void QWidgetPrivate::setWSGeometry() +{ + Q_Q(QWidget); + if (QWindow *window = q->windowHandle()) + window->setGeometry(data.crect); +} + void QWidgetPrivate::updateWidgetTransform(QEvent *event) { Q_Q(QWidget); @@ -1065,7 +1072,11 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w) // interpret WindowSystemMenuHint as a close button and we can't change that behavior // we can't just add this in. #ifndef Q_WS_MAC - if (flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint)) { + if ((flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint)) +# ifdef Q_OS_WIN + && type != Qt::Dialog // QTBUG-2027, allow for menu-less dialogs. +# endif + ) { flags |= Qt::WindowSystemMenuHint; #else if (flags & (Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint @@ -1348,6 +1359,155 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) d->updateFrameStrut(); } +void q_createNativeChildrenAndSetParent(const QWidget *parentWidget) +{ + QObjectList children = parentWidget->children(); + for (int i = 0; i < children.size(); i++) { + if (children.at(i)->isWidgetType()) { + const QWidget *childWidget = qobject_cast<const QWidget *>(children.at(i)); + if (childWidget) { // should not be necessary + if (childWidget->testAttribute(Qt::WA_NativeWindow)) { + if (!childWidget->internalWinId()) + childWidget->winId(); + if (childWidget->windowHandle()) { + QWindow *parentWindow = childWidget->nativeParentWidget()->windowHandle(); + if (childWidget->isWindow()) + childWidget->windowHandle()->setTransientParent(parentWindow); + else + childWidget->windowHandle()->setParent(parentWindow); + } + } else { + q_createNativeChildrenAndSetParent(childWidget); + } + } + } + } + +} + +void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) +{ + Q_Q(QWidget); + + Q_UNUSED(window); + Q_UNUSED(initializeWindow); + Q_UNUSED(destroyOldWindow); + + Qt::WindowFlags flags = data.window_flags; + + if (!q->testAttribute(Qt::WA_NativeWindow) && !q->isWindow()) + return; // we only care about real toplevels + + QWindow *win = topData()->window; + // topData() ensures the extra is created but does not ensure 'window' is non-null + // in case the extra was already valid. + if (!win) { + createTLSysExtra(); + win = topData()->window; + } + + foreach (const QByteArray &propertyName, q->dynamicPropertyNames()) { + if (!qstrncmp(propertyName, "_q_platform_", 12)) + win->setProperty(propertyName, q->property(propertyName)); + } + +#ifdef Q_OS_OSX + if (q->testAttribute(Qt::WA_ShowWithoutActivating)) + win->setProperty("_q_showWithoutActivating", QVariant(true)); +#endif + win->setFlags(data.window_flags); + fixPosIncludesFrame(); + if (q->testAttribute(Qt::WA_Moved) + || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowManagement)) + win->setGeometry(q->geometry()); + else + win->resize(q->size()); + if (win->isTopLevel()) + win->setScreen(QGuiApplication::screens().value(topData()->screenIndex, 0)); + + QSurfaceFormat format = win->requestedFormat(); + if ((flags & Qt::Window) && win->surfaceType() != QSurface::OpenGLSurface + && q->testAttribute(Qt::WA_TranslucentBackground)) { + format.setAlphaBufferSize(8); + } + win->setFormat(format); + + if (QWidget *nativeParent = q->nativeParentWidget()) { + if (nativeParent->windowHandle()) { + if (flags & Qt::Window) { + win->setTransientParent(nativeParent->window()->windowHandle()); + win->setParent(0); + } else { + win->setTransientParent(0); + win->setParent(nativeParent->windowHandle()); + } + } + } + + qt_window_private(win)->positionPolicy = topData()->posIncludesFrame ? + QWindowPrivate::WindowFrameInclusive : QWindowPrivate::WindowFrameExclusive; + win->create(); + // Enable nonclient-area events for QDockWidget and other NonClientArea-mouse event processing. + if ((flags & Qt::Desktop) == Qt::Window) + win->handle()->setFrameStrutEventsEnabled(true); + + data.window_flags = win->flags(); + + QBackingStore *store = q->backingStore(); + + if (!store) { + if (win && q->windowType() != Qt::Desktop) { + if (q->isTopLevel()) + q->setBackingStore(new QBackingStore(win)); + } else { + q->setAttribute(Qt::WA_PaintOnScreen, true); + } + } + + setWindowModified_helper(); + WId id = win->winId(); + // See the QPlatformWindow::winId() documentation + Q_ASSERT(id != WId(0)); + setWinId(id); + + // Check children and create windows for them if necessary + q_createNativeChildrenAndSetParent(q); + + if (extra && !extra->mask.isEmpty()) + setMask_sys(extra->mask); + + // If widget is already shown, set window visible, too + if (q->isVisible()) + win->setVisible(true); +} + +#ifdef Q_OS_WIN +static const char activeXNativeParentHandleProperty[] = "_q_embedded_native_parent_handle"; +#endif + +void QWidgetPrivate::createTLSysExtra() +{ + Q_Q(QWidget); + if (!extra->topextra->window && (q->testAttribute(Qt::WA_NativeWindow) || q->isWindow())) { + extra->topextra->window = new QWidgetWindow(q); + if (extra->minw || extra->minh) + extra->topextra->window->setMinimumSize(QSize(extra->minw, extra->minh)); + if (extra->maxw != QWIDGETSIZE_MAX || extra->maxh != QWIDGETSIZE_MAX) + extra->topextra->window->setMaximumSize(QSize(extra->maxw, extra->maxh)); + if (extra->topextra->opacity != 255 && q->isWindow()) + extra->topextra->window->setOpacity(qreal(extra->topextra->opacity) / qreal(255)); +#ifdef Q_OS_WIN + // Pass on native parent handle for Widget embedded into Active X. + const QVariant activeXNativeParentHandle = q->property(activeXNativeParentHandleProperty); + if (activeXNativeParentHandle.isValid()) + extra->topextra->window->setProperty(activeXNativeParentHandleProperty, activeXNativeParentHandle); + if (q->inherits("QTipLabel") || q->inherits("QAlphaWidget")) + extra->topextra->window->setProperty("_q_windowsDropShadow", QVariant(true)); +#endif + } + +} + /*! Destroys the widget. @@ -1612,6 +1772,9 @@ void QWidgetPrivate::createExtra() } } +void QWidgetPrivate::createSysExtra() +{ +} /*! \internal @@ -1642,6 +1805,46 @@ void QWidgetPrivate::deleteExtra() } } +void QWidgetPrivate::deleteSysExtra() +{ +} + +void QWidgetPrivate::deleteTLSysExtra() +{ + if (extra && extra->topextra) { + //the qplatformbackingstore may hold a reference to the window, so the backingstore + //needs to be deleted first. If the backingstore holds GL resources, we need to + // make the context current here, since the platform bs does not have a reference + // to the widget. + +#ifndef QT_NO_OPENGL + if (textureChildSeen && extra->topextra->shareContext) + extra->topextra->shareContext->makeCurrent(extra->topextra->window); +#endif + extra->topextra->backingStoreTracker.destroy(); + delete extra->topextra->backingStore; + extra->topextra->backingStore = 0; +#ifndef QT_NO_OPENGL + if (textureChildSeen && extra->topextra->shareContext) + extra->topextra->shareContext->doneCurrent(); + delete extra->topextra->shareContext; + extra->topextra->shareContext = 0; +#endif + + //the toplevel might have a context with a "qglcontext associated with it. We need to + //delete the qglcontext before we delete the qplatformopenglcontext. + //One unfortunate thing about this is that we potentially create a glContext just to + //delete it straight afterwards. + if (extra->topextra->window) { + extra->topextra->window->destroy(); + } + setWinId(0); + delete extra->topextra->window; + extra->topextra->window = 0; + + } +} + /* Returns \c true if there are widgets above this which overlap with \a rect, which is in parent's coordinate system (same as crect). @@ -2364,6 +2567,27 @@ WId QWidget::effectiveWinId() const return 0; } +/*! + If this is a native widget, return the associated QWindow. + Otherwise return null. + + Native widgets include toplevel widgets, QGLWidget, and child widgets + on which winId() was called. + + \since 5.0 + + \sa winId() +*/ +QWindow *QWidget::windowHandle() const +{ + Q_D(const QWidget); + QTLWExtra *extra = d->maybeTopData(); + if (extra) + return extra->window; + + return 0; +} + #ifndef QT_NO_STYLE_STYLESHEET /*! @@ -2654,6 +2878,13 @@ void QWidget::setWindowModality(Qt::WindowModality windowModality) setAttribute(Qt::WA_SetWindowModality, true); } +void QWidgetPrivate::setModal_sys() +{ + Q_Q(QWidget); + if (q->windowHandle()) + q->windowHandle()->setModality(q->windowModality()); +} + /*! \fn bool QWidget::underMouse() const @@ -2747,6 +2978,17 @@ void QWidget::overrideWindowState(Qt::WindowStates newstate) QApplication::sendEvent(this, &e); } +Qt::WindowState effectiveState(Qt::WindowStates state) +{ + if (state & Qt::WindowMinimized) + return Qt::WindowMinimized; + else if (state & Qt::WindowFullScreen) + return Qt::WindowFullScreen; + else if (state & Qt::WindowMaximized) + return Qt::WindowMaximized; + return Qt::WindowNoState; +} + /*! \fn void QWidget::setWindowState(Qt::WindowStates windowState) @@ -2777,6 +3019,39 @@ void QWidget::overrideWindowState(Qt::WindowStates newstate) \sa Qt::WindowState, windowState() */ +void QWidget::setWindowState(Qt::WindowStates newstate) +{ + Q_D(QWidget); + Qt::WindowStates oldstate = windowState(); + if (oldstate == newstate) + return; + if (isWindow() && !testAttribute(Qt::WA_WState_Created)) + create(); + + data->window_state = newstate; + data->in_set_window_state = 1; + Qt::WindowState newEffectiveState = effectiveState(newstate); + Qt::WindowState oldEffectiveState = effectiveState(oldstate); + if (isWindow() && newEffectiveState != oldEffectiveState) { + // Ensure the initial size is valid, since we store it as normalGeometry below. + if (!testAttribute(Qt::WA_Resized) && !isVisible()) + adjustSize(); + + d->createTLExtra(); + if (oldEffectiveState == Qt::WindowNoState) + d->topData()->normalGeometry = geometry(); + + Q_ASSERT(windowHandle()); + windowHandle()->setWindowState(newEffectiveState); + } + data->in_set_window_state = 0; + + if (newstate & Qt::WindowActive) + activateWindow(); + + QWindowStateChangeEvent e(oldstate); + QApplication::sendEvent(this, &e); +} /*! \property QWidget::fullScreen @@ -3169,6 +3444,10 @@ void QWidget::setAcceptDrops(bool on) } +void QWidgetPrivate::registerDropSite(bool on) +{ + Q_UNUSED(on); +} /*! Disables widget input events if \a disable is true; otherwise @@ -3643,6 +3922,28 @@ bool QWidgetPrivate::setMinimumSize_helper(int &minw, int &minh) return true; } +void QWidgetPrivate::setConstraints_sys() +{ + Q_Q(QWidget); + if (extra && q->windowHandle()) { + QWindow *win = q->windowHandle(); + QWindowPrivate *winp = qt_window_private(win); + + winp->minimumSize = QSize(extra->minw, extra->minh); + winp->maximumSize = QSize(extra->maxw, extra->maxh); + + if (extra->topextra) { + winp->baseSize = QSize(extra->topextra->basew, extra->topextra->baseh); + winp->sizeIncrement = QSize(extra->topextra->incw, extra->topextra->inch); + } + + if (winp->platformWindow) { + fixPosIncludesFrame(); + winp->platformWindow->propagateSizeHints(); + } + } +} + /*! \overload @@ -4293,6 +4594,10 @@ void QWidgetPrivate::setPalette_helper(const QPalette &palette) updateIsOpaque(); } +void QWidgetPrivate::updateSystemBackground() +{ +} + /*! \property QWidget::font \brief the font currently set for the widget @@ -4629,6 +4934,13 @@ void QWidget::setCursor(const QCursor &cursor) QApplication::sendEvent(this, &event); } +void QWidgetPrivate::setCursor_sys(const QCursor &cursor) +{ + Q_UNUSED(cursor); + Q_Q(QWidget); + qt_qpa_set_cursor(q, false); +} + void QWidget::unsetCursor() { Q_D(QWidget); @@ -4644,6 +4956,62 @@ void QWidget::unsetCursor() QApplication::sendEvent(this, &event); } +void QWidgetPrivate::unsetCursor_sys() +{ + Q_Q(QWidget); + qt_qpa_set_cursor(q, false); +} + +static inline void applyCursor(QWidget *w, QCursor c) +{ + if (QWindow *window = w->windowHandle()) + window->setCursor(c); +} + +static inline void unsetCursor(QWidget *w) +{ + if (QWindow *window = w->windowHandle()) + window->unsetCursor(); +} + +void qt_qpa_set_cursor(QWidget *w, bool force) +{ + if (!w->testAttribute(Qt::WA_WState_Created)) + return; + + static QPointer<QWidget> lastUnderMouse = 0; + if (force) { + lastUnderMouse = w; + } else if (lastUnderMouse) { + const WId lastWinId = lastUnderMouse->effectiveWinId(); + const WId winId = w->effectiveWinId(); + if (lastWinId && lastWinId == winId) + w = lastUnderMouse; + } else if (!w->internalWinId()) { + return; // The mouse is not under this widget, and it's not native, so don't change it. + } + + while (!w->internalWinId() && w->parentWidget() && !w->isWindow() + && !w->testAttribute(Qt::WA_SetCursor)) + w = w->parentWidget(); + + QWidget *nativeParent = w; + if (!w->internalWinId()) + nativeParent = w->nativeParentWidget(); + if (!nativeParent || !nativeParent->internalWinId()) + return; + + if (w->isWindow() || w->testAttribute(Qt::WA_SetCursor)) { + if (w->isEnabled()) + applyCursor(nativeParent, w->cursor()); + else + // Enforce the windows behavior of clearing the cursor on + // disabled widgets. + unsetCursor(nativeParent); + } else { + unsetCursor(nativeParent); + } +} #endif /*! @@ -5124,18 +5492,22 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //paint the background if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground)) && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { + beginBackingStorePainting(); QPainter p(q); paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0); + endBackingStorePainting(); } if (!sharedPainter) setSystemClip(pdev, toBePainted.translated(offset)); if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) { + beginBackingStorePainting(); QPainter p(q); QColor tint = q->palette().window().color(); tint.setAlphaF(qreal(.6)); p.fillRect(toBePainted.boundingRect(), tint); + endBackingStorePainting(); } } @@ -5145,16 +5517,30 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP << "geometry ==" << QRect(q->mapTo(q->window(), QPoint(0, 0)), q->size()); #endif + bool grabbed = false; +#ifndef QT_NO_OPENGL if (renderToTexture) { // This widget renders into a texture which is composed later. We just need to // punch a hole in the backingstore, so the texture will be visible. - QPainter p(q); - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(q->rect(), Qt::transparent); - } else { + if (!q->testAttribute(Qt::WA_AlwaysStackOnTop)) { + beginBackingStorePainting(); + QPainter p(q); + if (backingStore) { + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(q->rect(), Qt::transparent); + } else { + // We are not drawing to a backingstore: fall back to QImage + p.drawImage(q->rect(), grabFramebuffer()); + grabbed = true; + } + endBackingStorePainting(); + } + } +#endif // QT_NO_OPENGL + + if (!grabbed) { //actually send the paint event - QPaintEvent e(toBePainted); - QCoreApplication::sendSpontaneousEvent(q, &e); + sendPaintEvent(toBePainted); } // Native widgets need to be marked dirty on screen so painting will be done in correct context @@ -5210,6 +5596,13 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } } +void QWidgetPrivate::sendPaintEvent(const QRegion &toBePainted) +{ + Q_Q(QWidget); + QPaintEvent e(toBePainted); + QCoreApplication::sendSpontaneousEvent(q, &e); +} + void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, QWidget::RenderFlags renderFlags) { @@ -5604,6 +5997,17 @@ void QWidgetPrivate::setWindowTitle_helper(const QString &title) setWindowTitle_sys(qt_setWindowTitle_helperHelper(title, q)); } +void QWidgetPrivate::setWindowTitle_sys(const QString &caption) +{ + Q_Q(QWidget); + if (!q->isWindow()) + return; + + if (QWindow *window = q->windowHandle()) + window->setTitle(caption); + +} + void QWidgetPrivate::setWindowIconText_helper(const QString &title) { Q_Q(QWidget); @@ -5611,6 +6015,11 @@ void QWidgetPrivate::setWindowIconText_helper(const QString &title) setWindowIconText_sys(qt_setWindowTitle_helperHelper(title, q)); } +void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) +{ + Q_UNUSED(iconText); +} + /*! \fn void QWidget::windowIconTextChanged(const QString &iconText) @@ -5719,6 +6128,12 @@ void QWidget::setWindowIcon(const QIcon &icon) emit windowIconChanged(icon); } +void QWidgetPrivate::setWindowIcon_sys() +{ + Q_Q(QWidget); + if (QWindow *window = q->windowHandle()) + window->setIcon(q->windowIcon()); +} /*! \property QWidget::windowIconText @@ -5794,6 +6209,16 @@ void QWidgetPrivate::setWindowFilePath_helper(const QString &filePath) #endif } +void QWidgetPrivate::setWindowFilePath_sys(const QString &filePath) +{ + Q_Q(QWidget); + if (!q->isWindow()) + return; + + if (QWindow *window = q->windowHandle()) + window->setFilePath(filePath); +} + /*! Returns the window's role, or an empty string. @@ -6053,6 +6478,21 @@ void QWidget::setFocus(Qt::FocusReason reason) } } +void QWidgetPrivate::setFocus_sys() +{ + Q_Q(QWidget); + // Embedded native widget may have taken the focus; get it back to toplevel if that is the case + const QWidget *topLevel = q->window(); + if (topLevel->windowType() != Qt::Popup) { + if (QWindow *nativeWindow = q->window()->windowHandle()) { + if (nativeWindow != QGuiApplication::focusWindow() + && q->testAttribute(Qt::WA_WState_Created)) { + nativeWindow->requestActivate(); + } + } + } +} + // updates focus_child on parent widgets to point into this widget void QWidgetPrivate::updateFocusChild() { @@ -6554,6 +6994,31 @@ void QWidget::move(const QPoint &p) QWindowContainer::parentWasMoved(this); } +// move() was invoked with Qt::WA_WState_Created not set (frame geometry +// unknown), that is, crect has a position including the frame. +// If we can determine the frame strut, fix that and clear the flag. +void QWidgetPrivate::fixPosIncludesFrame() +{ + Q_Q(QWidget); + if (QTLWExtra *te = maybeTopData()) { + if (te->posIncludesFrame) { + // For Qt::WA_DontShowOnScreen, assume a frame of 0 (for + // example, in QGraphicsProxyWidget). + if (q->testAttribute(Qt::WA_DontShowOnScreen)) { + te->posIncludesFrame = 0; + } else { + if (q->windowHandle()) { + updateFrameStrut(); + if (!q->data->fstrut_dirty) { + data.crect.translate(te->frameStrut.x(), te->frameStrut.y()); + te->posIncludesFrame = 0; + } + } // windowHandle() + } // !WA_DontShowOnScreen + } // posIncludesFrame + } // QTLWExtra +} + /*! \fn void QWidget::resize(int w, int h) \overload @@ -6595,6 +7060,110 @@ void QWidget::setGeometry(const QRect &r) QWindowContainer::parentWasMoved(this); } +void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) +{ + Q_Q(QWidget); + if (extra) { // any size restrictions? + w = qMin(w,extra->maxw); + h = qMin(h,extra->maxh); + w = qMax(w,extra->minw); + h = qMax(h,extra->minh); + } + + if (q->isWindow() && q->windowHandle()) { + QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration(); + if (!integration->hasCapability(QPlatformIntegration::NonFullScreenWindows)) { + x = 0; + y = 0; + w = q->windowHandle()->width(); + h = q->windowHandle()->height(); + } + } + + QPoint oldp = q->geometry().topLeft(); + QSize olds = q->size(); + QRect r(x, y, w, h); + + bool isResize = olds != r.size(); + isMove = oldp != r.topLeft(); //### why do we have isMove as a parameter? + + + // We only care about stuff that changes the geometry, or may + // cause the window manager to change its state + if (r.size() == olds && oldp == r.topLeft()) + return; + + if (!data.in_set_window_state) { + q->data->window_state &= ~Qt::WindowMaximized; + q->data->window_state &= ~Qt::WindowFullScreen; + if (q->isWindow()) + topData()->normalGeometry = QRect(0, 0, -1, -1); + } + + QPoint oldPos = q->pos(); + data.crect = r; + + bool needsShow = false; + + if (!(data.window_state & Qt::WindowFullScreen) && (w == 0 || h == 0)) { + q->setAttribute(Qt::WA_OutsideWSRange, true); + if (q->isVisible() && q->testAttribute(Qt::WA_Mapped)) + hide_sys(); + data.crect = QRect(x, y, w, h); + } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) { + q->setAttribute(Qt::WA_OutsideWSRange, false); + needsShow = true; + } + + if (q->isVisible()) { + if (!q->testAttribute(Qt::WA_DontShowOnScreen) && !q->testAttribute(Qt::WA_OutsideWSRange)) { + if (q->windowHandle()) { + if (q->isWindow()) { + q->windowHandle()->setGeometry(q->geometry()); + } else { + QPoint posInNativeParent = q->mapTo(q->nativeParentWidget(),QPoint()); + q->windowHandle()->setGeometry(QRect(posInNativeParent,r.size())); + } + + if (needsShow) + show_sys(); + } + + if (!q->isWindow()) { + if (renderToTexture) { + QRegion updateRegion(q->geometry()); + updateRegion += QRect(oldPos, olds); + q->parentWidget()->d_func()->invalidateBuffer(updateRegion); + } else if (isMove && !isResize) { + moveRect(QRect(oldPos, olds), x - oldPos.x(), y - oldPos.y()); + } else { + invalidateBuffer_resizeHelper(oldPos, olds); + } + } + } + + // generate a move event for QWidgets without window handles. QWidgets with native + // window handles already receive a move event from + // QGuiApplicationPrivate::processGeometryChangeEvent. + if (isMove && (!q->windowHandle() || q->testAttribute(Qt::WA_DontShowOnScreen))) { + QMoveEvent e(q->pos(), oldPos); + QApplication::sendEvent(q, &e); + } + if (isResize) { + QResizeEvent e(r.size(), olds); + QApplication::sendEvent(q, &e); + if (q->windowHandle()) + q->update(); + } + } else { // not visible + if (isMove && q->pos() != oldPos) + q->setAttribute(Qt::WA_PendingMoveEvent, true); + if (isResize) + q->setAttribute(Qt::WA_PendingResizeEvent, true); + } + +} + /*! \since 4.2 Saves the current geometry and state for top-level widgets. @@ -7208,6 +7777,62 @@ void QWidgetPrivate::show_helper() data.in_show = false; // reset qws optimization } +void QWidgetPrivate::show_sys() +{ + Q_Q(QWidget); + + QWindow *window = q->windowHandle(); + + if (q->testAttribute(Qt::WA_DontShowOnScreen)) { + invalidateBuffer(q->rect()); + q->setAttribute(Qt::WA_Mapped); + if (q->isWindow() && q->windowModality() != Qt::NonModal && window) { + // add our window to the modal window list + QGuiApplicationPrivate::showModalWindow(window); + } + return; + } + + if (renderToTexture && !q->isWindow()) + QApplication::postEvent(q->parentWidget(), new QUpdateLaterEvent(q->geometry())); + else + QApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); + + if (!q->isWindow() && !q->testAttribute(Qt::WA_NativeWindow)) + return; + + if (window) { + if (q->isWindow()) + fixPosIncludesFrame(); + QRect geomRect = q->geometry(); + if (!q->isWindow()) { + QPoint topLeftOfWindow = q->mapTo(q->nativeParentWidget(),QPoint()); + geomRect.moveTopLeft(topLeftOfWindow); + } + const QRect windowRect = window->geometry(); + if (windowRect != geomRect) { + if (q->testAttribute(Qt::WA_Moved) + || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowManagement)) + window->setGeometry(geomRect); + else + window->resize(geomRect.size()); + } + +#ifndef QT_NO_CURSOR + qt_qpa_set_cursor(q, false); // Needed in case cursor was set before show +#endif + invalidateBuffer(q->rect()); + window->setVisible(true); + // Was the window moved by the Window system or QPlatformWindow::initialGeometry() ? + if (window->isTopLevel()) { + const QPoint crectTopLeft = q->data->crect.topLeft(); + const QPoint windowTopLeft = window->geometry().topLeft(); + if (crectTopLeft == QPoint(0, 0) && windowTopLeft != crectTopLeft) + q->data->crect.moveTopLeft(windowTopLeft); + } + } +} + /*! Hides the widget. This function is equivalent to setVisible(false). @@ -7285,6 +7910,39 @@ void QWidgetPrivate::hide_helper() #endif } +void QWidgetPrivate::hide_sys() +{ + Q_Q(QWidget); + + QWindow *window = q->windowHandle(); + + if (q->testAttribute(Qt::WA_DontShowOnScreen)) { + q->setAttribute(Qt::WA_Mapped, false); + if (q->isWindow() && q->windowModality() != Qt::NonModal && window) { + // remove our window from the modal window list + QGuiApplicationPrivate::hideModalWindow(window); + } + // do not return here, if window non-zero, we must hide it + } + + deactivateWidgetCleanup(); + + if (!q->isWindow()) { + QWidget *p = q->parentWidget(); + if (p &&p->isVisible()) { + if (renderToTexture) + p->d_func()->invalidateBuffer(q->geometry()); + else + invalidateBuffer(q->rect()); + } + } else { + invalidateBuffer(q->rect()); + } + + if (window) + window->setVisible(false); +} + /*! \fn bool QWidget::isHidden() const @@ -8213,6 +8871,7 @@ bool QWidget::event(QEvent *event) case QEvent::MacSizeChange: case QEvent::ContentsRectChange: case QEvent::ThemeChange: + case QEvent::ReadOnlyChange: changeEvent(event); break; @@ -8386,7 +9045,7 @@ bool QWidget::event(QEvent *event) QEvent::ModifiedChange, QEvent::MouseTrackingChange, QEvent::ParentChange, QEvent::WindowStateChange, QEvent::LanguageChange, QEvent::LocaleChange, - QEvent::LayoutDirectionChange. + QEvent::LayoutDirectionChange, QEvent::ReadOnlyChange. */ void QWidget::changeEvent(QEvent * event) @@ -9767,6 +10426,88 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) QWindowContainer::parentWasChanged(this); } +void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) +{ + Q_Q(QWidget); + + Qt::WindowFlags oldFlags = data.window_flags; + bool wasCreated = q->testAttribute(Qt::WA_WState_Created); + + int targetScreen = -1; + // Handle a request to move the widget to a particular screen + if (newparent && newparent->windowType() == Qt::Desktop) { + // make sure the widget is created on the same screen as the + // programmer specified desktop widget + + // get the desktop's screen number + targetScreen = newparent->window()->d_func()->topData()->screenIndex; + newparent = 0; + } + + setWinId(0); + + if (parent != newparent) { + QObjectPrivate::setParent_helper(newparent); //### why does this have to be done in the _sys function??? + if (q->windowHandle()) { + q->windowHandle()->setFlags(f); + QWidget *parentWithWindow = + newparent ? (newparent->windowHandle() ? newparent : newparent->nativeParentWidget()) : 0; + if (parentWithWindow) { + if (f & Qt::Window) { + q->windowHandle()->setTransientParent(parentWithWindow->windowHandle()); + q->windowHandle()->setParent(0); + } else { + q->windowHandle()->setTransientParent(0); + q->windowHandle()->setParent(parentWithWindow->windowHandle()); + } + } else { + q->windowHandle()->setTransientParent(0); + q->windowHandle()->setParent(0); + } + } + } + + if (!newparent) { + f |= Qt::Window; + if (targetScreen == -1) { + if (parent) + targetScreen = q->parentWidget()->window()->d_func()->topData()->screenIndex; + } + } + + bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); + + // Reparenting toplevel to child + if (wasCreated && !(f & Qt::Window) && (oldFlags & Qt::Window) && !q->testAttribute(Qt::WA_NativeWindow)) { + if (extra && extra->hasWindowContainer) + QWindowContainer::toplevelAboutToBeDestroyed(q); + q->destroy(); + } + + adjustFlags(f, q); + data.window_flags = f; + q->setAttribute(Qt::WA_WState_Created, false); + q->setAttribute(Qt::WA_WState_Visible, false); + q->setAttribute(Qt::WA_WState_Hidden, false); + + if (newparent && wasCreated && (q->testAttribute(Qt::WA_NativeWindow) || (f & Qt::Window))) + q->createWinId(); + + if (q->isWindow() || (!newparent || newparent->isVisible()) || explicitlyHidden) + q->setAttribute(Qt::WA_WState_Hidden); + q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); + + // move the window to the selected screen + if (!newparent && targetScreen != -1) { + if (maybeTopData()) + maybeTopData()->screenIndex = targetScreen; + // only if it is already created + if (q->testAttribute(Qt::WA_WState_Created)) { + q->windowHandle()->setScreen(QGuiApplication::screens().value(targetScreen, 0)); + } + } +} + /*! Scrolls the widget including its children \a dx pixels to the right and \a dy downward. Both \a dx and \a dy may be negative. @@ -9812,6 +10553,13 @@ void QWidget::scroll(int dx, int dy) d->scroll_sys(dx, dy); } +void QWidgetPrivate::scroll_sys(int dx, int dy) +{ + Q_Q(QWidget); + scrollChildren(dx, dy); + scrollRect(q->rect(), dx, dy); +} + /*! \overload @@ -9846,6 +10594,11 @@ void QWidget::scroll(int dx, int dy, const QRect &r) d->scroll_sys(dx, dy, r); } +void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) +{ + scrollRect(r, dx, dy); +} + /*! Repaints the widget directly by calling paintEvent() immediately, unless updates are disabled or the widget is hidden. @@ -10093,7 +10846,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) #ifdef Q_OS_WIN // ### Don't use PaintOnScreen+paintEngine() to do native painting in some future release if (attribute == Qt::WA_PaintOnScreen && on && windowType() != Qt::Desktop && !inherits("QGLWidget")) { - // see qwidget_qpa.cpp, ::paintEngine for details + // see ::paintEngine for details paintEngine(); if (d->noPaintOnScreen) return; @@ -10417,6 +11170,13 @@ void QWidget::setWindowOpacity(qreal opacity) #endif } +void QWidgetPrivate::setWindowOpacity_sys(qreal level) +{ + Q_Q(QWidget); + if (q->windowHandle()) + q->windowHandle()->setOpacity(level); +} + /*! \property QWidget::windowModified \brief whether the document shown in the window has unsaved changes @@ -10586,12 +11346,21 @@ QString QWidget::whatsThis() const \brief the widget's name as seen by assistive technologies - This property is used by accessible clients to identify, find, or announce - the widget for accessible clients. + This is the primary name by which assistive technology such as screen readers + announce this widget. For most widgets setting this property is not required. + For example for QPushButton the button's text will be used. + + It is important to set this property when the widget does not provide any + text. For example a button that only contains an icon needs to set this + property to work with screen readers. + The name should be short and equivalent to the visual information conveyed + by the widget. + + This property has to be \l{Internationalization with Qt}{localized}. By default, this property contains an empty string. - \sa QAccessibleInterface::text() + \sa QWidget::accessibleDescription, QAccessibleInterface::text() */ void QWidget::setAccessibleName(const QString &name) { @@ -10612,9 +11381,16 @@ QString QWidget::accessibleName() const \brief the widget's description as seen by assistive technologies - By default, this property contains an empty string. + The accessible description of a widget should convey what a widget does. + While the \l accessibleName should be a short and consise string (e.g. \gui{Save}), + the description should give more context, such as \gui{Saves the current document}. - \sa QAccessibleInterface::text() + This property has to be \l{Internationalization with Qt}{localized}. + + By default, this property contains an empty string and Qt falls back + to using the tool tip to provide this information. + + \sa QWidget::accessibleName, QAccessibleInterface::text() */ void QWidget::setAccessibleDescription(const QString &description) { @@ -10774,6 +11550,19 @@ void QWidget::raise() QApplication::sendEvent(this, &e); } +void QWidgetPrivate::raise_sys() +{ + Q_Q(QWidget); + if (q->isWindow() || q->testAttribute(Qt::WA_NativeWindow)) { + q->windowHandle()->raise(); + } else if (renderToTexture) { + if (QWidget *p = q->parentWidget()) { + setDirtyOpaqueRegion(); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + } + } +} + /*! Lowers the widget to the bottom of the parent widget's stack. @@ -10811,6 +11600,17 @@ void QWidget::lower() QApplication::sendEvent(this, &e); } +void QWidgetPrivate::lower_sys() +{ + Q_Q(QWidget); + if (q->isWindow() || q->testAttribute(Qt::WA_NativeWindow)) { + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + q->windowHandle()->lower(); + } else if (QWidget *p = q->parentWidget()) { + setDirtyOpaqueRegion(); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + } +} /*! Places the widget under \a w in the parent widget's stack. @@ -10847,6 +11647,14 @@ void QWidget::stackUnder(QWidget* w) QApplication::sendEvent(this, &e); } +void QWidgetPrivate::stackUnder_sys(QWidget*) +{ + Q_Q(QWidget); + if (QWidget *p = q->parentWidget()) { + setDirtyOpaqueRegion(); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + } +} /*! \fn bool QWidget::isTopLevel() const @@ -10907,6 +11715,24 @@ QRect QWidgetPrivate::frameStrut() const return maybeTopData() ? maybeTopData()->frameStrut : QRect(); } +void QWidgetPrivate::updateFrameStrut() +{ + Q_Q(QWidget); + if (q->data->fstrut_dirty) { + if (QTLWExtra *te = maybeTopData()) { + if (te->window) { + if (const QPlatformWindow *pw = te->window->handle()) { + const QMargins margins = pw->frameMargins(); + if (!margins.isNull()) { + te->frameStrut.setCoords(margins.left(), margins.top(), margins.right(), margins.bottom()); + q->data->fstrut_dirty = false; + } + } + } + } + } +} + #ifdef QT_KEYPAD_NAVIGATION /*! \internal @@ -11159,7 +11985,7 @@ QOpenGLContext *QWidgetPrivate::shareContext() const } QWidgetPrivate *that = const_cast<QWidgetPrivate *>(this); if (!extra->topextra->shareContext) { - QOpenGLContext *ctx = new QOpenGLContext(); + QOpenGLContext *ctx = new QOpenGLContext; ctx->setShareContext(QOpenGLContextPrivate::globalShareContext()); ctx->setFormat(extra->topextra->window->format()); ctx->create(); @@ -11169,6 +11995,24 @@ QOpenGLContext *QWidgetPrivate::shareContext() const #endif // QT_NO_OPENGL } +#ifndef QT_NO_OPENGL +void QWidgetPrivate::sendComposeStatus(QWidget *w, bool end) +{ + QWidgetPrivate *wd = QWidgetPrivate::get(w); + if (!wd->textureChildSeen) + return; + if (end) + wd->endCompose(); + else + wd->beginCompose(); + for (int i = 0; i < wd->children.size(); ++i) { + w = qobject_cast<QWidget *>(wd->children.at(i)); + if (w && !w->isWindow() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen) + sendComposeStatus(w, end); + } +} +#endif // QT_NO_OPENGL + Q_WIDGETS_EXPORT QWidgetData *qt_qwidget_data(QWidget *widget) { return widget->data; @@ -11243,6 +12087,50 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) This function is usually called from the QWidget destructor. */ +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ + Q_D(QWidget); + + d->aboutToDestroy(); + if (!isWindow() && parentWidget()) + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + d->deactivateWidgetCleanup(); + + if ((windowType() == Qt::Popup) && qApp) + qApp->d_func()->closePopup(this); + + if (this == QApplicationPrivate::active_window) + QApplication::setActiveWindow(0); + if (QWidget::mouseGrabber() == this) + releaseMouse(); + if (QWidget::keyboardGrabber() == this) + releaseKeyboard(); + + setAttribute(Qt::WA_WState_Created, false); + + if (windowType() != Qt::Desktop) { + if (destroySubWindows) { + QObjectList childList(children()); + for (int i = 0; i < childList.size(); i++) { + QWidget *widget = qobject_cast<QWidget *>(childList.at(i)); + if (widget && widget->testAttribute(Qt::WA_NativeWindow)) { + if (widget->windowHandle()) { + widget->destroy(); + } + } + } + } + if (destroyWindow) { + d->deleteTLSysExtra(); + } else { + if (parentWidget() && parentWidget()->testAttribute(Qt::WA_WState_Created)) { + d->hide_sys(); + } + } + + d->setWinId(0); + } +} /*! \fn QPaintEngine *QWidget::paintEngine() const @@ -11254,6 +12142,30 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) function is called by Qt internally, and the default implementation may not always return a valid pointer. */ +QPaintEngine *QWidget::paintEngine() const +{ + qWarning("QWidget::paintEngine: Should no longer be called"); + +#ifdef Q_OS_WIN + // We set this bit which is checked in setAttribute for + // Qt::WA_PaintOnScreen. We do this to allow these two scenarios: + // + // 1. Users accidentally set Qt::WA_PaintOnScreen on X and port to + // Windows which would mean suddenly their widgets stop working. + // + // 2. Users set paint on screen and subclass paintEngine() to + // return 0, in which case we have a "hole" in the backingstore + // allowing use of GDI or DirectX directly. + // + // 1 is WRONG, but to minimize silent failures, we have set this + // bit to ignore the setAttribute call. 2. needs to be + // supported because its our only means of embedding native + // graphics stuff. + const_cast<QWidgetPrivate *>(d_func())->noPaintOnScreen = 1; +#endif + + return 0; //##### @@@ +} /*! \fn QPoint QWidget::mapToGlobal(const QPoint &pos) const @@ -11264,6 +12176,21 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa mapFromGlobal(), mapTo(), mapToParent() */ +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + int x = pos.x(), y = pos.y(); + const QWidget *w = this; + while (w) { + QWindow *window = w->windowHandle(); + if (window && window->handle()) + return window->mapToGlobal(QPoint(x, y)); + + x += w->data->crect.x(); + y += w->data->crect.y(); + w = w->isWindow() ? 0 : w->parentWidget(); + } + return QPoint(x, y); +} /*! \fn QPoint QWidget::mapFromGlobal(const QPoint &pos) const @@ -11273,6 +12200,75 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa mapToGlobal(), mapFrom(), mapFromParent() */ +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + int x = pos.x(), y = pos.y(); + const QWidget *w = this; + while (w) { + QWindow *window = w->windowHandle(); + if (window && window->handle()) + return window->mapFromGlobal(QPoint(x, y)); + + x -= w->data->crect.x(); + y -= w->data->crect.y(); + w = w->isWindow() ? 0 : w->parentWidget(); + } + return QPoint(x, y); +} + +QWidget *qt_pressGrab = 0; +QWidget *qt_mouseGrb = 0; +static bool mouseGrabWithCursor = false; +static QWidget *keyboardGrb = 0; + +static inline QWindow *grabberWindow(const QWidget *w) +{ + QWindow *window = w->windowHandle(); + if (!window) + if (const QWidget *nativeParent = w->nativeParentWidget()) + window = nativeParent->windowHandle(); + return window; +} + +#ifndef QT_NO_CURSOR +static void grabMouseForWidget(QWidget *widget, const QCursor *cursor = 0) +#else +static void grabMouseForWidget(QWidget *widget) +#endif +{ + if (qt_mouseGrb) + qt_mouseGrb->releaseMouse(); + + mouseGrabWithCursor = false; + if (QWindow *window = grabberWindow(widget)) { +#ifndef QT_NO_CURSOR + if (cursor) { + mouseGrabWithCursor = true; + QGuiApplication::setOverrideCursor(*cursor); + } +#endif // !QT_NO_CURSOR + window->setMouseGrabEnabled(true); + } + + qt_mouseGrb = widget; + qt_pressGrab = 0; +} + +static void releaseMouseGrabOfWidget(QWidget *widget) +{ + if (qt_mouseGrb == widget) { + if (QWindow *window = grabberWindow(widget)) { +#ifndef QT_NO_CURSOR + if (mouseGrabWithCursor) { + QGuiApplication::restoreOverrideCursor(); + mouseGrabWithCursor = false; + } +#endif // !QT_NO_CURSOR + window->setMouseGrabEnabled(false); + } + } + qt_mouseGrb = 0; +} /*! \fn void QWidget::grabMouse() @@ -11303,6 +12299,10 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa releaseMouse(), grabKeyboard(), releaseKeyboard() */ +void QWidget::grabMouse() +{ + grabMouseForWidget(this); +} /*! \fn void QWidget::grabMouse(const QCursor &cursor) @@ -11320,6 +12320,21 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa releaseMouse(), grabKeyboard(), releaseKeyboard(), setCursor() */ +#ifndef QT_NO_CURSOR +void QWidget::grabMouse(const QCursor &cursor) +{ + grabMouseForWidget(this, &cursor); +} +#endif + +bool QWidgetPrivate::stealMouseGrab(bool grab) +{ + // This is like a combination of grab/releaseMouse() but with error checking + // and it has no effect on the result of mouseGrabber(). + Q_Q(QWidget); + QWindow *window = grabberWindow(q); + return window ? window->setMouseGrabEnabled(grab) : false; +} /*! \fn void QWidget::releaseMouse() @@ -11328,6 +12343,10 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa grabMouse(), grabKeyboard(), releaseKeyboard() */ +void QWidget::releaseMouse() +{ + releaseMouseGrabOfWidget(this); +} /*! \fn void QWidget::grabKeyboard() @@ -11348,6 +12367,23 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa releaseKeyboard(), grabMouse(), releaseMouse(), focusWidget() */ +void QWidget::grabKeyboard() +{ + if (keyboardGrb) + keyboardGrb->releaseKeyboard(); + if (QWindow *window = grabberWindow(this)) + window->setKeyboardGrabEnabled(true); + keyboardGrb = this; +} + +bool QWidgetPrivate::stealKeyboardGrab(bool grab) +{ + // This is like a combination of grab/releaseKeyboard() but with error + // checking and it has no effect on the result of keyboardGrabber(). + Q_Q(QWidget); + QWindow *window = grabberWindow(q); + return window ? window->setKeyboardGrabEnabled(grab) : false; +} /*! \fn void QWidget::releaseKeyboard() @@ -11356,6 +12392,14 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa grabKeyboard(), grabMouse(), releaseMouse() */ +void QWidget::releaseKeyboard() +{ + if (keyboardGrb == this) { + if (QWindow *window = grabberWindow(this)) + window->setKeyboardGrabEnabled(false); + keyboardGrb = 0; + } +} /*! \fn QWidget *QWidget::mouseGrabber() @@ -11367,6 +12411,12 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa grabMouse(), keyboardGrabber() */ +QWidget *QWidget::mouseGrabber() +{ + if (qt_mouseGrb) + return qt_mouseGrb; + return qt_pressGrab; +} /*! \fn QWidget *QWidget::keyboardGrabber() @@ -11378,6 +12428,10 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa grabMouse(), mouseGrabber() */ +QWidget *QWidget::keyboardGrabber() +{ + return keyboardGrb; +} /*! \fn void QWidget::activateWindow() @@ -11403,6 +12457,13 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \sa isActiveWindow(), window(), show() */ +void QWidget::activateWindow() +{ + QWindow *const wnd = window()->windowHandle(); + + if (wnd) + wnd->requestActivate(); +} /*! \fn int QWidget::metric(PaintDeviceMetric m) const @@ -11412,6 +12473,62 @@ void QWidget::ungrabGesture(Qt::GestureType gesture) \a m is the metric to get. */ +int QWidget::metric(PaintDeviceMetric m) const +{ + Q_D(const QWidget); + + QWindow *topLevelWindow = 0; + QScreen *screen = 0; + if (QWidget *topLevel = window()) + topLevelWindow = topLevel->windowHandle(); + + if (topLevelWindow) { + QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(topLevelWindow); + if (platformScreen) + screen = platformScreen->screen(); + } + if (!screen && QGuiApplication::primaryScreen()) + screen = QGuiApplication::primaryScreen(); + + if (!screen) { + if (m == PdmDpiX || m == PdmDpiY) + return 72; + return QPaintDevice::metric(m); + } + int val; + if (m == PdmWidth) { + val = data->crect.width(); + } else if (m == PdmWidthMM) { + val = data->crect.width() * screen->physicalSize().width() / screen->geometry().width(); + } else if (m == PdmHeight) { + val = data->crect.height(); + } else if (m == PdmHeightMM) { + val = data->crect.height() * screen->physicalSize().height() / screen->geometry().height(); + } else if (m == PdmDepth) { + return screen->depth(); + } else if (m == PdmDpiX) { + if (d->extra && d->extra->customDpiX) + return d->extra->customDpiX; + else if (d->parent) + return static_cast<QWidget *>(d->parent)->metric(m); + return qRound(screen->logicalDotsPerInchX()); + } else if (m == PdmDpiY) { + if (d->extra && d->extra->customDpiY) + return d->extra->customDpiY; + else if (d->parent) + return static_cast<QWidget *>(d->parent)->metric(m); + return qRound(screen->logicalDotsPerInchY()); + } else if (m == PdmPhysicalDpiX) { + return qRound(screen->physicalDotsPerInchX()); + } else if (m == PdmPhysicalDpiY) { + return qRound(screen->physicalDotsPerInchY()); + } else if (m == PdmDevicePixelRatio) { + return topLevelWindow ? topLevelWindow->devicePixelRatio() : qApp->devicePixelRatio(); + } else { + val = QPaintDevice::metric(m);// XXX + } + return val; +} /*! Initializes the \a painter pen, background and font to the same as @@ -11517,6 +12634,14 @@ void QWidget::setMask(const QRegion &newMask) #endif } +void QWidgetPrivate::setMask_sys(const QRegion ®ion) +{ + Q_Q(QWidget); + if (const QWindow *window = q->windowHandle()) + if (QPlatformWindow *platformWindow = window->handle()) + platformWindow->setMask(region); +} + /*! \fn void QWidget::setMask(const QBitmap &bitmap) diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h index 159011b824..9c22ad7c3d 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -694,7 +694,6 @@ private: friend class QPixmap; // for QPixmap::fill() friend class QFontMetrics; friend class QFontInfo; - friend class QETWidget; friend class QLayout; friend class QWidgetItem; friend class QWidgetItemV2; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 9586d1a8c9..011d456a39 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -61,6 +61,7 @@ #include "QtGui/qregion.h" #include "QtGui/qinputmethod.h" #include "QtGui/qopengl.h" +#include "QtGui/qsurfaceformat.h" #include "QtWidgets/qsizepolicy.h" #include "QtWidgets/qstyle.h" #include "QtWidgets/qapplication.h" @@ -392,6 +393,7 @@ public: QWidget::RenderFlags renderFlags); void drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, QPainter *sharedPainter = 0, QWidgetBackingStore *backingStore = 0); + void sendPaintEvent(const QRegion &toBePainted); void paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& children, int index, @@ -627,7 +629,11 @@ public: #ifndef QT_NO_OPENGL virtual GLuint textureId() const { return 0; } - + virtual QImage grabFramebuffer() { return QImage(); } + virtual void beginBackingStorePainting() { } + virtual void endBackingStorePainting() { } + virtual void beginCompose() { } + virtual void endCompose() { } void setRenderToTexture() { renderToTexture = true; setTextureChildSeen(); } void setTextureChildSeen() { @@ -642,6 +648,11 @@ public: get(parent)->setTextureChildSeen(); } } + static void sendComposeStatus(QWidget *w, bool end); + // When using a QOpenGLWidget as viewport with QAbstractScrollArea, resize events are + // filtered away from the widget. This is fine for QGLWidget but bad for QOpenGLWidget + // since the fbo must be resized. We need an alternative way to notify. + virtual void resizeViewportFramebuffer() { } #endif // Variables. @@ -732,7 +743,7 @@ public: // *************************** Platform specific ************************************ #if defined(Q_OS_WIN) - uint noPaintOnScreen : 1; // see qwidget_qpa.cpp ::paintEngine() + uint noPaintOnScreen : 1; // see qwidget.cpp ::paintEngine() #endif #if defined(Q_WS_X11) // <----------------------------------------------------------- X11 Qt::HANDLE picture; diff --git a/src/widgets/kernel/qwidget_qpa.cpp b/src/widgets/kernel/qwidget_qpa.cpp deleted file mode 100644 index 5a4bd33672..0000000000 --- a/src/widgets/kernel/qwidget_qpa.cpp +++ /dev/null @@ -1,1126 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "QtWidgets/qwidget.h" -#include "QtGui/qevent.h" -#include "QtWidgets/qapplication.h" -#include "private/qwidgetbackingstore_p.h" -#include "private/qwidget_p.h" -#include "private/qwidgetwindow_qpa_p.h" -#include "private/qapplication_p.h" -#include "QtWidgets/qdesktopwidget.h" -#include <qpa/qplatformwindow.h> -#include "QtGui/qsurfaceformat.h" -#include <QtGui/qopenglcontext.h> -#include <qpa/qplatformopenglcontext.h> -#include <qpa/qplatformintegration.h> -#include "QtGui/private/qwindow_p.h" -#include "QtGui/private/qguiapplication_p.h" -#include <private/qwindowcontainer_p.h> - -#include <qpa/qplatformcursor.h> -#include <QtGui/QGuiApplication> -#include <QtGui/QScreen> -#include <QtCore/QMargins> - -QT_BEGIN_NAMESPACE - -void q_createNativeChildrenAndSetParent(const QWidget *parentWidget) -{ - QObjectList children = parentWidget->children(); - for (int i = 0; i < children.size(); i++) { - if (children.at(i)->isWidgetType()) { - const QWidget *childWidget = qobject_cast<const QWidget *>(children.at(i)); - if (childWidget) { // should not be necessary - if (childWidget->testAttribute(Qt::WA_NativeWindow)) { - if (!childWidget->internalWinId()) - childWidget->winId(); - if (childWidget->windowHandle()) { - QWindow *parentWindow = childWidget->nativeParentWidget()->windowHandle(); - if (childWidget->isWindow()) - childWidget->windowHandle()->setTransientParent(parentWindow); - else - childWidget->windowHandle()->setParent(parentWindow); - } - } else { - q_createNativeChildrenAndSetParent(childWidget); - } - } - } - } - -} - -void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) -{ - Q_Q(QWidget); - - Q_UNUSED(window); - Q_UNUSED(initializeWindow); - Q_UNUSED(destroyOldWindow); - - Qt::WindowFlags flags = data.window_flags; - - if (!q->testAttribute(Qt::WA_NativeWindow) && !q->isWindow()) - return; // we only care about real toplevels - - QWindow *win = topData()->window; - // topData() ensures the extra is created but does not ensure 'window' is non-null - // in case the extra was already valid. - if (!win) { - createTLSysExtra(); - win = topData()->window; - } - - foreach (const QByteArray &propertyName, q->dynamicPropertyNames()) { - if (!qstrncmp(propertyName, "_q_platform_", 12)) - win->setProperty(propertyName, q->property(propertyName)); - } - -#ifdef Q_OS_OSX - if (q->testAttribute(Qt::WA_ShowWithoutActivating)) - win->setProperty("_q_showWithoutActivating", QVariant(true)); -#endif - win->setFlags(data.window_flags); - fixPosIncludesFrame(); - if (q->testAttribute(Qt::WA_Moved) - || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowManagement)) - win->setGeometry(q->geometry()); - else - win->resize(q->size()); - win->setScreen(QGuiApplication::screens().value(topData()->screenIndex, 0)); - - QSurfaceFormat format = win->requestedFormat(); - if ((flags & Qt::Window) && win->surfaceType() != QSurface::OpenGLSurface - && q->testAttribute(Qt::WA_TranslucentBackground)) { - format.setAlphaBufferSize(8); - } - win->setFormat(format); - - if (QWidget *nativeParent = q->nativeParentWidget()) { - if (nativeParent->windowHandle()) { - if (flags & Qt::Window) { - win->setTransientParent(nativeParent->window()->windowHandle()); - win->setParent(0); - } else { - win->setTransientParent(0); - win->setParent(nativeParent->windowHandle()); - } - } - } - - qt_window_private(win)->positionPolicy = topData()->posIncludesFrame ? - QWindowPrivate::WindowFrameInclusive : QWindowPrivate::WindowFrameExclusive; - win->create(); - // Enable nonclient-area events for QDockWidget and other NonClientArea-mouse event processing. - if ((flags & Qt::Desktop) == Qt::Window) - win->handle()->setFrameStrutEventsEnabled(true); - - data.window_flags = win->flags(); - - QBackingStore *store = q->backingStore(); - - if (!store) { - if (win && q->windowType() != Qt::Desktop) { - if (q->isTopLevel()) - q->setBackingStore(new QBackingStore(win)); - } else { - q->setAttribute(Qt::WA_PaintOnScreen, true); - } - } - - setWindowModified_helper(); - WId id = win->winId(); - // See the QPlatformWindow::winId() documentation - Q_ASSERT(id != WId(0)); - setWinId(id); - - // Check children and create windows for them if necessary - q_createNativeChildrenAndSetParent(q); - - if (extra && !extra->mask.isEmpty()) - setMask_sys(extra->mask); - - // If widget is already shown, set window visible, too - if (q->isVisible()) - win->setVisible(true); -} - -void QWidget::destroy(bool destroyWindow, bool destroySubWindows) -{ - Q_D(QWidget); - - d->aboutToDestroy(); - if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); - d->deactivateWidgetCleanup(); - - if ((windowType() == Qt::Popup) && qApp) - qApp->d_func()->closePopup(this); - - if (this == QApplicationPrivate::active_window) - QApplication::setActiveWindow(0); - if (QWidget::mouseGrabber() == this) - releaseMouse(); - if (QWidget::keyboardGrabber() == this) - releaseKeyboard(); - - setAttribute(Qt::WA_WState_Created, false); - - if (windowType() != Qt::Desktop) { - if (destroySubWindows) { - QObjectList childList(children()); - for (int i = 0; i < childList.size(); i++) { - QWidget *widget = qobject_cast<QWidget *>(childList.at(i)); - if (widget && widget->testAttribute(Qt::WA_NativeWindow)) { - if (widget->windowHandle()) { - widget->destroy(); - } - } - } - } - if (destroyWindow) { - d->deleteTLSysExtra(); - } else { - if (parentWidget() && parentWidget()->testAttribute(Qt::WA_WState_Created)) { - d->hide_sys(); - } - } - - d->setWinId(0); - } -} - -void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) -{ - Q_Q(QWidget); - - Qt::WindowFlags oldFlags = data.window_flags; - bool wasCreated = q->testAttribute(Qt::WA_WState_Created); - - int targetScreen = -1; - // Handle a request to move the widget to a particular screen - if (newparent && newparent->windowType() == Qt::Desktop) { - // make sure the widget is created on the same screen as the - // programmer specified desktop widget - - // get the desktop's screen number - targetScreen = newparent->window()->d_func()->topData()->screenIndex; - newparent = 0; - } - - setWinId(0); - - if (parent != newparent) { - QObjectPrivate::setParent_helper(newparent); //### why does this have to be done in the _sys function??? - if (q->windowHandle()) { - q->windowHandle()->setFlags(f); - QWidget *parentWithWindow = - newparent ? (newparent->windowHandle() ? newparent : newparent->nativeParentWidget()) : 0; - if (parentWithWindow) { - if (f & Qt::Window) { - q->windowHandle()->setTransientParent(parentWithWindow->windowHandle()); - q->windowHandle()->setParent(0); - } else { - q->windowHandle()->setTransientParent(0); - q->windowHandle()->setParent(parentWithWindow->windowHandle()); - } - } else { - q->windowHandle()->setTransientParent(0); - q->windowHandle()->setParent(0); - } - } - } - - if (!newparent) { - f |= Qt::Window; - if (targetScreen == -1) { - if (parent) - targetScreen = q->parentWidget()->window()->d_func()->topData()->screenIndex; - } - } - - bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); - - // Reparenting toplevel to child - if (wasCreated && !(f & Qt::Window) && (oldFlags & Qt::Window) && !q->testAttribute(Qt::WA_NativeWindow)) { - if (extra && extra->hasWindowContainer) - QWindowContainer::toplevelAboutToBeDestroyed(q); - q->destroy(); - } - - adjustFlags(f, q); - data.window_flags = f; - q->setAttribute(Qt::WA_WState_Created, false); - q->setAttribute(Qt::WA_WState_Visible, false); - q->setAttribute(Qt::WA_WState_Hidden, false); - - if (newparent && wasCreated && (q->testAttribute(Qt::WA_NativeWindow) || (f & Qt::Window))) - q->createWinId(); - - if (q->isWindow() || (!newparent || newparent->isVisible()) || explicitlyHidden) - q->setAttribute(Qt::WA_WState_Hidden); - q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); - - // move the window to the selected screen - if (!newparent && targetScreen != -1) { - if (maybeTopData()) - maybeTopData()->screenIndex = targetScreen; - // only if it is already created - if (q->testAttribute(Qt::WA_WState_Created)) { - q->windowHandle()->setScreen(QGuiApplication::screens().value(targetScreen, 0)); - } - } -} - -QPoint QWidget::mapToGlobal(const QPoint &pos) const -{ - int x = pos.x(), y = pos.y(); - const QWidget *w = this; - while (w) { - QWindow *window = w->windowHandle(); - if (window && window->handle()) - return window->mapToGlobal(QPoint(x, y)); - - x += w->data->crect.x(); - y += w->data->crect.y(); - w = w->isWindow() ? 0 : w->parentWidget(); - } - return QPoint(x, y); -} - -QPoint QWidget::mapFromGlobal(const QPoint &pos) const -{ - int x = pos.x(), y = pos.y(); - const QWidget *w = this; - while (w) { - QWindow *window = w->windowHandle(); - if (window && window->handle()) - return window->mapFromGlobal(QPoint(x, y)); - - x -= w->data->crect.x(); - y -= w->data->crect.y(); - w = w->isWindow() ? 0 : w->parentWidget(); - } - return QPoint(x, y); -} - -void QWidgetPrivate::updateSystemBackground() {} - -#ifndef QT_NO_CURSOR -void QWidgetPrivate::setCursor_sys(const QCursor &cursor) -{ - Q_UNUSED(cursor); - Q_Q(QWidget); - qt_qpa_set_cursor(q, false); -} - -void QWidgetPrivate::unsetCursor_sys() -{ - Q_Q(QWidget); - qt_qpa_set_cursor(q, false); -} - -#endif //QT_NO_CURSOR - -void QWidgetPrivate::setWindowTitle_sys(const QString &caption) -{ - Q_Q(QWidget); - if (!q->isWindow()) - return; - - if (QWindow *window = q->windowHandle()) - window->setTitle(caption); - -} - -void QWidgetPrivate::setWindowFilePath_sys(const QString &filePath) -{ - Q_Q(QWidget); - if (!q->isWindow()) - return; - - if (QWindow *window = q->windowHandle()) - window->setFilePath(filePath); -} - -void QWidgetPrivate::setWindowIcon_sys() -{ - Q_Q(QWidget); - if (QWindow *window = q->windowHandle()) - window->setIcon(q->windowIcon()); -} - -void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) -{ - Q_UNUSED(iconText); -} - -QWidget *qt_pressGrab = 0; -QWidget *qt_mouseGrb = 0; -static QWidget *keyboardGrb = 0; - -static inline QWindow *grabberWindow(const QWidget *w) -{ - QWindow *window = w->windowHandle(); - if (!window) - if (const QWidget *nativeParent = w->nativeParentWidget()) - window = nativeParent->windowHandle(); - return window; -} - -void QWidget::grabMouse() -{ - if (qt_mouseGrb) - qt_mouseGrb->releaseMouse(); - - if (QWindow *window = grabberWindow(this)) - window->setMouseGrabEnabled(true); - - qt_mouseGrb = this; - qt_pressGrab = 0; -} - -#ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &cursor) -{ - Q_UNUSED(cursor); - grabMouse(); -} -#endif - -bool QWidgetPrivate::stealMouseGrab(bool grab) -{ - // This is like a combination of grab/releaseMouse() but with error checking - // and it has no effect on the result of mouseGrabber(). - Q_Q(QWidget); - QWindow *window = grabberWindow(q); - return window ? window->setMouseGrabEnabled(grab) : false; -} - -void QWidget::releaseMouse() -{ - if (qt_mouseGrb == this) { - if (QWindow *window = grabberWindow(this)) - window->setMouseGrabEnabled(false); - qt_mouseGrb = 0; - } -} - -void QWidget::grabKeyboard() -{ - if (keyboardGrb) - keyboardGrb->releaseKeyboard(); - if (QWindow *window = grabberWindow(this)) - window->setKeyboardGrabEnabled(true); - keyboardGrb = this; -} - -bool QWidgetPrivate::stealKeyboardGrab(bool grab) -{ - // This is like a combination of grab/releaseKeyboard() but with error - // checking and it has no effect on the result of keyboardGrabber(). - Q_Q(QWidget); - QWindow *window = grabberWindow(q); - return window ? window->setKeyboardGrabEnabled(grab) : false; -} - -void QWidget::releaseKeyboard() -{ - if (keyboardGrb == this) { - if (QWindow *window = grabberWindow(this)) - window->setKeyboardGrabEnabled(false); - keyboardGrb = 0; - } -} - -QWidget *QWidget::mouseGrabber() -{ - if (qt_mouseGrb) - return qt_mouseGrb; - return qt_pressGrab; -} - -QWidget *QWidget::keyboardGrabber() -{ - return keyboardGrb; -} - -void QWidget::activateWindow() -{ - QWindow *const wnd = window()->windowHandle(); - - if (wnd) - wnd->requestActivate(); -} - -// move() was invoked with Qt::WA_WState_Created not set (frame geometry -// unknown), that is, crect has a position including the frame. -// If we can determine the frame strut, fix that and clear the flag. -void QWidgetPrivate::fixPosIncludesFrame() -{ - Q_Q(QWidget); - if (QTLWExtra *te = maybeTopData()) { - if (te->posIncludesFrame) { - // For Qt::WA_DontShowOnScreen, assume a frame of 0 (for - // example, in QGraphicsProxyWidget). - if (q->testAttribute(Qt::WA_DontShowOnScreen)) { - te->posIncludesFrame = 0; - } else { - if (q->windowHandle()) { - updateFrameStrut(); - if (!q->data->fstrut_dirty) { - data.crect.translate(te->frameStrut.x(), te->frameStrut.y()); - te->posIncludesFrame = 0; - } - } // windowHandle() - } // !WA_DontShowOnScreen - } // posIncludesFrame - } // QTLWExtra -} - -void QWidgetPrivate::show_sys() -{ - Q_Q(QWidget); - - QWindow *window = q->windowHandle(); - - if (q->testAttribute(Qt::WA_DontShowOnScreen)) { - invalidateBuffer(q->rect()); - q->setAttribute(Qt::WA_Mapped); - if (q->isWindow() && q->windowModality() != Qt::NonModal && window) { - // add our window to the modal window list - QGuiApplicationPrivate::showModalWindow(window); - } - return; - } - - if (renderToTexture && !q->isWindow()) - QApplication::postEvent(q->parentWidget(), new QUpdateLaterEvent(q->geometry())); - else - QApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); - - if (!q->isWindow() && !q->testAttribute(Qt::WA_NativeWindow)) - return; - - if (window) { - if (q->isWindow()) - fixPosIncludesFrame(); - QRect geomRect = q->geometry(); - if (!q->isWindow()) { - QPoint topLeftOfWindow = q->mapTo(q->nativeParentWidget(),QPoint()); - geomRect.moveTopLeft(topLeftOfWindow); - } - const QRect windowRect = window->geometry(); - if (windowRect != geomRect) { - if (q->testAttribute(Qt::WA_Moved) - || !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowManagement)) - window->setGeometry(geomRect); - else - window->resize(geomRect.size()); - } - -#ifndef QT_NO_CURSOR - qt_qpa_set_cursor(q, false); // Needed in case cursor was set before show -#endif - invalidateBuffer(q->rect()); - window->setVisible(true); - // Was the window moved by the Window system or QPlatformWindow::initialGeometry() ? - if (window->isTopLevel()) { - const QPoint crectTopLeft = q->data->crect.topLeft(); - const QPoint windowTopLeft = window->geometry().topLeft(); - if (crectTopLeft == QPoint(0, 0) && windowTopLeft != crectTopLeft) - q->data->crect.moveTopLeft(windowTopLeft); - } - } -} - - -void QWidgetPrivate::hide_sys() -{ - Q_Q(QWidget); - - QWindow *window = q->windowHandle(); - - if (q->testAttribute(Qt::WA_DontShowOnScreen)) { - q->setAttribute(Qt::WA_Mapped, false); - if (q->isWindow() && q->windowModality() != Qt::NonModal && window) { - // remove our window from the modal window list - QGuiApplicationPrivate::hideModalWindow(window); - } - // do not return here, if window non-zero, we must hide it - } - - deactivateWidgetCleanup(); - - if (!q->isWindow()) { - QWidget *p = q->parentWidget(); - if (p &&p->isVisible()) { - if (renderToTexture) - p->d_func()->invalidateBuffer(q->geometry()); - else - invalidateBuffer(q->rect()); - } - } else { - invalidateBuffer(q->rect()); - } - - if (window) - window->setVisible(false); -} - -Qt::WindowState effectiveState(Qt::WindowStates state) - { - if (state & Qt::WindowMinimized) - return Qt::WindowMinimized; - else if (state & Qt::WindowFullScreen) - return Qt::WindowFullScreen; - else if (state & Qt::WindowMaximized) - return Qt::WindowMaximized; - return Qt::WindowNoState; - } - -void QWidget::setWindowState(Qt::WindowStates newstate) -{ - Q_D(QWidget); - Qt::WindowStates oldstate = windowState(); - if (oldstate == newstate) - return; - if (isWindow() && !testAttribute(Qt::WA_WState_Created)) - create(); - - data->window_state = newstate; - data->in_set_window_state = 1; - Qt::WindowState newEffectiveState = effectiveState(newstate); - Qt::WindowState oldEffectiveState = effectiveState(oldstate); - if (isWindow() && newEffectiveState != oldEffectiveState) { - // Ensure the initial size is valid, since we store it as normalGeometry below. - if (!testAttribute(Qt::WA_Resized) && !isVisible()) - adjustSize(); - - d->createTLExtra(); - if (oldEffectiveState == Qt::WindowNoState) - d->topData()->normalGeometry = geometry(); - - Q_ASSERT(windowHandle()); - windowHandle()->setWindowState(newEffectiveState); - } - data->in_set_window_state = 0; - - if (newstate & Qt::WindowActive) - activateWindow(); - - QWindowStateChangeEvent e(oldstate); - QApplication::sendEvent(this, &e); -} - -void QWidgetPrivate::setFocus_sys() -{ - Q_Q(QWidget); - // Embedded native widget may have taken the focus; get it back to toplevel if that is the case - const QWidget *topLevel = q->window(); - if (topLevel->windowType() != Qt::Popup) { - if (QWindow *nativeWindow = q->window()->windowHandle()) { - if (nativeWindow != QGuiApplication::focusWindow() - && q->testAttribute(Qt::WA_WState_Created)) { - nativeWindow->requestActivate(); - } - } - } -} - -void QWidgetPrivate::raise_sys() -{ - Q_Q(QWidget); - if (q->isWindow() || q->testAttribute(Qt::WA_NativeWindow)) { - q->windowHandle()->raise(); - } else if (renderToTexture) { - if (QWidget *p = q->parentWidget()) { - setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); - } - } -} - -void QWidgetPrivate::lower_sys() -{ - Q_Q(QWidget); - if (q->isWindow() || q->testAttribute(Qt::WA_NativeWindow)) { - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - q->windowHandle()->lower(); - } else if (QWidget *p = q->parentWidget()) { - setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); - } -} - -void QWidgetPrivate::stackUnder_sys(QWidget*) -{ - Q_Q(QWidget); - if (QWidget *p = q->parentWidget()) { - setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); - } -} - -void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) -{ - Q_Q(QWidget); - if (extra) { // any size restrictions? - w = qMin(w,extra->maxw); - h = qMin(h,extra->maxh); - w = qMax(w,extra->minw); - h = qMax(h,extra->minh); - } - - if (q->isWindow() && q->windowHandle()) { - QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration(); - if (!integration->hasCapability(QPlatformIntegration::NonFullScreenWindows)) { - x = 0; - y = 0; - w = q->windowHandle()->width(); - h = q->windowHandle()->height(); - } - } - - QPoint oldp = q->geometry().topLeft(); - QSize olds = q->size(); - QRect r(x, y, w, h); - - bool isResize = olds != r.size(); - isMove = oldp != r.topLeft(); //### why do we have isMove as a parameter? - - - // We only care about stuff that changes the geometry, or may - // cause the window manager to change its state - if (r.size() == olds && oldp == r.topLeft()) - return; - - if (!data.in_set_window_state) { - q->data->window_state &= ~Qt::WindowMaximized; - q->data->window_state &= ~Qt::WindowFullScreen; - if (q->isWindow()) - topData()->normalGeometry = QRect(0, 0, -1, -1); - } - - QPoint oldPos = q->pos(); - data.crect = r; - - bool needsShow = false; - - if (!(data.window_state & Qt::WindowFullScreen) && (w == 0 || h == 0)) { - q->setAttribute(Qt::WA_OutsideWSRange, true); - if (q->isVisible() && q->testAttribute(Qt::WA_Mapped)) - hide_sys(); - data.crect = QRect(x, y, w, h); - } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) { - q->setAttribute(Qt::WA_OutsideWSRange, false); - needsShow = true; - } - - if (q->isVisible()) { - if (!q->testAttribute(Qt::WA_DontShowOnScreen) && !q->testAttribute(Qt::WA_OutsideWSRange)) { - if (q->windowHandle()) { - if (q->isWindow()) { - q->windowHandle()->setGeometry(q->geometry()); - } else { - QPoint posInNativeParent = q->mapTo(q->nativeParentWidget(),QPoint()); - q->windowHandle()->setGeometry(QRect(posInNativeParent,r.size())); - } - - if (needsShow) - show_sys(); - } - - if (!q->isWindow()) { - if (renderToTexture) { - QRegion updateRegion(q->geometry()); - updateRegion += QRect(oldPos, olds); - q->parentWidget()->d_func()->invalidateBuffer(updateRegion); - } else if (isMove && !isResize) { - moveRect(QRect(oldPos, olds), x - oldPos.x(), y - oldPos.y()); - } else { - invalidateBuffer_resizeHelper(oldPos, olds); - } - } - } - - // generate a move event for QWidgets without window handles. QWidgets with native - // window handles already receive a move event from - // QGuiApplicationPrivate::processGeometryChangeEvent. - if (isMove && (!q->windowHandle() || q->testAttribute(Qt::WA_DontShowOnScreen))) { - QMoveEvent e(q->pos(), oldPos); - QApplication::sendEvent(q, &e); - } - if (isResize) { - QResizeEvent e(r.size(), olds); - QApplication::sendEvent(q, &e); - if (q->windowHandle()) - q->update(); - } - } else { // not visible - if (isMove && q->pos() != oldPos) - q->setAttribute(Qt::WA_PendingMoveEvent, true); - if (isResize) - q->setAttribute(Qt::WA_PendingResizeEvent, true); - } - -} - -void QWidgetPrivate::setConstraints_sys() -{ - Q_Q(QWidget); - if (extra && q->windowHandle()) { - QWindow *win = q->windowHandle(); - QWindowPrivate *winp = qt_window_private(win); - - winp->minimumSize = QSize(extra->minw, extra->minh); - winp->maximumSize = QSize(extra->maxw, extra->maxh); - - if (extra->topextra) { - winp->baseSize = QSize(extra->topextra->basew, extra->topextra->baseh); - winp->sizeIncrement = QSize(extra->topextra->incw, extra->topextra->inch); - } - - if (winp->platformWindow) { - fixPosIncludesFrame(); - winp->platformWindow->propagateSizeHints(); - } - } -} - -void QWidgetPrivate::scroll_sys(int dx, int dy) -{ - Q_Q(QWidget); - scrollChildren(dx, dy); - scrollRect(q->rect(), dx, dy); -} - -void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) -{ - scrollRect(r, dx, dy); -} - -int QWidget::metric(PaintDeviceMetric m) const -{ - Q_D(const QWidget); - - QWindow *topLevelWindow = 0; - QScreen *screen = 0; - if (QWidget *topLevel = window()) - topLevelWindow = topLevel->windowHandle(); - - if (topLevelWindow) { - QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(topLevelWindow); - if (platformScreen) - screen = platformScreen->screen(); - } - if (!screen && QGuiApplication::primaryScreen()) - screen = QGuiApplication::primaryScreen(); - - if (!screen) { - if (m == PdmDpiX || m == PdmDpiY) - return 72; - return QPaintDevice::metric(m); - } - int val; - if (m == PdmWidth) { - val = data->crect.width(); - } else if (m == PdmWidthMM) { - val = data->crect.width() * screen->physicalSize().width() / screen->geometry().width(); - } else if (m == PdmHeight) { - val = data->crect.height(); - } else if (m == PdmHeightMM) { - val = data->crect.height() * screen->physicalSize().height() / screen->geometry().height(); - } else if (m == PdmDepth) { - return screen->depth(); - } else if (m == PdmDpiX) { - if (d->extra && d->extra->customDpiX) - return d->extra->customDpiX; - else if (d->parent) - return static_cast<QWidget *>(d->parent)->metric(m); - return qRound(screen->logicalDotsPerInchX()); - } else if (m == PdmDpiY) { - if (d->extra && d->extra->customDpiY) - return d->extra->customDpiY; - else if (d->parent) - return static_cast<QWidget *>(d->parent)->metric(m); - return qRound(screen->logicalDotsPerInchY()); - } else if (m == PdmPhysicalDpiX) { - return qRound(screen->physicalDotsPerInchX()); - } else if (m == PdmPhysicalDpiY) { - return qRound(screen->physicalDotsPerInchY()); - } else if (m == PdmDevicePixelRatio) { - return topLevelWindow ? topLevelWindow->devicePixelRatio() : qApp->devicePixelRatio(); - } else { - val = QPaintDevice::metric(m);// XXX - } - return val; -} - -/*! - If this is a native widget, return the associated QWindow. - Otherwise return null. - - Native widgets include toplevel widgets, QGLWidget, and child widgets - on which winId() was called. - - \since 5.0 - - \sa winId() -*/ -QWindow *QWidget::windowHandle() const -{ - Q_D(const QWidget); - QTLWExtra *extra = d->maybeTopData(); - if (extra) - return extra->window; - - return 0; -} - -void QWidgetPrivate::createSysExtra() -{ -} - -void QWidgetPrivate::deleteSysExtra() -{ - -} - -#ifdef Q_OS_WIN -static const char activeXNativeParentHandleProperty[] = "_q_embedded_native_parent_handle"; -#endif - -void QWidgetPrivate::createTLSysExtra() -{ - Q_Q(QWidget); - if (!extra->topextra->window && (q->testAttribute(Qt::WA_NativeWindow) || q->isWindow())) { - extra->topextra->window = new QWidgetWindow(q); - if (extra->minw || extra->minh) - extra->topextra->window->setMinimumSize(QSize(extra->minw, extra->minh)); - if (extra->maxw != QWIDGETSIZE_MAX || extra->maxh != QWIDGETSIZE_MAX) - extra->topextra->window->setMaximumSize(QSize(extra->maxw, extra->maxh)); - if (extra->topextra->opacity != 255 && q->isWindow()) - extra->topextra->window->setOpacity(qreal(extra->topextra->opacity) / qreal(255)); -#ifdef Q_OS_WIN - // Pass on native parent handle for Widget embedded into Active X. - const QVariant activeXNativeParentHandle = q->property(activeXNativeParentHandleProperty); - if (activeXNativeParentHandle.isValid()) - extra->topextra->window->setProperty(activeXNativeParentHandleProperty, activeXNativeParentHandle); - if (q->inherits("QTipLabel") || q->inherits("QAlphaWidget")) - extra->topextra->window->setProperty("_q_windowsDropShadow", QVariant(true)); -#endif - } - -} - -void QWidgetPrivate::deleteTLSysExtra() -{ - if (extra && extra->topextra) { - //the qplatformbackingstore may hold a reference to the window, so the backingstore - //needs to be deleted first. If the backingstore holds GL resources, we need to - // make the context current here, since the platform bs does not have a reference - // to the widget. - -#ifndef QT_NO_OPENGL - if (textureChildSeen && extra->topextra->shareContext) - extra->topextra->shareContext->makeCurrent(extra->topextra->window); -#endif - extra->topextra->backingStoreTracker.destroy(); - delete extra->topextra->backingStore; - extra->topextra->backingStore = 0; -#ifndef QT_NO_OPENGL - if (textureChildSeen && extra->topextra->shareContext) - extra->topextra->shareContext->doneCurrent(); - delete extra->topextra->shareContext; - extra->topextra->shareContext = 0; -#endif - - //the toplevel might have a context with a "qglcontext associated with it. We need to - //delete the qglcontext before we delete the qplatformopenglcontext. - //One unfortunate thing about this is that we potentially create a glContext just to - //delete it straight afterwards. - if (extra->topextra->window) { - extra->topextra->window->destroy(); - } - setWinId(0); - delete extra->topextra->window; - extra->topextra->window = 0; - - } -} - -void QWidgetPrivate::registerDropSite(bool on) -{ - Q_UNUSED(on); -} - -void QWidgetPrivate::setMask_sys(const QRegion ®ion) -{ - if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowMasks)) { - qWarning("%s: Not supported on %s.", Q_FUNC_INFO, qPrintable(QGuiApplication::platformName())); - return; - } - Q_Q(QWidget); - if (const QWindow *window = q->windowHandle()) - if (QPlatformWindow *platformWindow = window->handle()) - platformWindow->setMask(region); -} - -void QWidgetPrivate::updateFrameStrut() -{ - Q_Q(QWidget); - if (q->data->fstrut_dirty) { - if (QTLWExtra *te = maybeTopData()) { - if (te->window) { - if (const QPlatformWindow *pw = te->window->handle()) { - const QMargins margins = pw->frameMargins(); - if (!margins.isNull()) { - te->frameStrut.setCoords(margins.left(), margins.top(), margins.right(), margins.bottom()); - q->data->fstrut_dirty = false; - } - } - } - } - } -} - -void QWidgetPrivate::setWindowOpacity_sys(qreal level) -{ - Q_Q(QWidget); - if (q->windowHandle()) - q->windowHandle()->setOpacity(level); -} - -void QWidgetPrivate::setWSGeometry() -{ - Q_Q(QWidget); - if (QWindow *window = q->windowHandle()) - window->setGeometry(data.crect); -} - -QPaintEngine *QWidget::paintEngine() const -{ - qWarning("QWidget::paintEngine: Should no longer be called"); - -#ifdef Q_OS_WIN - // We set this bit which is checked in setAttribute for - // Qt::WA_PaintOnScreen. We do this to allow these two scenarios: - // - // 1. Users accidentally set Qt::WA_PaintOnScreen on X and port to - // Windows which would mean suddenly their widgets stop working. - // - // 2. Users set paint on screen and subclass paintEngine() to - // return 0, in which case we have a "hole" in the backingstore - // allowing use of GDI or DirectX directly. - // - // 1 is WRONG, but to minimize silent failures, we have set this - // bit to ignore the setAttribute call. 2. needs to be - // supported because its our only means of embedding native - // graphics stuff. - const_cast<QWidgetPrivate *>(d_func())->noPaintOnScreen = 1; -#endif - - return 0; //##### @@@ -} - -void QWidgetPrivate::setModal_sys() -{ - Q_Q(QWidget); - if (q->windowHandle()) - q->windowHandle()->setModality(q->windowModality()); -} - -#ifndef QT_NO_CURSOR -static inline void applyCursor(QWidget *w, QCursor c) -{ - if (QWindow *window = w->windowHandle()) - window->setCursor(c); -} - -static inline void unsetCursor(QWidget *w) -{ - if (QWindow *window = w->windowHandle()) - window->unsetCursor(); -} - -void qt_qpa_set_cursor(QWidget *w, bool force) -{ - if (!w->testAttribute(Qt::WA_WState_Created)) - return; - - static QPointer<QWidget> lastUnderMouse = 0; - if (force) { - lastUnderMouse = w; - } else if (lastUnderMouse) { - const WId lastWinId = lastUnderMouse->effectiveWinId(); - const WId winId = w->effectiveWinId(); - if (lastWinId && lastWinId == winId) - w = lastUnderMouse; - } else if (!w->internalWinId()) { - return; // The mouse is not under this widget, and it's not native, so don't change it. - } - - while (!w->internalWinId() && w->parentWidget() && !w->isWindow() - && !w->testAttribute(Qt::WA_SetCursor)) - w = w->parentWidget(); - - QWidget *nativeParent = w; - if (!w->internalWinId()) - nativeParent = w->nativeParentWidget(); - if (!nativeParent || !nativeParent->internalWinId()) - return; - - if (w->isWindow() || w->testAttribute(Qt::WA_SetCursor)) { - if (w->isEnabled()) - applyCursor(nativeParent, w->cursor()); - else - // Enforce the windows behavior of clearing the cursor on - // disabled widgets. - unsetCursor(nativeParent); - } else { - unsetCursor(nativeParent); - } -} -#endif //QT_NO_CURSOR - -QT_END_NAMESPACE diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index 9d024fd359..6f635611f0 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -74,7 +74,8 @@ extern QRegion qt_dirtyRegion(QWidget *); * \a region is the region to be updated in \a widget coordinates. */ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, - QWidget *tlw, const QPoint &tlwOffset, QPlatformTextureList *widgetTextures) + QWidget *tlw, const QPoint &tlwOffset, QPlatformTextureList *widgetTextures, + QWidgetBackingStore *widgetBackingStore) { #ifdef QT_NO_OPENGL Q_UNUSED(widgetTextures); @@ -92,33 +93,29 @@ void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion ®ion, QBack QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); #endif - //The performance hit by doing this should be negligible. However, be aware that - //using this FPS when you have > 1 windowsurface can give you inaccurate FPS + if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) + return; static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt(); if (fpsDebug) { - static QTime time = QTime::currentTime(); - static int frames = 0; - - frames++; - - if(time.elapsed() > 5000) { - double fps = double(frames * 1000) /time.restart(); - fprintf(stderr,"FPS: %.1f\n",fps); - frames = 0; + if (!widgetBackingStore->perfFrames++) + widgetBackingStore->perfTime.start(); + if (widgetBackingStore->perfTime.elapsed() > 5000) { + double fps = double(widgetBackingStore->perfFrames * 1000) / widgetBackingStore->perfTime.restart(); + qDebug("FPS: %.1f\n", fps); + widgetBackingStore->perfFrames = 0; } } - if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) - return; - QPoint offset = tlwOffset; if (widget != tlw) offset += widget->mapTo(tlw, QPoint()); #ifndef QT_NO_OPENGL - if (widgetTextures) + if (widgetTextures) { + widget->window()->d_func()->sendComposeStatus(widget->window(), false); backingStore->handle()->composeAndFlush(widget->windowHandle(), region, offset, widgetTextures, tlw->d_func()->shareContext()); - else + widget->window()->d_func()->sendComposeStatus(widget->window(), true); + } else #endif backingStore->flush(region, widget->windowHandle(), offset); } @@ -270,7 +267,7 @@ void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn) return; const QPoint offset = widget->mapTo(tlw, QPoint()); - qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset); + qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset, 0, tlw->d_func()->maybeBackingStore()); } #endif // QT_NO_PAINT_DEBUG @@ -519,6 +516,8 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, const QPoint offset = widget->mapTo(tlw, QPoint()); if (QWidgetPrivate::get(widget)->renderToTexture) { + if (!widget->d_func()->inDirtyList) + addDirtyRenderToTextureWidget(widget); if (!updateRequestSent || updateTime == UpdateNow) sendUpdateRequest(tlw, updateTime); return; @@ -613,6 +612,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, } if (QWidgetPrivate::get(widget)->renderToTexture) { + if (!widget->d_func()->inDirtyList) + addDirtyRenderToTextureWidget(widget); if (!updateRequestSent || updateTime == UpdateNow) sendUpdateRequest(tlw, updateTime); return; @@ -710,6 +711,7 @@ void QWidgetBackingStore::removeDirtyWidget(QWidget *w) dirtyWidgetsRemoveAll(w); dirtyOnScreenWidgetsRemoveAll(w); + dirtyRenderToTextureWidgets.removeAll(w); resetWidget(w); QWidgetPrivate *wd = w->d_func(); @@ -744,7 +746,8 @@ QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) widgetTextures(0), fullUpdatePending(0), updateRequestSent(0), - textureListWatcher(0) + textureListWatcher(0), + perfFrames(0) { store = tlw->backingStore(); Q_ASSERT(store); @@ -755,9 +758,11 @@ QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) QWidgetBackingStore::~QWidgetBackingStore() { - for (int c = 0; c < dirtyWidgets.size(); ++c) { + for (int c = 0; c < dirtyWidgets.size(); ++c) resetWidget(dirtyWidgets.at(c)); - } + for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c) + resetWidget(dirtyRenderToTextureWidgets.at(c)); + #ifndef QT_NO_OPENGL delete dirtyOnScreenWidgets; #endif @@ -944,7 +949,7 @@ void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedReg // Nothing to repaint. if (!isDirty() && store->size().isValid()) { - qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTextures); + qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset, widgetTextures, this); return; } @@ -961,7 +966,8 @@ static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatfo { QWidgetPrivate *wd = QWidgetPrivate::get(widget); if (wd->renderToTexture) - widgetTextures->appendTexture(wd->textureId(), QRect(widget->mapTo(tlw, QPoint()), widget->size())); + widgetTextures->appendTexture(wd->textureId(), QRect(widget->mapTo(tlw, QPoint()), widget->size()), + widget->testAttribute(Qt::WA_AlwaysStackOnTop)); for (int i = 0; i < wd->children.size(); ++i) { QWidget *w = qobject_cast<QWidget *>(wd->children.at(i)); @@ -1125,13 +1131,47 @@ void QWidgetBackingStore::doSync() #endif if (toClean.isEmpty()) { - // Nothing to repaint. However, we might have newly exposed areas on the - // screen if this function was called from sync(QWidget *, QRegion)), so - // we have to make sure those are flushed. + // Nothing to repaint. However renderToTexture widgets are handled + // specially, they are not in the regular dirty list, in order to + // prevent triggering unnecessary backingstore painting when only the + // OpenGL content changes. Check if we have such widgets in the special + // dirty list. + for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) { + QWidget *w = dirtyRenderToTextureWidgets.at(i); + w->d_func()->sendPaintEvent(w->rect()); + resetWidget(w); + } + dirtyRenderToTextureWidgets.clear(); + + // We might have newly exposed areas on the screen if this function was + // called from sync(QWidget *, QRegion)), so we have to make sure those + // are flushed. We also need to composite the renderToTexture widgets. flush(); + return; } + // There is something other dirty than the renderToTexture widgets. + // Now it is time to include the renderToTexture ones among the others. + if (widgetTextures && widgetTextures->count()) { + for (int i = 0; i < widgetTextures->count(); ++i) { + const QRect rect = widgetTextures->geometry(i); // mapped to the tlw already + dirty += rect; + toClean += rect; + } + } + // The dirtyRenderToTextureWidgets list is useless here, so just reset. As + // unintuitive as it is, we need to send paint events to renderToTexture + // widgets always when something (any widget) needs to be updated, even if + // the renderToTexture widget itself is clean, i.e. there was no update() + // call for it. This is because changing any widget will cause a flush and + // so a potentially blocking buffer swap for the window, and skipping paints + // for the renderToTexture widgets would make it impossible to have smoothly + // animated content in them. + for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) + resetWidget(dirtyRenderToTextureWidgets.at(i)); + dirtyRenderToTextureWidgets.clear(); + #ifndef QT_NO_GRAPHICSVIEW if (tlw->d_func()->extra->proxyWidget) { updateStaticContentsSize(); @@ -1200,15 +1240,17 @@ void QWidgetBackingStore::flush(QWidget *widget) { if (!dirtyOnScreen.isEmpty()) { QWidget *target = widget ? widget : tlw; - qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTextures); + qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset, widgetTextures, this); dirtyOnScreen = QRegion(); + if (widgetTextures && widgetTextures->count()) + return; } if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty()) { #ifndef QT_NO_OPENGL if (widgetTextures && widgetTextures->count()) { QWidget *target = widget ? widget : tlw; - qt_flush(target, QRegion(), store, tlw, tlwOffset, widgetTextures); + qt_flush(target, QRegion(), store, tlw, tlwOffset, widgetTextures, this); } #endif return; @@ -1218,7 +1260,7 @@ void QWidgetBackingStore::flush(QWidget *widget) QWidget *w = dirtyOnScreenWidgets->at(i); QWidgetPrivate *wd = w->d_func(); Q_ASSERT(wd->needsFlush); - qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset); + qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset, 0, this); *wd->needsFlush = QRegion(); } dirtyOnScreenWidgets->clear(); diff --git a/src/widgets/kernel/qwidgetbackingstore_p.h b/src/widgets/kernel/qwidgetbackingstore_p.h index 473e9deef4..0da85e33d5 100644 --- a/src/widgets/kernel/qwidgetbackingstore_p.h +++ b/src/widgets/kernel/qwidgetbackingstore_p.h @@ -133,6 +133,7 @@ private: QRegion dirty; // needsRepaint QRegion dirtyFromPreviousSync; QVector<QWidget *> dirtyWidgets; + QVector<QWidget *> dirtyRenderToTextureWidgets; QVector<QWidget *> *dirtyOnScreenWidgets; QList<QWidget *> staticWidgets; QPlatformTextureList *widgetTextures; @@ -143,6 +144,8 @@ private: QPoint tlwOffset; QPlatformTextureListWatcher *textureListWatcher; + QElapsedTimer perfTime; + int perfFrames; void sendUpdateRequest(QWidget *widget, UpdateTime updateTime); @@ -150,7 +153,8 @@ private: static void unflushPaint(QWidget *widget, const QRegion &rgn); static void qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, QWidget *tlw, const QPoint &tlwOffset, - QPlatformTextureList *widgetTextures = 0); + QPlatformTextureList *widgetTextures, + QWidgetBackingStore *widgetBackingStore); void doSync(); bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); @@ -184,6 +188,16 @@ private: } } + inline void addDirtyRenderToTextureWidget(QWidget *widget) + { + if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { + QWidgetPrivate *widgetPrivate = widget->d_func(); + Q_ASSERT(widgetPrivate->renderToTexture); + dirtyRenderToTextureWidgets.append(widget); + widgetPrivate->inDirtyList = true; + } + } + inline void dirtyWidgetsRemoveAll(QWidget *widget) { int i = 0; @@ -294,7 +308,6 @@ private: friend QRegion qt_dirtyRegion(QWidget *); friend class QWidgetPrivate; friend class QWidget; - friend class QETWidget; friend class QBackingStore; }; diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index fc328e7af0..b255bc16f9 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include "private/qwindow_p.h" -#include "qwidgetwindow_qpa_p.h" +#include "qwidgetwindow_p.h" #include "private/qwidget_p.h" #include "private/qapplication_p.h" @@ -443,7 +443,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) // Use postEvent() to ensure the local QEventLoop terminates when called from QMenu::exec() const QPoint localPos = win->mapFromGlobal(event->globalPos()); QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPos(), event->button(), event->buttons(), event->modifiers()); - e->spont = 1; + QCoreApplicationPrivate::setEventSpontaneous(e, true); QGuiApplicationPrivate::setMouseEventSource(e, QGuiApplicationPrivate::mouseEventSource(event)); e->setTimestamp(event->timestamp()); QCoreApplication::postEvent(win, e); @@ -795,7 +795,7 @@ void QWidgetWindow::handleTabletEvent(QTabletEvent *event) QPointF mapped = qt_tablet_target->mapFromGlobal(event->globalPos()) + delta; QTabletEvent ev(event->type(), mapped, event->globalPosF(), event->device(), event->pointerType(), event->pressure(), event->xTilt(), event->yTilt(), event->tangentialPressure(), - event->rotation(), event->z(), event->modifiers(), event->uniqueId()); + event->rotation(), event->z(), event->modifiers(), event->uniqueId(), event->button(), event->buttons()); ev.setTimestamp(event->timestamp()); QGuiApplication::sendSpontaneousEvent(qt_tablet_target, &ev); } diff --git a/src/widgets/kernel/qwidgetwindow_qpa_p.h b/src/widgets/kernel/qwidgetwindow_p.h index 06ba8ea646..be2adddff5 100644 --- a/src/widgets/kernel/qwidgetwindow_qpa_p.h +++ b/src/widgets/kernel/qwidgetwindow_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QWIDGETWINDOW_QPA_P_H -#define QWIDGETWINDOW_QPA_P_H +#ifndef QWIDGETWINDOW_P_H +#define QWIDGETWINDOW_P_H #include <QtGui/qwindow.h> @@ -122,4 +122,4 @@ private: QT_END_NAMESPACE -#endif // QWIDGETWINDOW_QPA_P_H +#endif // QWIDGETWINDOW_P_H diff --git a/src/widgets/statemachine/qguistatemachine.cpp b/src/widgets/statemachine/qguistatemachine.cpp index d737a1e323..9824b96334 100644 --- a/src/widgets/statemachine/qguistatemachine.cpp +++ b/src/widgets/statemachine/qguistatemachine.cpp @@ -312,6 +312,8 @@ static QEvent *cloneEvent(QEvent *e) me2->setButtons(me->buttons()); me2->setButton(me->button()); me2->setModifiers(me->modifiers()); + me2->setSource(me->source()); + me2->setFlags(me->flags()); return me2; } diff --git a/src/widgets/styles/qandroidstyle.cpp b/src/widgets/styles/qandroidstyle.cpp index ceb95aa125..9f241c42e1 100644 --- a/src/widgets/styles/qandroidstyle.cpp +++ b/src/widgets/styles/qandroidstyle.cpp @@ -118,6 +118,7 @@ QAndroidStyle::QAndroidStyle() : QFusionStyle() { QPixmapCache::clear(); + checkBoxControl = NULL; QString stylePath(QLatin1String(qgetenv("MINISTRO_ANDROID_STYLE_PATH"))); const QLatin1Char slashChar('/'); if (!stylePath.isEmpty() && !stylePath.endsWith(slashChar)) @@ -127,8 +128,7 @@ QAndroidStyle::QAndroidStyle() if (!androidTheme.isEmpty() && !androidTheme.endsWith(slashChar)) androidTheme += slashChar; - if (stylePath.isEmpty()) - { + if (stylePath.isEmpty()) { stylePath = QLatin1String("/data/data/org.kde.necessitas.ministro/files/dl/style/") + QLatin1String(qgetenv("QT_ANDROID_THEME_DISPLAY_DPI")) + slashChar; } @@ -143,7 +143,7 @@ QAndroidStyle::QAndroidStyle() QJsonParseError error; QJsonDocument document = QJsonDocument::fromJson(f.readAll(), &error); - if (document.isNull()) { + if (document.isNull()) { qCritical() << error.errorString(); return; } @@ -155,8 +155,8 @@ QAndroidStyle::QAndroidStyle() QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); - QHash<int, QPalette> * palettes = reinterpret_cast<QHash<int, QPalette> *>(nativeInterface->nativeResourceForIntegration("AndroidStylePalettes")); - QHash<int, QFont> * fonts = reinterpret_cast<QHash<int, QFont> *>(nativeInterface->nativeResourceForIntegration("AndroidStyleFonts")); + QHash<int, QPalette> *palettes = reinterpret_cast<QHash<int, QPalette> *>(nativeInterface->nativeResourceForIntegration("AndroidStylePalettes")); + QHash<int, QFont> *fonts = reinterpret_cast<QHash<int, QFont> *>(nativeInterface->nativeResourceForIntegration("AndroidStyleFonts")); palettes->clear(); fonts->clear(); QJsonObject object = document.object(); @@ -214,7 +214,7 @@ QAndroidStyle::QAndroidStyle() } if (!qtClassName.isEmpty()) QApplication::setFont(font, qtClassName.toUtf8()); - if (ft>-1) + if (ft > -1) fonts->insert(ft, font); // Extract font information } @@ -256,6 +256,9 @@ QAndroidStyle::QAndroidStyle() switch (itemType) { case QC_Checkbox: + checkBoxControl = new AndroidCompoundButtonControl(item.toVariantMap(), itemType); + m_androidControlsHash[int(itemType)] = checkBoxControl; + break; case QC_RadioButton: m_androidControlsHash[int(itemType)] = new AndroidCompoundButtonControl(item.toVariantMap(), itemType); @@ -516,16 +519,31 @@ void QAndroidStyle::drawPrimitive(PrimitiveElement pe, ? m_androidControlsHash.find(itemType) : m_androidControlsHash.end(); if (it != m_androidControlsHash.end()) { - if (itemType != QC_EditText) + if (itemType != QC_EditText) { it.value()->drawControl(opt, p, w); - else { + } else { QStyleOption copy(*opt); copy.state &= ~QStyle::State_Sunken; it.value()->drawControl(©, p, w); } - } - else + } else if (pe == PE_FrameGroupBox) { + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt); + if (frame2 && (frame2->features & QStyleOptionFrameV2::Flat)) { + QRect fr = frame->rect; + QPoint p1(fr.x(), fr.y() + 1); + QPoint p2(fr.x() + fr.width(), p1.y()); + qDrawShadeLine(p, p1, p2, frame->palette, true, + frame->lineWidth, frame->midLineWidth); + } else { + qDrawShadeRect(p, frame->rect.x(), frame->rect.y(), frame->rect.width(), + frame->rect.height(), frame->palette, true, + frame->lineWidth, frame->midLineWidth); + } + } + } else { QFusionStyle::drawPrimitive(pe, opt, p, w); + } } @@ -575,6 +593,36 @@ void QAndroidStyle::drawControl(QStyle::ControlElement element, default: break; } + } else if (element == CE_ItemViewItem) { + const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt); + if (vopt && vopt->features & QStyleOptionViewItem::HasCheckIndicator) { + p->save(); + p->setClipRect(opt->rect); + + QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, w); + + // draw the background + proxy()->drawPrimitive(PE_PanelItemViewItem, opt, p, w); + + // draw the check mark + QStyleOptionViewItem option(*vopt); + option.rect = checkRect; + option.state = option.state & ~QStyle::State_HasFocus; + + switch (vopt->checkState) { + case Qt::Unchecked: + option.state |= QStyle::State_Off; + break; + default: + option.state |= QStyle::State_On; + break; + } + QPixmap pixmap = checkBoxControl->imgCheckBox(&option); + p->drawPixmap(checkRect, pixmap); + p->restore(); + } else { + QFusionStyle::drawControl(element, opt, p, w); + } } else { QFusionStyle::drawControl(element, opt, p, w); } @@ -584,6 +632,8 @@ QRect QAndroidStyle::subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget) const { + if (subElement == SE_LineEditContents) // fix spinbox display problem + return QFusionStyle::subElementRect(subElement, option, widget); const ItemType itemType = qtControl(subElement); AndroidControlsHash::const_iterator it = itemType != QC_UnknownType ? m_androidControlsHash.find(itemType) @@ -602,10 +652,74 @@ void QAndroidStyle::drawComplexControl(ComplexControl cc, AndroidControlsHash::const_iterator it = itemType != QC_UnknownType ? m_androidControlsHash.find(itemType) : m_androidControlsHash.end(); - if (it != m_androidControlsHash.end()) + if (it != m_androidControlsHash.end()) { it.value()->drawControl(opt, p, widget); - else - QFusionStyle::drawComplexControl(cc, opt, p, widget); + return; + } + if (cc == CC_GroupBox) { + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + // Draw frame + QRect textRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, widget); + QRect checkBoxRect; + if (groupBox->subControls & SC_GroupBoxCheckBox) + checkBoxRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, widget); + if (groupBox->subControls & QStyle::SC_GroupBoxFrame) { + QStyleOptionFrameV2 frame; + frame.QStyleOption::operator=(*groupBox); + frame.features = groupBox->features; + frame.lineWidth = groupBox->lineWidth; + frame.midLineWidth = groupBox->midLineWidth; + frame.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, widget); + p->save(); + QRegion region(groupBox->rect); + if (!groupBox->text.isEmpty()) { + bool ltr = groupBox->direction == Qt::LeftToRight; + QRect finalRect; + if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) { + finalRect = checkBoxRect.united(textRect); + finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0); + } else { + finalRect = textRect; + } + region -= finalRect; + } + p->setClipRegion(region); + drawPrimitive(PE_FrameGroupBox, &frame, p, widget); + p->restore(); + } + + // Draw title + if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) { + QColor textColor = groupBox->textColor; + if (textColor.isValid()) + p->setPen(textColor); + int alignment = int(groupBox->textAlignment); + if (!styleHint(QStyle::SH_UnderlineShortcut, opt, widget)) + alignment |= Qt::TextHideMnemonic; + + drawItemText(p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment, + groupBox->palette, groupBox->state & State_Enabled, groupBox->text, + textColor.isValid() ? QPalette::NoRole : QPalette::WindowText); + + if (groupBox->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*groupBox); + fropt.rect = textRect; + drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); + } + } + + // Draw checkbox + if (groupBox->subControls & SC_GroupBoxCheckBox) { + QStyleOptionButton box; + box.QStyleOption::operator=(*groupBox); + box.rect = checkBoxRect; + checkBoxControl->drawControl(&box, p, widget); + } + } + return; + } + QFusionStyle::drawComplexControl(cc, opt, p, widget); } QStyle::SubControl QAndroidStyle::hitTestComplexControl(ComplexControl cc, @@ -649,6 +763,52 @@ QRect QAndroidStyle::subControlRect(ComplexControl cc, : m_androidControlsHash.end(); if (it != m_androidControlsHash.end()) return it.value()->subControlRect(opt, sc, widget); + QRect rect = opt->rect; + switch (cc) { + case CC_GroupBox: { + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2); + QSize checkBoxSize = checkBoxControl->sizeCheckBox(opt); + int indicatorWidth = checkBoxSize.width(); + int indicatorHeight = checkBoxSize.height(); + QRect checkBoxRect; + if (opt->subControls & QStyle::SC_GroupBoxCheckBox) { + checkBoxRect.setWidth(indicatorWidth); + checkBoxRect.setHeight(indicatorHeight); + } + checkBoxRect.moveLeft(1); + QRect textRect = checkBoxRect; + textRect.setSize(textSize); + if (opt->subControls & QStyle::SC_GroupBoxCheckBox) + textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2); + if (sc == SC_GroupBoxFrame) { + rect = opt->rect.adjusted(0, 0, 0, 0); + rect.translate(0, textRect.height() / 2); + rect.setHeight(rect.height() - textRect.height() / 2); + } else if (sc == SC_GroupBoxContents) { + QRect frameRect = opt->rect.adjusted(0, 0, 0, -groupBox->lineWidth); + int margin = 3; + int leftMarginExtension = 0; + int topMargin = qMax(pixelMetric(PM_ExclusiveIndicatorHeight), opt->fontMetrics.height()) + groupBox->lineWidth; + frameRect.adjust(leftMarginExtension + margin, margin + topMargin, -margin, -margin - groupBox->lineWidth); + frameRect.translate(0, textRect.height() / 2); + rect = frameRect; + rect.setHeight(rect.height() - textRect.height() / 2); + } else if (sc == SC_GroupBoxCheckBox) { + rect = checkBoxRect; + } else if (sc == SC_GroupBoxLabel) { + rect = textRect; + } + return visualRect(opt->direction, opt->rect, rect); + } + + return rect; + } + + default: + break; + } + return QFusionStyle::subControlRect(cc, opt, sc, widget); } @@ -664,6 +824,10 @@ int QAndroidStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, case PM_SpinBoxFrameWidth: case PM_ScrollBarExtent: return 0; + case PM_IndicatorWidth: + return checkBoxControl->sizeCheckBox(option).width(); + case PM_IndicatorHeight: + return checkBoxControl->sizeCheckBox(option).height(); default: return QFusionStyle::pixelMetric(metric, option, widget); } @@ -676,12 +840,61 @@ QSize QAndroidStyle::sizeFromContents(ContentsType ct, const QWidget *w) const { QSize sz = QFusionStyle::sizeFromContents(ct, opt, contentsSize, w); + if (ct == CT_HeaderSection) { + if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + bool nullIcon = hdr->icon.isNull(); + int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w); + int iconSize = nullIcon ? 0 : checkBoxControl->sizeCheckBox(opt).width(); + QSize txt; +/* + * These next 4 lines are a bad hack to fix a bug in case a QStyleSheet is applied at QApplication level. + * In that case, even if the stylesheet does not refer to headers, the header font is changed to application + * font, which is wrong. Even worst, hdr->fontMetrics(...) does not report the proper size. + */ + if (qApp->styleSheet().isEmpty()) + txt = hdr->fontMetrics.size(0, hdr->text); + else + txt = qApp->fontMetrics().size(0, hdr->text); + + sz.setHeight(margin + qMax(iconSize, txt.height()) + margin); + sz.setWidth((nullIcon ? 0 : margin) + iconSize + + (hdr->text.isNull() ? 0 : margin) + txt.width() + margin); + if (hdr->sortIndicator != QStyleOptionHeader::None) { + int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w); + if (hdr->orientation == Qt::Horizontal) + sz.rwidth() += sz.height() + margin; + else + sz.rheight() += sz.width() + margin; + } + return sz; + } + } const ItemType itemType = qtControl(ct); AndroidControlsHash::const_iterator it = itemType != QC_UnknownType ? m_androidControlsHash.find(itemType) : m_androidControlsHash.end(); if (it != m_androidControlsHash.end()) return it.value()->sizeFromContents(opt, sz, w); + if (ct == CT_GroupBox) { + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2); + QSize checkBoxSize = checkBoxControl->sizeCheckBox(opt); + int indicatorWidth = checkBoxSize.width(); + int indicatorHeight = checkBoxSize.height(); + QRect checkBoxRect; + if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) { + checkBoxRect.setWidth(indicatorWidth); + checkBoxRect.setHeight(indicatorHeight); + } + checkBoxRect.moveLeft(1); + QRect textRect = checkBoxRect; + textRect.setSize(textSize); + if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) + textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2); + QRect u = textRect.united(checkBoxRect); + sz = QSize(sz.width(), sz.height() + u.height()); + } + } return sz; } @@ -712,7 +925,7 @@ int QAndroidStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, return RSIP_OnMouseClick; default: - return QFusionStyle::styleHint(hint,option, widget, returnData); + return QFusionStyle::styleHint(hint, option, widget, returnData); } } @@ -761,6 +974,13 @@ QSize QAndroidStyle::AndroidDrawable::size() const return QSize(); } +QPixmap QAndroidStyle::AndroidDrawable::img() const +{ + if (type() == Image || type() == NinePatch) + return static_cast<const QAndroidStyle::AndroidImageDrawable *>(this)->img(); + + return QPixmap(); +} QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidDrawable::fromMap(const QVariantMap &drawable, ItemType itemType) @@ -828,6 +1048,18 @@ void QAndroidStyle::AndroidImageDrawable::draw(QPainter *painter, const QStyleOp painter->drawPixmap(opt->rect.x(), (opt->rect.height() - pm.height()) / 2, pm); } +QPixmap QAndroidStyle::AndroidImageDrawable::img() const +{ + if (m_hashKey.isEmpty()) + m_hashKey = QFileInfo(m_filePath).fileName(); + + QPixmap pm; + if (!QPixmapCache::find(m_hashKey, &pm)) { + pm.load(m_filePath); + QPixmapCache::insert(m_hashKey, pm); + } + return pm; +} QSize QAndroidStyle::AndroidImageDrawable::size() const { @@ -886,7 +1118,7 @@ void QAndroidStyle::Android9PatchDrawable::extractIntArray(const QVariantList &v } -void QAndroidStyle::Android9PatchDrawable::draw(QPainter * painter, const QStyleOption *opt) const +void QAndroidStyle::Android9PatchDrawable::draw(QPainter *painter, const QStyleOption *opt) const { if (m_hashKey.isEmpty()) m_hashKey = QFileInfo(m_filePath).fileName(); @@ -897,7 +1129,7 @@ void QAndroidStyle::Android9PatchDrawable::draw(QPainter * painter, const QStyle QPixmapCache::insert(m_hashKey, pixmap); } - const QRect &bounds=opt->rect; + const QRect &bounds = opt->rect; // shamelessly stolen from Android's sources (NinepatchImpl.cpp) and adapted for Qt const int pixmapWidth = pixmap.width(); @@ -1052,11 +1284,11 @@ QAndroidStyle::AndroidGradientDrawable::AndroidGradientDrawable(const QVariantMa QVariantList colors = drawable.value(QLatin1String("colors")).toList(); QVariantList positions = drawable.value(QLatin1String("positions")).toList(); - int min=colors.size() < positions.size() ? colors.size() : positions.size(); + int min = colors.size() < positions.size() ? colors.size() : positions.size(); for (int i = 0; i < min; i++) m_gradient.setColorAt(positions.at(i).toDouble(), QRgb(colors.at(i).toInt())); - QByteArray orientation=drawable.value(QLatin1String("orientation")).toByteArray(); + QByteArray orientation = drawable.value(QLatin1String("orientation")).toByteArray(); if (orientation == "TOP_BOTTOM") // draw the gradient from the top to the bottom m_orientation = TOP_BOTTOM; else if (orientation == "TR_BL") // draw the gradient from the top-right to the bottom-left @@ -1089,43 +1321,43 @@ void QAndroidStyle::AndroidGradientDrawable::draw(QPainter *painter, const QStyl switch (m_orientation) { case TOP_BOTTOM: // draw the gradient from the top to the bottom - m_gradient.setStart(width/2,0); - m_gradient.setFinalStop(width/2,height); + m_gradient.setStart(width / 2, 0); + m_gradient.setFinalStop(width / 2, height); break; case TR_BL: // draw the gradient from the top-right to the bottom-left - m_gradient.setStart(width,0); - m_gradient.setFinalStop(0,height); + m_gradient.setStart(width, 0); + m_gradient.setFinalStop(0, height); break; case RIGHT_LEFT: // draw the gradient from the right to the left - m_gradient.setStart(width,height/2); - m_gradient.setFinalStop(0,height/2); + m_gradient.setStart(width, height / 2); + m_gradient.setFinalStop(0, height / 2); break; case BR_TL: // draw the gradient from the bottom-right to the top-left - m_gradient.setStart(width,height); - m_gradient.setFinalStop(0,0); + m_gradient.setStart(width, height); + m_gradient.setFinalStop(0, 0); break; case BOTTOM_TOP: // draw the gradient from the bottom to the top - m_gradient.setStart(width/2,height); - m_gradient.setFinalStop(width/2,0); + m_gradient.setStart(width / 2, height); + m_gradient.setFinalStop(width / 2, 0); break; case BL_TR: // draw the gradient from the bottom-left to the top-right - m_gradient.setStart(0,height); - m_gradient.setFinalStop(width,0); + m_gradient.setStart(0, height); + m_gradient.setFinalStop(width, 0); break; case LEFT_RIGHT: // draw the gradient from the left to the right - m_gradient.setStart(0,height/2); - m_gradient.setFinalStop(width,height/2); + m_gradient.setStart(0, height / 2); + m_gradient.setFinalStop(width, height / 2); break; case TL_BR: // draw the gradient from the top-left to the bottom-right - m_gradient.setStart(0,0); - m_gradient.setFinalStop(width,height); + m_gradient.setStart(0, 0); + m_gradient.setFinalStop(width, height); break; } @@ -1140,7 +1372,7 @@ void QAndroidStyle::AndroidGradientDrawable::draw(QPainter *painter, const QStyl QSize QAndroidStyle::AndroidGradientDrawable::size() const { - return QSize(m_radius*2, m_radius*2); + return QSize(m_radius * 2, m_radius * 2); } QAndroidStyle::AndroidClipDrawable::AndroidClipDrawable(const QVariantMap &drawable, @@ -1172,9 +1404,9 @@ void QAndroidStyle::AndroidClipDrawable::draw(QPainter *painter, const QStyleOpt { QStyleOption copy(*opt); if (m_orientation == Qt::Horizontal) - copy.rect.setWidth(copy.rect.width()*m_factor); + copy.rect.setWidth(copy.rect.width() * m_factor); else - copy.rect.setHeight(copy.rect.height()*m_factor); + copy.rect.setHeight(copy.rect.height() * m_factor); m_drawable->draw(painter, ©); } @@ -1212,12 +1444,28 @@ QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidStateDrawable::type() c void QAndroidStyle::AndroidStateDrawable::draw(QPainter *painter, const QStyleOption *opt) const { - const AndroidDrawable *drawable=bestAndroidStateMatch(opt); + const AndroidDrawable *drawable = bestAndroidStateMatch(opt); if (drawable) drawable->draw(painter, opt); } +QSize QAndroidStyle::AndroidStateDrawable::sizeImage(const QStyleOption *opt) const +{ + QSize s; + const AndroidDrawable *drawable = bestAndroidStateMatch(opt); + if (drawable) + s = drawable->size(); + return s; +} +QPixmap QAndroidStyle::AndroidStateDrawable::img(const QStyleOption *opt) const +{ + QPixmap pm; + const AndroidDrawable *drawable = bestAndroidStateMatch(opt); + if (drawable) + pm = drawable->img(); + return pm; +} -const QAndroidStyle::AndroidDrawable* QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const +const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const { const AndroidDrawable *bestMatch = 0; if (!opt) { @@ -1226,7 +1474,7 @@ const QAndroidStyle::AndroidDrawable* QAndroidStyle::AndroidStateDrawable::bestA return bestMatch; } - uint bestCost=0xffff; + uint bestCost = 0xffff; foreach (const StateType & state, m_states) { if (int(opt->state) == state.first) return state.second; @@ -1376,15 +1624,13 @@ void QAndroidStyle::AndroidLayerDrawable::setFactor(int id, double factor, Qt::O void QAndroidStyle::AndroidLayerDrawable::draw(QPainter *painter, const QStyleOption *opt) const { - foreach (const LayerType &layer, m_layers) - { - if (layer.first == m_id) - { + foreach (const LayerType &layer, m_layers) { + if (layer.first == m_id) { QStyleOption copy(*opt); if (m_orientation == Qt::Horizontal) - copy.rect.setWidth(copy.rect.width()*m_factor); + copy.rect.setWidth(copy.rect.width() * m_factor); else - copy.rect.setHeight(copy.rect.height()*m_factor); + copy.rect.setHeight(copy.rect.height() * m_factor); layer.second->draw(painter, ©); } else { layer.second->draw(painter, opt); @@ -1418,7 +1664,7 @@ QAndroidStyle::AndroidControl::AndroidControl(const QVariantMap &control, m_background = 0; it = control.find(QLatin1String("View_minWidth")); - if (it!=control.end()) + if (it != control.end()) m_minSize.setWidth(it.value().toInt()); it = control.find(QLatin1String("View_minHeight")); @@ -1534,10 +1780,9 @@ QSize QAndroidStyle::AndroidControl::sizeFromContents(const QStyleOption *opt, QMargins QAndroidStyle::AndroidControl::padding() { - if (const AndroidDrawable *drawable = m_background) - { + if (const AndroidDrawable *drawable = m_background) { if (drawable->type() == State) - drawable=static_cast<const AndroidStateDrawable *>(m_background)->bestAndroidStateMatch(0); + drawable = static_cast<const AndroidStateDrawable *>(m_background)->bestAndroidStateMatch(0); return drawable->padding(); } return QMargins(); @@ -1573,8 +1818,17 @@ void QAndroidStyle::AndroidCompoundButtonControl::drawControl(const QStyleOption if (m_button) m_button->draw(p, opt); } - -const QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidCompoundButtonControl::backgroundDrawable() const +QSize QAndroidStyle::AndroidCompoundButtonControl::sizeCheckBox(const QStyleOption *opt) const +{ + const AndroidDrawable *drawable = m_button; + return static_cast<const QAndroidStyle::AndroidStateDrawable *>(drawable)->sizeImage(opt); +} +QPixmap QAndroidStyle::AndroidCompoundButtonControl::imgCheckBox(const QStyleOption *opt) const +{ + const AndroidDrawable *drawable = m_button; + return static_cast<const QAndroidStyle::AndroidStateDrawable *>(drawable)->img(opt); +} +const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidCompoundButtonControl::backgroundDrawable() const { return m_background ? m_background : m_button; } @@ -1637,10 +1891,10 @@ void QAndroidStyle::AndroidProgressBarControl::drawControl(const QStyleOption *o if (m_progressDrawable->type() == QAndroidStyle::Layer) { QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId); if (clipDrawable->type() == QAndroidStyle::Clip) - static_cast<QAndroidStyle::AndroidClipDrawable *>(clipDrawable)->setFactor(double(progressBarV2.progress)/double(progressBarV2.maximum-progressBarV2.minimum), + static_cast<QAndroidStyle::AndroidClipDrawable *>(clipDrawable)->setFactor(double(progressBarV2.progress) / double(progressBarV2.maximum - progressBarV2.minimum), progressBarV2.orientation); else - static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId,double(progressBarV2.progress)/double(progressBarV2.maximum-progressBarV2.minimum), + static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, double(progressBarV2.progress) / double(progressBarV2.maximum - progressBarV2.minimum), progressBarV2.orientation); } m_progressDrawable->draw(p, option); @@ -1659,11 +1913,11 @@ QRect QAndroidStyle::AndroidProgressBarControl::subElementRect(QStyle::SubElemen return option->rect; QMargins padding = m_background->padding(); - QRect p(padding.left(), padding.top(), padding.right()-padding.left(), padding.bottom()-padding.top()); + QRect p(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top()); padding = m_indeterminateDrawable->padding(); - p |= QRect(padding.left(), padding.top(), padding.right()-padding.left(), padding.bottom()-padding.top()); + p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top()); padding = m_progressDrawable->padding(); - p |= QRect(padding.left(), padding.top(), padding.right()-padding.left(), padding.bottom()-padding.top()); + p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top()); QRect r = option->rect.adjusted(p.left(), p.top(), -p.right(), -p.bottom()); @@ -1734,26 +1988,27 @@ void QAndroidStyle::AndroidSeekBarControl::drawControl(const QStyleOption *optio if (const QStyleOptionSlider *styleOption = qstyleoption_cast<const QStyleOptionSlider *>(option)) { - double factor = double(styleOption->sliderPosition)/double(styleOption->maximum-styleOption->minimum); - if (m_progressDrawable->type()==QAndroidStyle::Layer) { + double factor = double(styleOption->sliderPosition - styleOption->minimum) + / double(styleOption->maximum - styleOption->minimum); + if (m_progressDrawable->type() == QAndroidStyle::Layer) { QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId); if (clipDrawable->type() == QAndroidStyle::Clip) static_cast<QAndroidStyle::AndroidClipDrawable *>(clipDrawable)->setFactor(factor, styleOption->orientation); else static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, factor, styleOption->orientation); } - const AndroidDrawable *drawable=m_seekBarThumb; + const AndroidDrawable *drawable = m_seekBarThumb; if (drawable->type() == State) drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option); QStyleOption copy(*option); - copy.rect.setY((copy.rect.height()-m_minSize.height())/2); + copy.rect.setY((copy.rect.height() - m_minSize.height()) / 2); copy.rect.setHeight(m_minSize.height()); copy.rect.setWidth(copy.rect.width() - drawable->size().width()); - copy.rect.translate(drawable->size().width()/2, 0); + copy.rect.translate(drawable->size().width() / 2, 0); m_progressDrawable->draw(p, ©); if (styleOption->orientation == Qt::Vertical) qCritical() << "Vertical slider are not supported"; - int pos = copy.rect.width()*factor - drawable->size().width()/2; + int pos = copy.rect.width()*factor - drawable->size().width() / 2; copy.rect.translate(pos, 0); copy.rect.setSize(drawable->size()); m_seekBarThumb->draw(p, ©); @@ -1767,7 +2022,7 @@ QSize QAndroidStyle::AndroidSeekBarControl::sizeFromContents(const QStyleOption QSize sz = AndroidProgressBarControl::sizeFromContents(opt, contentsSize, w); if (!m_seekBarThumb) return sz; - const AndroidDrawable *drawable=m_seekBarThumb; + const AndroidDrawable *drawable = m_seekBarThumb; if (drawable->type() == State) drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(opt); return sz.expandedTo(drawable->size()); @@ -1786,7 +2041,8 @@ QRect QAndroidStyle::AndroidSeekBarControl::subControlRect(const QStyleOptionCom drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option); QRect r(option->rect); - double factor = double(styleOption->sliderPosition) / (styleOption->maximum - styleOption->minimum); + double factor = double(styleOption->sliderPosition - styleOption->minimum) + / (styleOption->maximum - styleOption->minimum); int pos = option->rect.width() * factor - double(drawable->size().width() / 2); r.setX(r.x() + pos); r.setSize(drawable->size()); diff --git a/src/widgets/styles/qandroidstyle_p.h b/src/widgets/styles/qandroidstyle_p.h index d64e37b9db..5334882908 100644 --- a/src/widgets/styles/qandroidstyle_p.h +++ b/src/widgets/styles/qandroidstyle_p.h @@ -132,6 +132,7 @@ public: static AndroidDrawable *fromMap(const QVariantMap &drawable, ItemType itemType); static QMargins extractMargins(const QVariantMap &value); virtual void setPaddingLeftToSizeWidth(); + QPixmap img() const; protected: ItemType m_itemType; QMargins m_padding; @@ -156,6 +157,7 @@ public: virtual void draw(QPainter *painter,const QStyleOption *opt) const; virtual QSize size() const; + QPixmap img() const; protected: QString m_filePath; mutable QString m_hashKey; @@ -228,7 +230,8 @@ public: inline const AndroidDrawable *bestAndroidStateMatch(const QStyleOption *opt) const; static int extractState(const QVariantMap &value); virtual void setPaddingLeftToSizeWidth(); - + QSize sizeImage(const QStyleOption *opt) const; + QPixmap img(const QStyleOption *opt) const; private: typedef QPair<int, const AndroidDrawable *> StateType; QList<StateType> m_states; @@ -281,7 +284,8 @@ public: AndroidCompoundButtonControl(const QVariantMap &control, ItemType itemType); virtual ~AndroidCompoundButtonControl(); virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w); - + QSize sizeCheckBox(const QStyleOption *opt) const; + QPixmap imgCheckBox(const QStyleOption *opt) const; protected: virtual const AndroidDrawable * backgroundDrawable() const; const AndroidDrawable *m_button; @@ -387,6 +391,7 @@ private: typedef QHash<int, AndroidControl *> AndroidControlsHash; AndroidControlsHash m_androidControlsHash; QPalette m_standardPalette; + AndroidCompoundButtonControl *checkBoxControl; }; #endif // QT_NO_STYLE_ANDROID diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index d7030c5d72..d8b67edff8 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -1155,7 +1155,7 @@ void QCommonStylePrivate::startAnimation(QStyleAnimation *animation) const stopAnimation(animation->target()); q->connect(animation, SIGNAL(destroyed()), SLOT(_q_removeAnimation()), Qt::UniqueConnection); animations.insert(animation->target(), animation); - animation->start(QAbstractAnimation::DeleteWhenStopped); + animation->start(); } /*! \internal */ @@ -4930,6 +4930,9 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget ret = hint.toChar().unicode(); break; } + case SH_LineEdit_PasswordMaskDelay: + ret = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::PasswordMaskDelay).toInt(); + break; case SH_ToolBox_SelectedPageTitleBold: ret = 1; break; diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 82c53def7d..d1b5bcc95c 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -749,6 +749,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, painter->drawRect(rect); QColor checkMarkColor = option->palette.text().color().darker(120); + const int checkMarkPadding = QStyleHelper::dpiScaled(3); if (checkbox->state & State_NoChange) { gradient = QLinearGradient(rect.topLeft(), rect.bottomLeft()); @@ -759,10 +760,10 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, checkMarkColor.setAlpha(180); painter->setPen(QPen(checkMarkColor, 1)); painter->setBrush(gradient); - painter->drawRect(rect.adjusted(3, 3, -3, -3)); + painter->drawRect(rect.adjusted(checkMarkPadding, checkMarkPadding, -checkMarkPadding, -checkMarkPadding)); } else if (checkbox->state & (State_On)) { - QPen checkPen = QPen(checkMarkColor, 1.8); + QPen checkPen = QPen(checkMarkColor, QStyleHelper::dpiScaled(1.8)); checkMarkColor.setAlpha(210); painter->translate(-1, 0.5); painter->setPen(checkPen); @@ -771,9 +772,9 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, // Draw checkmark QPainterPath path; - path.moveTo(5, rect.height() / 2.0); - path.lineTo(rect.width() / 2.0 - 0, rect.height() - 3); - path.lineTo(rect.width() - 2.5, 3); + path.moveTo(2 + checkMarkPadding, rect.height() / 2.0); + path.lineTo(rect.width() / 2.0, rect.height() - checkMarkPadding); + path.lineTo(rect.width() - checkMarkPadding - 0.5, checkMarkPadding); painter->drawPath(path.translated(rect.topLeft())); } } @@ -786,7 +787,9 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, painter->setBrush((state & State_Sunken) ? pressedColor : option->palette.base().color()); painter->setRenderHint(QPainter::Antialiasing, true); QPainterPath circle; - circle.addEllipse(rect.center() + QPoint(1.0, 1.0), 6.5, 6.5); + const QPointF circleCenter = rect.center() + QPoint(1, 1); + const qreal outlineRadius = (rect.width() + (rect.width() + 1) % 2) / 2.0 - 1; + circle.addEllipse(circleCenter, outlineRadius, outlineRadius); painter->setPen(QPen(option->palette.background().color().darker(150))); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) painter->setPen(QPen(highlightedOutline)); @@ -794,7 +797,8 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, if (state & (State_On )) { circle = QPainterPath(); - circle.addEllipse(rect.center() + QPoint(1, 1), 2.8, 2.8); + const qreal checkmarkRadius = outlineRadius / 2.32; + circle.addEllipse(circleCenter, checkmarkRadius, checkmarkRadius); QColor checkMarkColor = option->palette.text().color().darker(120); checkMarkColor.setAlpha(200); painter->setPen(checkMarkColor); @@ -1604,8 +1608,8 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio else pixmap = menuItem->icon.pixmap(iconSize, mode); - int pixw = pixmap.width(); - int pixh = pixmap.height(); + const int pixw = pixmap.width() / pixmap.devicePixelRatio(); + const int pixh = pixmap.height() / pixmap.devicePixelRatio(); QRect pmr(0, 0, pixw, pixh); pmr.moveCenter(vCheckRect.center()); @@ -3016,97 +3020,120 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption */ int QFusionStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const { + int val = -1; switch (metric) { case PM_SliderTickmarkOffset: - return 4; + val = 4; + break; case PM_HeaderMargin: - return 2; case PM_ToolTipLabelFrameWidth: - return 2; + val = 2; + break; case PM_ButtonDefaultIndicator: - return 0; case PM_ButtonShiftHorizontal: case PM_ButtonShiftVertical: - return 0; + val = 0; + break; case PM_MessageBoxIconSize: - return 48; + val = 48; + break; case PM_ListViewIconSize: - return 24; + val = 24; + break; case PM_DialogButtonsSeparator: case PM_ScrollBarSliderMin: - return 26; + val = 26; + break; case PM_TitleBarHeight: - return 24; + val = 24; + break; case PM_ScrollBarExtent: - return 14; + val = 14; + break; case PM_SliderThickness: - return 15; case PM_SliderLength: - return 15; + val = 15; + break; case PM_DockWidgetTitleMargin: - return 1; - case PM_DefaultFrameWidth: - return 1; + val = 1; + break; case PM_SpinBoxFrameWidth: - return 3; + val = 3; + break; case PM_MenuVMargin: case PM_MenuHMargin: - return 0; case PM_MenuPanelWidth: - return 0; + val = 0; + break; case PM_MenuBarItemSpacing: - return 6; + val = 6; + break; case PM_MenuBarVMargin: - return 0; case PM_MenuBarHMargin: - return 0; case PM_MenuBarPanelWidth: - return 0; + val = 0; + break; case PM_ToolBarHandleExtent: - return 9; + val = 9; + break; case PM_ToolBarItemSpacing: - return 1; + val = 1; + break; case PM_ToolBarFrameWidth: - return 2; case PM_ToolBarItemMargin: - return 2; + val = 2; + break; case PM_SmallIconSize: - return 16; case PM_ButtonIconSize: - return 16; + val = 16; + break; case PM_DockWidgetTitleBarButtonMargin: - return 2; + val = 2; + break; case PM_MaximumDragDistance: - return -1; + val = -1; + break; case PM_TabCloseIndicatorWidth: case PM_TabCloseIndicatorHeight: - return 20; + val = 20; + break; case PM_TabBarTabVSpace: - return 12; + val = 12; + break; case PM_TabBarTabOverlap: - return 1; + val = 1; + break; case PM_TabBarBaseOverlap: - return 2; + val = 2; + break; case PM_SubMenuOverlap: - return -1; + val = -1; + break; case PM_DockWidgetHandleExtent: case PM_SplitterWidth: - return 4; + val = 4; + break; case PM_IndicatorHeight: case PM_IndicatorWidth: case PM_ExclusiveIndicatorHeight: case PM_ExclusiveIndicatorWidth: - return 14; + val = 14; + break; case PM_ScrollView_ScrollBarSpacing: - return 0; + val = 0; + break; case PM_ScrollView_ScrollBarOverlap: if (proxy()->styleHint(SH_ScrollBar_Transient, option, widget)) return proxy()->pixelMetric(PM_ScrollBarExtent, option, widget); - return 0; - default: + val = 0; break; + case PM_DefaultFrameWidth: + return 1; // Do not dpi-scale because the drawn frame is always exactly 1 pixel thick + break; + default: + return QCommonStyle::pixelMetric(metric, option, widget); } - return QCommonStyle::pixelMetric(metric, option, widget); + return QStyleHelper::dpiScaled(val); } /*! @@ -3309,14 +3336,15 @@ QRect QFusionStyle::subControlRect(ComplexControl control, const QStyleOptionCom break; case SC_SliderGroove: { QPoint grooveCenter = slider->rect.center(); + const int grooveThickness = QStyleHelper::dpiScaled(7); if (slider->orientation == Qt::Horizontal) { - rect.setHeight(7); + rect.setHeight(grooveThickness); if (slider->tickPosition & QSlider::TicksAbove) grooveCenter.ry() += tickSize; if (slider->tickPosition & QSlider::TicksBelow) grooveCenter.ry() -= tickSize; } else { - rect.setWidth(7); + rect.setWidth(grooveThickness); if (slider->tickPosition & QSlider::TicksAbove) grooveCenter.rx() += tickSize; if (slider->tickPosition & QSlider::TicksBelow) @@ -3332,27 +3360,25 @@ QRect QFusionStyle::subControlRect(ComplexControl control, const QStyleOptionCom break; case CC_SpinBox: if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { - QSize bs; int center = spinbox->rect.height() / 2; - int fw = spinbox->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0; + int fw = spinbox->frame ? 3 : 0; // Is drawn with 3 pixels width in drawComplexControl, independently from PM_SpinBoxFrameWidth int y = fw; - bs.setHeight(qMax(8, spinbox->rect.height()/2 - y)); - bs.setWidth(14); + const int buttonWidth = QStyleHelper::dpiScaled(14); int x, lx, rx; - x = spinbox->rect.width() - y - bs.width() + 2; + x = spinbox->rect.width() - y - buttonWidth + 2; lx = fw; rx = x - fw; switch (subControl) { case SC_SpinBoxUp: if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) return QRect(); - rect = QRect(x, fw, bs.width(), center - fw); + rect = QRect(x, fw, buttonWidth, center - fw); break; case SC_SpinBoxDown: if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) return QRect(); - rect = QRect(x, center, bs.width(), spinbox->rect.bottom() - center - fw + 1); + rect = QRect(x, center, buttonWidth, spinbox->rect.bottom() - center - fw + 1); break; case SC_SpinBoxEditField: if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) { diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index b3c0463bca..531f538820 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -152,13 +152,32 @@ static uint qHash(const QPointer<QObject> &ptr) return qHash(ptr.data()); } -// These colors specify the titlebar gradient colors on -// Leopard. Ideally we should get them from the system. -static const QColor titlebarGradientActiveBegin(220, 220, 220); -static const QColor titlebarGradientActiveEnd(151, 151, 151); +// Title bar gradient colors for Lion were determined by inspecting PSDs exported +// using CoreUI's CoreThemeDocument; there is no public API to retrieve them + +static QLinearGradient titlebarGradientActive() +{ + static QLinearGradient gradient; + if (gradient == QLinearGradient()) { + gradient.setColorAt(0, QColor(235, 235, 235)); + gradient.setColorAt(0.5, QColor(210, 210, 210)); + gradient.setColorAt(0.75, QColor(195, 195, 195)); + gradient.setColorAt(1, QColor(180, 180, 180)); + } + return gradient; +} + +static QLinearGradient titlebarGradientInactive() +{ + static QLinearGradient gradient; + if (gradient == QLinearGradient()) { + gradient.setColorAt(0, QColor(250, 250, 250)); + gradient.setColorAt(1, QColor(225, 225, 225)); + } + return gradient; +} + static const QColor titlebarSeparatorLineActive(111, 111, 111); -static const QColor titlebarGradientInactiveBegin(241, 241, 241); -static const QColor titlebarGradientInactiveEnd(207, 207, 207); static const QColor titlebarSeparatorLineInactive(131, 131, 131); // Gradient colors used for the dock widget title bar and @@ -526,25 +545,6 @@ public: }; } // anonymous namespace -static QColor qcolorFromCGColor(CGColorRef cgcolor) -{ - QColor pc; - CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgcolor)); - const CGFloat *components = CGColorGetComponents(cgcolor); - if (model == kCGColorSpaceModelRGB) { - pc.setRgbF(components[0], components[1], components[2], components[3]); - } else if (model == kCGColorSpaceModelCMYK) { - pc.setCmykF(components[0], components[1], components[2], components[3]); - } else if (model == kCGColorSpaceModelMonochrome) { - pc.setRgbF(components[0], components[0], components[0], components[1]); - } else { - // Colorspace we can't deal with. - qWarning("Qt: qcolorFromCGColor: cannot convert from colorspace model: %d", model); - Q_ASSERT(false); - } - return pc; -} - OSStatus qt_mac_shape2QRegionHelper(int inMessage, HIShapeRef, const CGRect *inRect, void *inRefcon) { QRegion *region = static_cast<QRegion *>(inRefcon); @@ -702,7 +702,6 @@ static bool qt_macWindowMainWindow(const QWidget *window) const int qt_mac_hitheme_version = 0; //the HITheme version we speak const int macItemFrame = 2; // menu item frame width const int macItemHMargin = 3; // menu item hor text margin -const int macItemVMargin = 2; // menu item ver text margin const int macRightBorder = 12; // right border on mac const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar. QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background. @@ -3150,7 +3149,8 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai [triangleCell setButtonType:NSOnOffButton]; [triangleCell setState:(opt->state & State_Open) ? NSOnState : NSOffState]; [triangleCell setBezelStyle:NSDisclosureBezelStyle]; - [triangleCell setBackgroundStyle:((opt->state & State_Selected) && w->hasFocus()) ? NSBackgroundStyleDark : NSBackgroundStyleLight]; + bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus); + [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight]; CGContextSaveGState(cg); [NSGraphicsContext saveGraphicsState]; @@ -3263,14 +3263,15 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } break; case PE_PanelStatusBar: { // Fill the status bar with the titlebar gradient. - QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom()); + QLinearGradient linearGrad; if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active)) { - linearGrad.setColorAt(0, titlebarGradientActiveBegin); - linearGrad.setColorAt(1, titlebarGradientActiveEnd); + linearGrad = titlebarGradientActive(); } else { - linearGrad.setColorAt(0, titlebarGradientInactiveBegin); - linearGrad.setColorAt(1, titlebarGradientInactiveEnd); + linearGrad = titlebarGradientInactive(); } + + linearGrad.setStart(0, opt->rect.top()); + linearGrad.setFinalStop(0, opt->rect.bottom()); p->fillRect(opt->rect, linearGrad); // Draw the black separator line at the top of the status bar. diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 4c5c7cd17e..8774d71c0c 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -1784,6 +1784,9 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, \value SH_LineEdit_PasswordCharacter The Unicode character to be used for passwords. + \value SH_LineEdit_PasswordMaskDelay Determines the delay before visible character is masked + with password character, in milliseconds. This enum value was added in Qt 5.4. + \value SH_Table_GridLineColor The RGB value of the grid for a table. \value SH_UnderlineShortcut Whether shortcuts are underlined. diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index 136daa9abd..ea012faf46 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -703,6 +703,7 @@ public: SH_Widget_Animate, SH_Splitter_OpaqueResize, SH_ComboBox_UseNativePopup, + SH_LineEdit_PasswordMaskDelay, // Add new style hint values here SH_CustomBase = 0xf0000000 diff --git a/src/widgets/styles/qstyleanimation.cpp b/src/widgets/styles/qstyleanimation.cpp index 85dc357ab5..e6e47a98e9 100644 --- a/src/widgets/styles/qstyleanimation.cpp +++ b/src/widgets/styles/qstyleanimation.cpp @@ -50,7 +50,7 @@ static const qreal ScrollBarFadeOutDuration = 200.0; static const qreal ScrollBarFadeOutDelay = 450.0; QStyleAnimation::QStyleAnimation(QObject *target) : QAbstractAnimation(target), - _delay(0), _duration(-1), _startTime(QTime::currentTime()) + _delay(0), _duration(-1), _startTime(QTime::currentTime()), _fps(ThirtyFps), _skip(0) { } @@ -93,6 +93,16 @@ void QStyleAnimation::setStartTime(const QTime &time) _startTime = time; } +QStyleAnimation::FrameRate QStyleAnimation::frameRate() const +{ + return _fps; +} + +void QStyleAnimation::setFrameRate(FrameRate fps) +{ + _fps = fps; +} + void QStyleAnimation::updateTarget() { QEvent event(QEvent::StyleAnimationUpdate); @@ -102,6 +112,12 @@ void QStyleAnimation::updateTarget() stop(); } +void QStyleAnimation::start() +{ + _skip = 0; + QAbstractAnimation::start(DeleteWhenStopped); +} + bool QStyleAnimation::isUpdateNeeded() const { return currentTime() > _delay; @@ -109,8 +125,11 @@ bool QStyleAnimation::isUpdateNeeded() const void QStyleAnimation::updateCurrentTime(int) { - if (target() && isUpdateNeeded()) - updateTarget(); + if (++_skip >= _fps) { + _skip = 0; + if (target() && isUpdateNeeded()) + updateTarget(); + } } QProgressStyleAnimation::QProgressStyleAnimation(int speed, QObject *target) : diff --git a/src/widgets/styles/qstyleanimation_p.h b/src/widgets/styles/qstyleanimation_p.h index c344858812..19e629cb25 100644 --- a/src/widgets/styles/qstyleanimation_p.h +++ b/src/widgets/styles/qstyleanimation_p.h @@ -78,8 +78,21 @@ public: QTime startTime() const; void setStartTime(const QTime &time); + enum FrameRate { + DefaultFps, + SixtyFps, + ThirtyFps, + TwentyFps + }; + + FrameRate frameRate() const; + void setFrameRate(FrameRate fps); + void updateTarget(); +public Q_SLOTS: + void start(); + protected: virtual bool isUpdateNeeded() const; virtual void updateCurrentTime(int time); @@ -88,6 +101,8 @@ private: int _delay; int _duration; QTime _startTime; + FrameRate _fps; + int _skip; }; class QProgressStyleAnimation : public QStyleAnimation diff --git a/src/widgets/styles/qstyleoption.cpp b/src/widgets/styles/qstyleoption.cpp index 5913b2f261..f642a05d13 100644 --- a/src/widgets/styles/qstyleoption.cpp +++ b/src/widgets/styles/qstyleoption.cpp @@ -3702,6 +3702,8 @@ QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(int version) of the painter used to draw the item. By default, if no transformations are applied, its value is 1. If zoomed out 1:2, the level of detail will be 0.5, and if zoomed in 2:1, its value is 2. + + \sa QGraphicsScene::minimumRenderSize() */ qreal QStyleOptionGraphicsItem::levelOfDetailFromTransform(const QTransform &worldTransform) { diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 60bfc8e075..b82aa1b5a0 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -645,6 +645,7 @@ static const char *knownStyleHints[] = { "icon-size", "leftarrow-icon", "lineedit-password-character", + "lineedit-password-mask-delay", "mdi-fill-space-on-maximize", "menu-scrollable", "menubar-altkey-navigation", @@ -5143,6 +5144,7 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi QString s; switch (sh) { case SH_LineEdit_PasswordCharacter: s = QLatin1String("lineedit-password-character"); break; + case SH_LineEdit_PasswordMaskDelay: s = QLatin1String("lineedit-password-mask-delay"); break; case SH_DitherDisabledText: s = QLatin1String("dither-disabled-text"); break; case SH_EtchDisabledText: s = QLatin1String("etch-disabled-text"); break; case SH_ItemView_ActivateItemOnSingleClick: s = QLatin1String("activate-on-singleclick"); break; @@ -5804,6 +5806,25 @@ QRect QStyleSheetStyle::subElementRect(SubElement se, const QStyleOption *opt, c break; #endif //QT_NO_TOOLBAR + // On mac we make pixel adjustments to layouts which are not + // desireable when you have custom style sheets on them + case SE_CheckBoxLayoutItem: + case SE_ComboBoxLayoutItem: + case SE_DateTimeEditLayoutItem: + case SE_LabelLayoutItem: + case SE_ProgressBarLayoutItem: + case SE_PushButtonLayoutItem: + case SE_RadioButtonLayoutItem: + case SE_SliderLayoutItem: + case SE_SpinBoxLayoutItem: + case SE_ToolButtonLayoutItem: + case SE_FrameLayoutItem: + case SE_GroupBoxLayoutItem: + case SE_TabWidgetLayoutItem: + if (!rule.hasNativeBorder()) + return opt->rect; + break; + default: break; } diff --git a/src/widgets/styles/qwindowsvistastyle.cpp b/src/widgets/styles/qwindowsvistastyle.cpp index bc78ea3296..e3699da7ef 100644 --- a/src/widgets/styles/qwindowsvistastyle.cpp +++ b/src/widgets/styles/qwindowsvistastyle.cpp @@ -1421,15 +1421,14 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption } break; case CE_DockWidgetTitle: - if (const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(widget)) { + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { + const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(widget); QRect rect = option->rect; - if (dockWidget->isFloating()) { + if (dockWidget && dockWidget->isFloating()) { QWindowsXPStyle::drawControl(element, option, painter, widget); break; //otherwise fall through } - if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { - const QStyleOptionDockWidgetV2 *v2 = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt); bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar; @@ -1490,7 +1489,6 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption } } break; - } #ifndef QT_NO_ITEMVIEWS case CE_ItemViewItem: { diff --git a/src/widgets/util/qflickgesture.cpp b/src/widgets/util/qflickgesture.cpp index b0cd4a9540..a372a7fe0d 100644 --- a/src/widgets/util/qflickgesture.cpp +++ b/src/widgets/util/qflickgesture.cpp @@ -101,6 +101,8 @@ static QMouseEvent *copyMouseEvent(QEvent *e) copy->setButtons(me->buttons()); copy->setButton(me->button()); copy->setModifiers(me->modifiers()); + copy->setSource(me->source()); + copy->setFlags(me->flags()); return copy; #endif } diff --git a/src/widgets/util/qsystemtrayicon.cpp b/src/widgets/util/qsystemtrayicon.cpp index f1a69e6b36..fa318f3233 100644 --- a/src/widgets/util/qsystemtrayicon.cpp +++ b/src/widgets/util/qsystemtrayicon.cpp @@ -672,6 +672,74 @@ void QBalloonTip::timerEvent(QTimerEvent *e) QWidget::timerEvent(e); } +////////////////////////////////////////////////////////////////////// +void QSystemTrayIconPrivate::install_sys_qpa() +{ + qpa_sys->init(); + QObject::connect(qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), + q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); + QObject::connect(qpa_sys, &QPlatformSystemTrayIcon::messageClicked, + q_func(), &QSystemTrayIcon::messageClicked); + updateMenu_sys(); + updateIcon_sys(); + updateToolTip_sys(); +} + +void QSystemTrayIconPrivate::remove_sys_qpa() +{ + qpa_sys->cleanup(); +} + +QRect QSystemTrayIconPrivate::geometry_sys_qpa() const +{ + return qpa_sys->geometry(); +} + +void QSystemTrayIconPrivate::updateIcon_sys_qpa() +{ + qpa_sys->updateIcon(icon); +} + +void QSystemTrayIconPrivate::updateMenu_sys_qpa() +{ + if (menu) { + if (!menu->platformMenu()) { + QPlatformMenu *platformMenu = qpa_sys->createMenu(); + if (platformMenu) + menu->setPlatformMenu(platformMenu); + } + qpa_sys->updateMenu(menu->platformMenu()); + } +} + +void QSystemTrayIconPrivate::updateToolTip_sys_qpa() +{ + qpa_sys->updateToolTip(toolTip); +} + +void QSystemTrayIconPrivate::showMessage_sys_qpa(const QString &message, + const QString &title, + QSystemTrayIcon::MessageIcon icon, + int msecs) +{ + QIcon notificationIcon; + switch (icon) { + case QSystemTrayIcon::Information: + notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + break; + case QSystemTrayIcon::Warning: + notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + break; + case QSystemTrayIcon::Critical: + notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); + break; + default: + break; + } + qpa_sys->showMessage(message, title, notificationIcon, + static_cast<QPlatformSystemTrayIcon::MessageIcon>(icon), msecs); +} + QT_END_NAMESPACE #endif // QT_NO_SYSTEMTRAYICON diff --git a/src/widgets/util/qsystemtrayicon_p.h b/src/widgets/util/qsystemtrayicon_p.h index 211ef308f1..317664a365 100644 --- a/src/widgets/util/qsystemtrayicon_p.h +++ b/src/widgets/util/qsystemtrayicon_p.h @@ -98,6 +98,15 @@ public: QSystemTrayIconSys *sys; QPlatformSystemTrayIcon *qpa_sys; bool visible; + +private: + void install_sys_qpa(); + void remove_sys_qpa(); + void updateIcon_sys_qpa(); + void updateToolTip_sys_qpa(); + void updateMenu_sys_qpa(); + QRect geometry_sys_qpa() const; + void showMessage_sys_qpa(const QString &msg, const QString &title, QSystemTrayIcon::MessageIcon icon, int secs); }; class QBalloonTip : public QWidget diff --git a/src/widgets/util/qsystemtrayicon_qpa.cpp b/src/widgets/util/qsystemtrayicon_qpa.cpp index f98aeaf678..045641c509 100644 --- a/src/widgets/util/qsystemtrayicon_qpa.cpp +++ b/src/widgets/util/qsystemtrayicon_qpa.cpp @@ -65,28 +65,20 @@ QSystemTrayIconPrivate::~QSystemTrayIconPrivate() void QSystemTrayIconPrivate::install_sys() { - if (qpa_sys) { - qpa_sys->init(); - QObject::connect(qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), - q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); - QObject::connect(qpa_sys, SIGNAL(messageClicked()), - q_func(), SIGNAL(messageClicked())); - updateMenu_sys(); - updateIcon_sys(); - updateToolTip_sys(); - } + if (qpa_sys) + install_sys_qpa(); } void QSystemTrayIconPrivate::remove_sys() { if (qpa_sys) - qpa_sys->cleanup(); + remove_sys_qpa(); } QRect QSystemTrayIconPrivate::geometry_sys() const { if (qpa_sys) - return qpa_sys->geometry(); + return geometry_sys_qpa(); else return QRect(); } @@ -94,25 +86,19 @@ QRect QSystemTrayIconPrivate::geometry_sys() const void QSystemTrayIconPrivate::updateIcon_sys() { if (qpa_sys) - qpa_sys->updateIcon(icon); + updateIcon_sys_qpa(); } void QSystemTrayIconPrivate::updateMenu_sys() { - if (qpa_sys && menu) { - if (!menu->platformMenu()) { - QPlatformMenu *platformMenu = qpa_sys->createMenu(); - if (platformMenu) - menu->setPlatformMenu(platformMenu); - } - qpa_sys->updateMenu(menu->platformMenu()); - } + if (qpa_sys) + updateMenu_sys_qpa(); } void QSystemTrayIconPrivate::updateToolTip_sys() { if (qpa_sys) - qpa_sys->updateToolTip(toolTip); + updateToolTip_sys_qpa(); } bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() @@ -138,25 +124,8 @@ void QSystemTrayIconPrivate::showMessage_sys(const QString &message, QSystemTrayIcon::MessageIcon icon, int msecs) { - if (!qpa_sys) - return; - - QIcon notificationIcon; - switch (icon) { - case QSystemTrayIcon::Information: - notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); - break; - case QSystemTrayIcon::Warning: - notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); - break; - case QSystemTrayIcon::Critical: - notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); - break; - default: - break; - } - qpa_sys->showMessage(message, title, notificationIcon, - static_cast<QPlatformSystemTrayIcon::MessageIcon>(icon), msecs); + if (qpa_sys) + showMessage_sys_qpa(message, title, icon, msecs); } QT_END_NAMESPACE diff --git a/src/widgets/util/qsystemtrayicon_x11.cpp b/src/widgets/util/qsystemtrayicon_x11.cpp index 21ce23437f..e7007e4091 100644 --- a/src/widgets/util/qsystemtrayicon_x11.cpp +++ b/src/widgets/util/qsystemtrayicon_x11.cpp @@ -55,6 +55,9 @@ #include <qscreen.h> #include <qbackingstore.h> #include <qpa/qplatformnativeinterface.h> +#include <qpa/qplatformsystemtrayicon.h> +#include <qpa/qplatformtheme.h> +#include <private/qguiapplication_p.h> #include <qdebug.h> #ifndef QT_NO_SYSTEMTRAYICON @@ -101,11 +104,24 @@ QSystemTrayIconSys::QSystemTrayIconSys(QSystemTrayIcon *qIn) setObjectName(QStringLiteral("QSystemTrayIconSys")); setToolTip(q->toolTip()); setAttribute(Qt::WA_AlwaysShowToolTips, true); - setAttribute(Qt::WA_TranslucentBackground, true); setAttribute(Qt::WA_QuitOnClose, false); const QSize size(22, 22); // Gnome, standard size setGeometry(QRect(QPoint(0, 0), size)); setMinimumSize(size); + + // We need two different behaviors depending on whether the X11 visual for the system tray + // (a) exists and (b) supports an alpha channel, i.e. is 32 bits. + // If we have a visual that has an alpha channel, we can paint this widget with a transparent + // background and it will work. + // However, if there's no alpha channel visual, in order for transparent tray icons to work, + // we do not have a transparent background on the widget, but call xcb_clear_region before + // painting the icon + bool hasAlphaChannel = false; + QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(), + "systrayVisualHasAlphaChannel", Qt::DirectConnection, + Q_RETURN_ARG(bool, hasAlphaChannel)); + setAttribute(Qt::WA_TranslucentBackground, hasAlphaChannel); + addToTray(); } @@ -196,12 +212,21 @@ bool QSystemTrayIconSys::event(QEvent *e) void QSystemTrayIconSys::paintEvent(QPaintEvent *) { - // Note: Transparent pixels require a particular Visual which XCB - // currently does not support yet. const QRect rect(QPoint(0, 0), geometry().size()); QPainter painter(this); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.fillRect(rect, Qt::transparent); + + // If we have Qt::WA_TranslucentBackground set, during widget creation + // we detected the systray visual supported an alpha channel + if (testAttribute(Qt::WA_TranslucentBackground)) { + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(rect, Qt::transparent); + } else { + QMetaObject::invokeMethod(QGuiApplication::platformNativeInterface(), + "clearRegion", Qt::DirectConnection, + Q_ARG(const QWindow *, windowHandle()), + Q_ARG(const QRect&, rect) + ); + } painter.setCompositionMode(QPainter::CompositionMode_SourceOver); q->icon().paint(&painter, rect); } @@ -215,16 +240,22 @@ void QSystemTrayIconSys::resizeEvent(QResizeEvent *) QSystemTrayIconPrivate::QSystemTrayIconPrivate() : sys(0), + qpa_sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()), visible(false) { } QSystemTrayIconPrivate::~QSystemTrayIconPrivate() { + delete qpa_sys; } void QSystemTrayIconPrivate::install_sys() { + if (qpa_sys) { + install_sys_qpa(); + return; + } Q_Q(QSystemTrayIcon); if (!sys && locateSystemTray()) { sys = new QSystemTrayIconSys(q); @@ -235,6 +266,8 @@ void QSystemTrayIconPrivate::install_sys() QRect QSystemTrayIconPrivate::geometry_sys() const { + if (qpa_sys) + return geometry_sys_qpa(); if (!sys) return QRect(); return sys->globalGeometry(); @@ -242,6 +275,10 @@ QRect QSystemTrayIconPrivate::geometry_sys() const void QSystemTrayIconPrivate::remove_sys() { + if (qpa_sys) { + remove_sys_qpa(); + return; + } if (!sys) return; QBalloonTip::hideBalloon(); @@ -252,17 +289,26 @@ void QSystemTrayIconPrivate::remove_sys() void QSystemTrayIconPrivate::updateIcon_sys() { + if (qpa_sys) { + updateIcon_sys_qpa(); + return; + } if (sys) sys->updateIcon(); } void QSystemTrayIconPrivate::updateMenu_sys() { - + if (qpa_sys) + updateMenu_sys_qpa(); } void QSystemTrayIconPrivate::updateToolTip_sys() { + if (qpa_sys) { + updateToolTip_sys_qpa(); + return; + } if (!sys) return; #ifndef QT_NO_TOOLTIP @@ -272,6 +318,11 @@ void QSystemTrayIconPrivate::updateToolTip_sys() bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() { + QScopedPointer<QPlatformSystemTrayIcon> sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()); + if (sys) + return sys->isSystemTrayAvailable(); + + // no QPlatformSystemTrayIcon so fall back to default xcb platform behavior const QString platform = QGuiApplication::platformName(); if (platform.compare(QStringLiteral("xcb"), Qt::CaseInsensitive) == 0) return locateSystemTray(); @@ -280,12 +331,21 @@ bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() bool QSystemTrayIconPrivate::supportsMessages_sys() { + QScopedPointer<QPlatformSystemTrayIcon> sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()); + if (sys) + return sys->supportsMessages(); + + // no QPlatformSystemTrayIcon so fall back to default xcb platform behavior return true; } void QSystemTrayIconPrivate::showMessage_sys(const QString &message, const QString &title, QSystemTrayIcon::MessageIcon icon, int msecs) { + if (qpa_sys) { + showMessage_sys_qpa(message, title, icon, msecs); + return; + } if (!sys) return; const QPoint g = sys->globalGeometry().topLeft(); diff --git a/src/widgets/widgets.pro b/src/widgets/widgets.pro index b12e2836d4..d819436f66 100644 --- a/src/widgets/widgets.pro +++ b/src/widgets/widgets.pro @@ -9,8 +9,7 @@ win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x65000000 irix-cc*:QMAKE_CXXFLAGS += -no_prelink -ptused MODULE_PLUGIN_TYPES += \ - styles \ - accessible/libqtaccessiblewidgets.so + styles QMAKE_DOCS = $$PWD/doc/qtwidgets.qdocconf diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp index 98e715fc62..1b48441993 100644 --- a/src/widgets/widgets/qabstractscrollarea.cpp +++ b/src/widgets/widgets/qabstractscrollarea.cpp @@ -1191,6 +1191,10 @@ bool QAbstractScrollArea::viewportEvent(QEvent *e) case QEvent::DragMove: case QEvent::DragLeave: #endif + // QOpenGLWidget needs special support because it has to know + // its size has changed, so that it can resize its fbo. + if (e->type() == QEvent::Resize) + QWidgetPrivate::get(viewport())->resizeViewportFramebuffer(); return QFrame::event(e); case QEvent::LayoutRequest: #ifndef QT_NO_GESTURES diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp index 92af91b66e..43f5d6fd31 100644 --- a/src/widgets/widgets/qabstractspinbox.cpp +++ b/src/widgets/widgets/qabstractspinbox.cpp @@ -315,6 +315,8 @@ void QAbstractSpinBox::setReadOnly(bool enable) Q_D(QAbstractSpinBox); d->readOnly = enable; d->edit->setReadOnly(enable); + QEvent event(QEvent::ReadOnlyChange); + QApplication::sendEvent(this, &event); update(); } diff --git a/src/widgets/widgets/qcheckbox.h b/src/widgets/widgets/qcheckbox.h index 9adf5f6567..db6be7d1c0 100644 --- a/src/widgets/widgets/qcheckbox.h +++ b/src/widgets/widgets/qcheckbox.h @@ -86,6 +86,7 @@ protected: private: Q_DECLARE_PRIVATE(QCheckBox) Q_DISABLE_COPY(QCheckBox) + friend class QAccessibleButton; }; QT_END_NAMESPACE diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp index 066b3c7a86..5a40ceb2f7 100644 --- a/src/widgets/widgets/qdockarealayout.cpp +++ b/src/widgets/widgets/qdockarealayout.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -44,6 +44,7 @@ #include "QtWidgets/qtabbar.h" #include "QtWidgets/qstyle.h" #include "QtWidgets/qdesktopwidget.h" +#include "QtWidgets/qapplication.h" #include "QtCore/qvariant.h" #include "qdockarealayout_p.h" #include "qdockwidget.h" @@ -2978,8 +2979,9 @@ bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget) item.widgetItem = new QDockWidgetItem(dockWidget); if (placeHolder->window) { - QDesktopWidget desktop; - QRect r = constrainedRect(placeHolder->topLevelRect, desktop.screenGeometry(dockWidget)); + const QRect screenGeometry = + QApplication::desktop()->screenGeometry(placeHolder->topLevelRect.center()); + const QRect r = constrainedRect(placeHolder->topLevelRect, screenGeometry); dockWidget->d_func()->setWindowState(true, true, r); } dockWidget->setVisible(!placeHolder->hidden); diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index ad9481a928..3efa89edb3 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -591,10 +591,6 @@ void QDockWidgetPrivate::init() QObject::connect(button, SIGNAL(clicked()), q, SLOT(close())); layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button); - resizer = new QWidgetResizeHandler(q); - resizer->setMovingEnabled(false); - resizer->setActive(false); - #ifndef QT_NO_ACTION toggleViewAction = new QAction(q); toggleViewAction->setCheckable(true); @@ -760,13 +756,12 @@ void QDockWidgetPrivate::endDrag(bool abort) Qt::WindowFlags flags = q->windowFlags(); flags &= ~Qt::X11BypassWindowManagerHint; q->setWindowFlags(flags); - resizer->setActive(QWidgetResizeHandler::Resize, true); + setResizerActive(true); q->show(); } else { QDockWidgetLayout *myLayout = qobject_cast<QDockWidgetLayout*>(layout); - resizer->setActive(QWidgetResizeHandler::Resize, - myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0); + setResizerActive(myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0); } undockedGeometry = q->geometry(); q->activateWindow(); @@ -779,6 +774,17 @@ void QDockWidgetPrivate::endDrag(bool abort) state = 0; } +void QDockWidgetPrivate::setResizerActive(bool active) +{ + Q_Q(QDockWidget); + if (active && !resizer) { + resizer = new QWidgetResizeHandler(q); + resizer->setMovingEnabled(false); + } + if (resizer) + resizer->setActive(QWidgetResizeHandler::Resize, active); +} + bool QDockWidgetPrivate::isAnimating() const { Q_Q(const QDockWidget); @@ -1052,7 +1058,7 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect } } - resizer->setActive(QWidgetResizeHandler::Resize, !unplug && floating && !nativeDeco); + setResizerActive(!unplug && floating && !nativeDeco); } /*! diff --git a/src/widgets/widgets/qdockwidget_p.h b/src/widgets/widgets/qdockwidget_p.h index f7cd8c0039..a4dc68dd7d 100644 --- a/src/widgets/widgets/qdockwidget_p.h +++ b/src/widgets/widgets/qdockwidget_p.h @@ -88,7 +88,7 @@ public: features(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable), - allowedAreas(Qt::AllDockWidgetAreas) + allowedAreas(Qt::AllDockWidgetAreas), resizer(0) { } void init(); @@ -101,8 +101,6 @@ public: QDockWidget::DockWidgetFeatures features; Qt::DockWidgetAreas allowedAreas; - QWidgetResizeHandler *resizer; - #ifndef QT_NO_ACTION QAction *toggleViewAction; #endif @@ -124,8 +122,12 @@ public: void unplug(const QRect &rect); void plug(const QRect &rect); + void setResizerActive(bool active); bool isAnimating() const; + +private: + QWidgetResizeHandler *resizer; }; class Q_WIDGETS_EXPORT QDockWidgetLayout : public QLayout diff --git a/src/widgets/widgets/qgroupbox.cpp b/src/widgets/widgets/qgroupbox.cpp index 168f0bbd67..42b0c68971 100644 --- a/src/widgets/widgets/qgroupbox.cpp +++ b/src/widgets/widgets/qgroupbox.cpp @@ -405,6 +405,8 @@ void QGroupBox::childEvent(QChildEvent *c) if (c->type() != QEvent::ChildAdded || !c->child()->isWidgetType()) return; QWidget *w = (QWidget*)c->child(); + if (w->isWindow()) + return; if (d->checkable) { if (d->checked) { if (!w->testAttribute(Qt::WA_ForceDisabled)) diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index ba4dbcc878..b6b7ffb1a2 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -1348,6 +1348,8 @@ void QLineEdit::setReadOnly(bool enable) #ifndef QT_NO_CURSOR setCursor(enable ? Qt::ArrowCursor : Qt::IBeamCursor); #endif + QEvent event(QEvent::ReadOnlyChange); + QCoreApplication::sendEvent(this, &event); update(); } } @@ -2188,6 +2190,7 @@ void QLineEdit::changeEvent(QEvent *ev) QStyleOptionFrameV2 opt; initStyleOption(&opt); d->control->setPasswordCharacter(style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter, &opt, this)); + d->control->setPasswordMaskDelay(style()->styleHint(QStyle::SH_LineEdit_PasswordMaskDelay, &opt, this)); } d->m_iconSize = QSize(); update(); diff --git a/src/widgets/widgets/qlineedit.h b/src/widgets/widgets/qlineedit.h index 3d52863db2..d53437c479 100644 --- a/src/widgets/widgets/qlineedit.h +++ b/src/widgets/widgets/qlineedit.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -261,6 +261,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_selectionChanged()) Q_PRIVATE_SLOT(d_func(), void _q_updateNeeded(const QRect &)) Q_PRIVATE_SLOT(d_func(), void _q_textChanged(const QString &)) + Q_PRIVATE_SLOT(d_func(), void _q_clearButtonClicked()) }; #endif // QT_NO_LINEEDIT diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp index 10689b2fc3..7ad893a54e 100644 --- a/src/widgets/widgets/qlineedit_p.cpp +++ b/src/widgets/widgets/qlineedit_p.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -205,6 +205,7 @@ void QLineEditPrivate::init(const QString& txt) QStyleOptionFrameV2 opt; q->initStyleOption(&opt); control->setPasswordCharacter(q->style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter, &opt, q)); + control->setPasswordMaskDelay(q->style()->styleHint(QStyle::SH_LineEdit_PasswordMaskDelay, &opt, q)); #ifndef QT_NO_CURSOR q->setCursor(Qt::IBeamCursor); #endif @@ -372,6 +373,15 @@ void QLineEditPrivate::_q_textChanged(const QString &text) } } +void QLineEditPrivate::_q_clearButtonClicked() +{ + Q_Q(QLineEdit); + if (!q->text().isEmpty()) { + q->clear(); + emit q->textEdited(QString()); + } +} + QSize QLineEditPrivate::iconSize() const { if (!m_iconSize.isValid()) // This might require style-specific handling (pixel metric). @@ -447,7 +457,7 @@ QWidget *QLineEditPrivate::addAction(QAction *newAction, QAction *before, QLineE toolButton->setIcon(newAction->icon()); toolButton->setOpacity(lastTextSize > 0 || !(flags & SideWidgetFadeInWithText) ? 1 : 0); if (flags & SideWidgetClearButton) - QObject::connect(toolButton, SIGNAL(clicked()), q, SLOT(clear())); + QObject::connect(toolButton, SIGNAL(clicked()), q, SLOT(_q_clearButtonClicked())); toolButton->setDefaultAction(newAction); w = toolButton; } diff --git a/src/widgets/widgets/qlineedit_p.h b/src/widgets/widgets/qlineedit_p.h index db8edee005..08365521d5 100644 --- a/src/widgets/widgets/qlineedit_p.h +++ b/src/widgets/widgets/qlineedit_p.h @@ -195,6 +195,7 @@ public: void drag(); #endif void _q_textChanged(const QString &); + void _q_clearButtonClicked(); int leftTextMargin; // use effectiveLeftTextMargin() in case of icon. int topTextMargin; diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp index 36ca90ba00..79f42a13ed 100644 --- a/src/widgets/widgets/qmainwindow.cpp +++ b/src/widgets/widgets/qmainwindow.cpp @@ -468,6 +468,10 @@ void QMainWindow::setIconSize(const QSize &iconSize) /*! \property QMainWindow::toolButtonStyle \brief style of toolbar buttons in this mainwindow. + To have the style of toolbuttons follow the system settings, set this property to Qt::ToolButtonFollowStyle. + On Unix, the user settings from the desktop environment will be used. + On other platforms, Qt::ToolButtonFollowStyle means icon only. + The default is Qt::ToolButtonIconOnly. */ diff --git a/src/widgets/widgets/qmainwindow.h b/src/widgets/widgets/qmainwindow.h index 8411bf9f6b..6c392ca4cc 100644 --- a/src/widgets/widgets/qmainwindow.h +++ b/src/widgets/widgets/qmainwindow.h @@ -155,7 +155,6 @@ public: void removeToolBar(QToolBar *toolbar); void removeToolBarBreak(QToolBar *before); - void setUnifiedTitleAndToolBarOnMac(bool set); bool unifiedTitleAndToolBarOnMac() const; Qt::ToolBarArea toolBarArea(QToolBar *toolbar) const; @@ -182,12 +181,14 @@ public: virtual QMenu *createPopupMenu(); #endif - -#ifndef QT_NO_DOCKWIDGET public Q_SLOTS: +#ifndef QT_NO_DOCKWIDGET void setAnimated(bool enabled); void setDockNestingEnabled(bool enabled); #endif +#ifndef QT_NO_TOOLBAR + void setUnifiedTitleAndToolBarOnMac(bool set); +#endif Q_SIGNALS: void iconSizeChanged(const QSize &iconSize); diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index c026b79103..757d70c64b 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -62,7 +62,10 @@ #include <qmap.h> #include <qtimer.h> -#include <qdebug.h> +#ifndef QT_NO_DEBUG_STREAM +# include <qdebug.h> +# include <qtextstream.h> +#endif #include <private/qapplication_p.h> #include <private/qlayoutengine_p.h> @@ -71,23 +74,17 @@ # include <private/qt_cocoa_helpers_mac_p.h> #endif -#ifdef QT_NO_DOCKWIDGET -extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); -#endif +QT_BEGIN_NAMESPACE -#ifdef Q_DEBUG_MAINWINDOW_LAYOUT -# include <QTextStream> +#ifndef QT_NO_DOCKWIDGET +extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); #endif -QT_BEGIN_NAMESPACE - /****************************************************************************** ** debug */ -#if defined(Q_DEBUG_MAINWINDOW_LAYOUT) && !defined(QT_NO_DOCKWIDGET) - -static QTextStream qout(stderr, QIODevice::WriteOnly); +#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_DEBUG_STREAM) static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent); @@ -101,7 +98,7 @@ static void dumpLayout(QTextStream &qout, const QDockAreaLayoutItem &item, QStri if (item.widgetItem != 0) { qout << indent << "widget: " << item.widgetItem->widget()->metaObject()->className() - << ' ' << item.widgetItem->widget()->windowTitle() << '\n'; + << " \"" << item.widgetItem->widget()->windowTitle() << "\"\n"; } else if (item.subinfo != 0) { qout << indent << "subinfo:\n"; dumpLayout(qout, *item.subinfo, indent + QLatin1String(" ")); @@ -117,16 +114,17 @@ static void dumpLayout(QTextStream &qout, const QDockAreaLayoutItem &item, QStri << " rect:" << r.x() << ',' << r.y() << ' ' << r.width() << 'x' << r.height() << '\n'; } - qout.flush(); } static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent) { + const QSize minSize = layout.minimumSize(); qout << indent << "QDockAreaLayoutInfo: " << layout.rect.left() << ',' << layout.rect.top() << ' ' << layout.rect.width() << 'x' << layout.rect.height() + << " min size: " << minSize.width() << ',' << minSize.height() << " orient:" << layout.o << " tabbed:" << layout.tabbed << " tbshape:" << layout.tabBarShape << '\n'; @@ -137,36 +135,42 @@ static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QSt qout << indent << "Item: " << i << '\n'; dumpLayout(qout, layout.item_list.at(i), indent + QLatin1String(" ")); } - qout.flush(); -}; +} -static void dumpLayout(QTextStream &qout, const QDockAreaLayout &layout, QString indent) +static void dumpLayout(QTextStream &qout, const QDockAreaLayout &layout) { - qout << indent << "QDockAreaLayout: " + qout << "QDockAreaLayout: " << layout.rect.left() << ',' << layout.rect.top() << ' ' << layout.rect.width() << 'x' << layout.rect.height() << '\n'; - qout << indent << "TopDockArea:\n"; - dumpLayout(qout, layout.docks[QInternal::TopDock], indent + QLatin1String(" ")); - qout << indent << "LeftDockArea:\n"; - dumpLayout(qout, layout.docks[QInternal::LeftDock], indent + QLatin1String(" ")); - qout << indent << "RightDockArea:\n"; - dumpLayout(qout, layout.docks[QInternal::RightDock], indent + QLatin1String(" ")); - qout << indent << "BottomDockArea:\n"; - dumpLayout(qout, layout.docks[QInternal::BottomDock], indent + QLatin1String(" ")); + qout << "TopDockArea:\n"; + dumpLayout(qout, layout.docks[QInternal::TopDock], QLatin1String(" ")); + qout << "LeftDockArea:\n"; + dumpLayout(qout, layout.docks[QInternal::LeftDock], QLatin1String(" ")); + qout << "RightDockArea:\n"; + dumpLayout(qout, layout.docks[QInternal::RightDock], QLatin1String(" ")); + qout << "BottomDockArea:\n"; + dumpLayout(qout, layout.docks[QInternal::BottomDock], QLatin1String(" ")); +} - qout.flush(); -}; +QDebug operator<<(QDebug debug, const QDockAreaLayout &layout) +{ + QString s; + QTextStream str(&s); + dumpLayout(str, layout); + debug << s; + return debug; +} -void qt_dumpLayout(QTextStream &qout, QMainWindow *window) +QDebug operator<<(QDebug debug, const QMainWindowLayout *layout) { - QMainWindowLayout *layout = qt_mainwindow_layout(window); - dumpLayout(qout, layout->layoutState.dockAreaLayout, QString()); + debug << layout->layoutState.dockAreaLayout; + return debug; } -#endif // Q_DEBUG_MAINWINDOW_LAYOUT && !QT_NO_DOCKWIDGET +#endif // !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_DEBUG) /****************************************************************************** ** QMainWindowLayoutState diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h index f866a554dc..4b578e94b7 100644 --- a/src/widgets/widgets/qmainwindowlayout_p.h +++ b/src/widgets/widgets/qmainwindowlayout_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -68,15 +68,6 @@ #include "qdockarealayout_p.h" #include "qtoolbararealayout_p.h" -//#define Q_DEBUG_MAINWINDOW_LAYOUT - -#if defined(Q_DEBUG_MAINWINDOW_LAYOUT) && !defined(QT_NO_DOCKWIDGET) -QT_BEGIN_NAMESPACE -class QTextStream; -Q_WIDGETS_EXPORT void qt_dumpLayout(QTextStream &qout, QMainWindow *window); -QT_END_NAMESPACE -#endif // Q_DEBUG_MAINWINDOW_LAYOUT && !QT_NO_DOCKWIDGET - #ifdef Q_WS_MAC // Forward defs to make avoid including Carbon.h (faster compile you know ;). struct OpaqueHIObjectRef; @@ -344,6 +335,13 @@ public: #endif // Q_WS_MAC }; + +#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_DEBUG_STREAM) +class QDebug; +QDebug operator<<(QDebug debug, const QDockAreaLayout &layout); +QDebug operator<<(QDebug debug, const QMainWindowLayout *layout); +#endif + QT_END_NAMESPACE #endif // QT_NO_MAINWINDOW diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp index 17f73b2809..beabaa87da 100644 --- a/src/widgets/widgets/qmdiarea.cpp +++ b/src/widgets/widgets/qmdiarea.cpp @@ -169,7 +169,7 @@ #include <QResizeEvent> #include <QScrollBar> #include <QtAlgorithms> -#include <QMutableListIterator> +#include <QMutableVectorIterator> #include <QPainter> #include <QFontMetrics> #include <QStyleOption> @@ -413,7 +413,7 @@ void IconTiler::rearrange(QList<QWidget *> &widgets, const QRect &domain) const \internal Calculates the accumulated overlap (intersection area) between 'source' and 'rects'. */ -int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QList<QRect> &rects) +int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QVector<QRect> &rects) { int accOverlap = 0; foreach (const QRect &rect, rects) { @@ -429,7 +429,7 @@ int MinOverlapPlacer::accumulatedOverlap(const QRect &source, const QList<QRect> Finds among 'source' the rectangle with the minimum accumulated overlap with the rectangles in 'rects'. */ -QRect MinOverlapPlacer::findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects) +QRect MinOverlapPlacer::findMinOverlapRect(const QVector<QRect> &source, const QVector<QRect> &rects) { int minAccOverlap = -1; QRect minAccOverlapRect; @@ -447,28 +447,37 @@ QRect MinOverlapPlacer::findMinOverlapRect(const QList<QRect> &source, const QLi \internal Gets candidates for the final placement. */ -void MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QList<QRect> &rects, - const QRect &domain,QList<QRect> &candidates) +QVector<QRect> MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QVector<QRect> &rects, + const QRect &domain) { - QSet<int> xset; - QSet<int> yset; - xset << domain.left() << domain.right() - size.width() + 1; - yset << domain.top(); + QVector<QRect> result; + + QVector<int> xlist; + xlist.reserve(2 + rects.size()); + xlist << domain.left() << domain.right() - size.width() + 1; + + QVector<int> ylist; + ylist.reserve(2 + rects.size()); + ylist << domain.top(); if (domain.bottom() - size.height() + 1 >= 0) - yset << domain.bottom() - size.height() + 1; + ylist << domain.bottom() - size.height() + 1; + foreach (const QRect &rect, rects) { - xset << rect.right() + 1; - yset << rect.bottom() + 1; + xlist << rect.right() + 1; + ylist << rect.bottom() + 1; } - QList<int> xlist = xset.values(); std::sort(xlist.begin(), xlist.end()); - QList<int> ylist = yset.values(); + xlist.erase(std::unique(xlist.begin(), xlist.end()), xlist.end()); + std::sort(ylist.begin(), ylist.end()); + ylist.erase(std::unique(ylist.begin(), ylist.end()), ylist.end()); + result.reserve(ylist.size() * xlist.size()); foreach (int y, ylist) foreach (int x, xlist) - candidates << QRect(QPoint(x, y), size); + result << QRect(QPoint(x, y), size); + return result; } /*! @@ -476,10 +485,12 @@ void MinOverlapPlacer::getCandidatePlacements(const QSize &size, const QList<QRe Finds all rectangles in 'source' not completely inside 'domain'. The result is stored in 'result' and also removed from 'source'. */ -void MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source, - QList<QRect> &result) +QVector<QRect> MinOverlapPlacer::findNonInsiders(const QRect &domain, QVector<QRect> &source) { - QMutableListIterator<QRect> it(source); + QVector<QRect> result; + result.reserve(source.size()); + + QMutableVectorIterator<QRect> it(source); while (it.hasNext()) { const QRect srcRect = it.next(); if (!domain.contains(srcRect)) { @@ -487,6 +498,8 @@ void MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source it.remove(); } } + + return result; } /*! @@ -494,9 +507,11 @@ void MinOverlapPlacer::findNonInsiders(const QRect &domain, QList<QRect> &source Finds all rectangles in 'source' that overlaps 'domain' by the maximum overlap area between 'domain' and any rectangle in 'source'. The result is stored in 'result'. */ -void MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect> &source, - QList<QRect> &result) +QVector<QRect> MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QVector<QRect> &source) { + QVector<QRect> result; + result.reserve(source.size()); + int maxOverlap = -1; foreach (const QRect &srcRect, source) { QRect intersection = domain.intersected(srcRect); @@ -509,6 +524,8 @@ void MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect result << srcRect; } } + + return result; } /*! @@ -517,17 +534,15 @@ void MinOverlapPlacer::findMaxOverlappers(const QRect &domain, const QList<QRect placement that overlaps the rectangles in 'rects' as little as possible while at the same time being as much as possible inside 'domain'. */ -QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QList<QRect> &rects, - QList<QRect> &source) +QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QVector<QRect> &rects, + QVector<QRect> &source) { - QList<QRect> nonInsiders; - findNonInsiders(domain, source, nonInsiders); + const QVector<QRect> nonInsiders = findNonInsiders(domain, source); if (!source.empty()) return findMinOverlapRect(source, rects).topLeft(); - QList<QRect> maxOverlappers; - findMaxOverlappers(domain, nonInsiders, maxOverlappers); + QVector<QRect> maxOverlappers = findMaxOverlappers(domain, nonInsiders); return findMinOverlapRect(maxOverlappers, rects).topLeft(); } @@ -538,7 +553,7 @@ QPoint MinOverlapPlacer::findBestPlacement(const QRect &domain, const QList<QRec overlaps 'rects' as little as possible and 'domain' as much as possible. Returns the position of the resulting rectangle. */ -QPoint MinOverlapPlacer::place(const QSize &size, const QList<QRect> &rects, +QPoint MinOverlapPlacer::place(const QSize &size, const QVector<QRect> &rects, const QRect &domain) const { if (size.isEmpty() || !domain.isValid()) @@ -548,8 +563,7 @@ QPoint MinOverlapPlacer::place(const QSize &size, const QList<QRect> &rects, return QPoint(); } - QList<QRect> candidates; - getCandidatePlacements(size, rects, domain, candidates); + QVector<QRect> candidates = getCandidatePlacements(size, rects, domain); return findBestPlacement(domain, rects, candidates); } @@ -882,7 +896,8 @@ void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child) return; } - QList<QRect> rects; + QVector<QRect> rects; + rects.reserve(childWindows.size()); QRect parentRect = q->rect(); foreach (QMdiSubWindow *window, childWindows) { if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q) diff --git a/src/widgets/widgets/qmdiarea_p.h b/src/widgets/widgets/qmdiarea_p.h index 90db55438a..360ed2508c 100644 --- a/src/widgets/widgets/qmdiarea_p.h +++ b/src/widgets/widgets/qmdiarea_p.h @@ -59,6 +59,7 @@ #ifndef QT_NO_MDIAREA #include <QList> +#include <QVector> #include <QRect> #include <QPoint> #include <QtWidgets/qapplication.h> @@ -116,24 +117,19 @@ public: // Places the rectangle defined by 'size' relative to 'rects' and 'domain'. // Returns the position of the resulting rectangle. virtual QPoint place( - const QSize &size, const QList<QRect> &rects, const QRect &domain) const = 0; + const QSize &size, const QVector<QRect> &rects, const QRect &domain) const = 0; virtual ~Placer() {} }; class MinOverlapPlacer : public Placer { - QPoint place(const QSize &size, const QList<QRect> &rects, const QRect &domain) const; - static int accumulatedOverlap(const QRect &source, const QList<QRect> &rects); - static QRect findMinOverlapRect(const QList<QRect> &source, const QList<QRect> &rects); - static void getCandidatePlacements( - const QSize &size, const QList<QRect> &rects, const QRect &domain, - QList<QRect> &candidates); - static QPoint findBestPlacement( - const QRect &domain, const QList<QRect> &rects, QList<QRect> &source); - static void findNonInsiders( - const QRect &domain, QList<QRect> &source, QList<QRect> &result); - static void findMaxOverlappers( - const QRect &domain, const QList<QRect> &source, QList<QRect> &result); + QPoint place(const QSize &size, const QVector<QRect> &rects, const QRect &domain) const; + static int accumulatedOverlap(const QRect &source, const QVector<QRect> &rects); + static QRect findMinOverlapRect(const QVector<QRect> &source, const QVector<QRect> &rects); + static QVector<QRect> getCandidatePlacements(const QSize &size, const QVector<QRect> &rects, const QRect &domain); + static QPoint findBestPlacement(const QRect &domain, const QVector<QRect> &rects, QVector<QRect> &source); + static QVector<QRect> findNonInsiders(const QRect &domain, QVector<QRect> &source); + static QVector<QRect> findMaxOverlappers(const QRect &domain, const QVector<QRect> &source); }; } // namespace QMdi diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index 9104074122..5521d866b2 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtWidgets module of the Qt Toolkit. @@ -1321,6 +1321,14 @@ void QMdiSubWindowPrivate::setNormalMode() updateMask(); } +inline void QMdiSubWindowPrivate::storeFocusWidget() +{ + if (QWidget *focus = QApplication::focusWidget()) { + if (!restoreFocusWidget && q_func()->isAncestorOf(focus)) + restoreFocusWidget = focus; + } +} + /*! \internal */ @@ -1333,8 +1341,7 @@ void QMdiSubWindowPrivate::setMaximizeMode() isShadeMode = false; isMaximizeMode = true; - if (!restoreFocusWidget && q->isAncestorOf(QApplication::focusWidget())) - restoreFocusWidget = QApplication::focusWidget(); + storeFocusWidget(); #ifndef QT_NO_SIZEGRIP setSizeGripVisible(false); @@ -1436,6 +1443,7 @@ void QMdiSubWindowPrivate::setActive(bool activate, bool changeFocus) Qt::WindowStates oldWindowState = q->windowState(); q->overrideWindowState(q->windowState() & ~Qt::WindowActive); if (changeFocus) { + storeFocusWidget(); QWidget *focusWidget = QApplication::focusWidget(); if (focusWidget && (focusWidget == q || q->isAncestorOf(focusWidget))) focusWidget->clearFocus(); @@ -2026,6 +2034,9 @@ void QMdiSubWindowPrivate::setFocusWidget() return; } + if (!(q->windowState() & Qt::WindowMinimized) && restoreFocus()) + return; + if (QWidget *focusWidget = baseWidget->focusWidget()) { if (!focusWidget->hasFocus() && q->isAncestorOf(focusWidget) && focusWidget->isVisible() && !q->isMinimized() @@ -2048,16 +2059,19 @@ void QMdiSubWindowPrivate::setFocusWidget() q->setFocus(); } -void QMdiSubWindowPrivate::restoreFocus() +bool QMdiSubWindowPrivate::restoreFocus() { - if (!restoreFocusWidget) - return; - if (!restoreFocusWidget->hasFocus() && q_func()->isAncestorOf(restoreFocusWidget) - && restoreFocusWidget->isVisible() - && restoreFocusWidget->focusPolicy() != Qt::NoFocus) { - restoreFocusWidget->setFocus(); + if (restoreFocusWidget.isNull()) + return false; + QWidget *candidate = restoreFocusWidget; + restoreFocusWidget.clear(); + if (!candidate->hasFocus() && q_func()->isAncestorOf(candidate) + && candidate->isVisible() + && candidate->focusPolicy() != Qt::NoFocus) { + candidate->setFocus(); + return true; } - restoreFocusWidget = 0; + return candidate->hasFocus(); } /*! @@ -2605,9 +2619,7 @@ void QMdiSubWindow::showShaded() d->isMaximizeMode = false; - QWidget *currentFocusWidget = QApplication::focusWidget(); - if (!d->restoreFocusWidget && isAncestorOf(currentFocusWidget)) - d->restoreFocusWidget = currentFocusWidget; + d->storeFocusWidget(); if (!d->isShadeRequestFromMinimizeMode) { d->isShadeMode = true; @@ -2621,7 +2633,7 @@ void QMdiSubWindow::showShaded() // showMinimized() will reset Qt::WindowActive, which makes sense // for top level widgets, but in MDI it makes sense to have an // active window which is minimized. - if (hasFocus() || isAncestorOf(currentFocusWidget)) + if (hasFocus() || isAncestorOf(QApplication::focusWidget())) d->ensureWindowState(Qt::WindowActive); #ifndef QT_NO_SIZEGRIP diff --git a/src/widgets/widgets/qmdisubwindow_p.h b/src/widgets/widgets/qmdisubwindow_p.h index 90369ff526..6abbe89b4c 100644 --- a/src/widgets/widgets/qmdisubwindow_p.h +++ b/src/widgets/widgets/qmdisubwindow_p.h @@ -266,7 +266,8 @@ public: QPalette desktopPalette() const; void updateActions(); void setFocusWidget(); - void restoreFocus(); + bool restoreFocus(); + void storeFocusWidget(); void setWindowFlags(Qt::WindowFlags windowFlags); void setVisible(WindowStateAction, bool visible = true); #ifndef QT_NO_ACTION diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 7e48badea5..846199ebe2 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -187,8 +187,8 @@ void QMenuPrivate::syncPlatformMenu() QPlatformMenuItem *menuItem = platformMenu->createMenuItem(); QAction *action = it.previous(); menuItem->setTag(reinterpret_cast<quintptr>(action)); - QObject::connect(menuItem, SIGNAL(activated()), action, SLOT(trigger())); - QObject::connect(menuItem, SIGNAL(hovered()), action, SIGNAL(hovered())); + QObject::connect(menuItem, SIGNAL(activated()), action, SLOT(trigger()), Qt::QueuedConnection); + QObject::connect(menuItem, SIGNAL(hovered()), action, SIGNAL(hovered()), Qt::QueuedConnection); copyActionToPlatformItem(action, menuItem); platformMenu->insertMenuItem(menuItem, beforeItem); beforeItem = menuItem; diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp index 23c1e99231..df13085dee 100644 --- a/src/widgets/widgets/qplaintextedit.cpp +++ b/src/widgets/widgets/qplaintextedit.cpp @@ -2537,6 +2537,8 @@ void QPlainTextEdit::setReadOnly(bool ro) } setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); d->control->setTextInteractionFlags(flags); + QEvent event(QEvent::ReadOnlyChange); + QApplication::sendEvent(this, &event); } /*! diff --git a/src/widgets/widgets/qradiobutton.h b/src/widgets/widgets/qradiobutton.h index 104d4a12e0..8888ccf86d 100644 --- a/src/widgets/widgets/qradiobutton.h +++ b/src/widgets/widgets/qradiobutton.h @@ -73,6 +73,7 @@ protected: private: Q_DECLARE_PRIVATE(QRadioButton) Q_DISABLE_COPY(QRadioButton) + friend class QAccessibleButton; }; QT_END_NAMESPACE diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp index 789ec2f6fd..e8fdc65711 100644 --- a/src/widgets/widgets/qtabbar.cpp +++ b/src/widgets/widgets/qtabbar.cpp @@ -632,6 +632,14 @@ void QTabBarPrivate::layoutWidgets(int start) } } +void QTabBarPrivate::autoHideTabs() +{ + Q_Q(QTabBar); + + if (autoHide) + q->setVisible(q->count() > 1); +} + void QTabBarPrivate::_q_closeTab() { Q_Q(QTabBar); @@ -861,6 +869,7 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text) } tabInserted(index); + d->autoHideTabs(); return index; } @@ -936,6 +945,7 @@ void QTabBar::removeTab(int index) setCurrentIndex(d->currentIndex - 1); } d->refresh(); + d->autoHideTabs(); tabRemoved(index); } } @@ -2267,6 +2277,36 @@ void QTabBar::setDocumentMode(bool enabled) } /*! + \property QTabBar::autoHide + \brief If true, the tab bar is automatically hidden when it contains less + than 2 tabs. + \since 5.4 + + By default, this property is false. + + \sa QWidget::visible +*/ + +bool QTabBar::autoHide() const +{ + Q_D(const QTabBar); + return d->autoHide; +} + +void QTabBar::setAutoHide(bool hide) +{ + Q_D(QTabBar); + if (d->autoHide == hide) + return; + + d->autoHide = hide; + if (hide) + d->autoHideTabs(); + else + setVisible(true); +} + +/*! Sets \a widget on the tab \a index. The widget is placed on the left or right hand side depending upon the \a position. \since 4.5 diff --git a/src/widgets/widgets/qtabbar.h b/src/widgets/widgets/qtabbar.h index a0e52c2f86..a1616b1524 100644 --- a/src/widgets/widgets/qtabbar.h +++ b/src/widgets/widgets/qtabbar.h @@ -70,6 +70,7 @@ class Q_WIDGETS_EXPORT QTabBar: public QWidget Q_PROPERTY(bool expanding READ expanding WRITE setExpanding) Q_PROPERTY(bool movable READ isMovable WRITE setMovable) Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode) + Q_PROPERTY(bool autoHide READ autoHide WRITE setAutoHide) public: explicit QTabBar(QWidget* parent=0); @@ -166,6 +167,9 @@ public: bool documentMode() const; void setDocumentMode(bool set); + bool autoHide() const; + void setAutoHide(bool hide); + public Q_SLOTS: void setCurrentIndex(int index); diff --git a/src/widgets/widgets/qtabbar_p.h b/src/widgets/widgets/qtabbar_p.h index 3228308bc6..1238057e2a 100644 --- a/src/widgets/widgets/qtabbar_p.h +++ b/src/widgets/widgets/qtabbar_p.h @@ -77,7 +77,7 @@ public: :currentIndex(-1), pressedIndex(-1), shape(QTabBar::RoundedNorth), layoutDirty(false), drawBase(true), scrollOffset(0), elideModeSetByUser(false), useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false), selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), - dragInProgress(false), documentMode(false), movingTab(0) + dragInProgress(false), documentMode(false), autoHide(false), movingTab(0) #ifdef Q_WS_MAC , previousPressedIndex(-1) #endif @@ -184,6 +184,7 @@ public: void updateMacBorderMetrics(); bool isTabInMacUnifiedToolbarArea() const; void setupMovableTab(); + void autoHideTabs(); void makeVisible(int index); QSize iconSize; @@ -201,6 +202,7 @@ public: bool movable; bool dragInProgress; bool documentMode; + bool autoHide; QWidget *movingTab; #ifdef Q_WS_MAC diff --git a/src/widgets/widgets/qtabwidget.cpp b/src/widgets/widgets/qtabwidget.cpp index fced1c01ec..616b66b80b 100644 --- a/src/widgets/widgets/qtabwidget.cpp +++ b/src/widgets/widgets/qtabwidget.cpp @@ -1351,6 +1351,29 @@ void QTabWidget::setDocumentMode(bool enabled) } /*! + \property QTabWidget::tabBarAutoHide + \brief If true, the tab bar is automatically hidden when it contains less + than 2 tabs. + \since 5.4 + + By default, this property is false. + + \sa QWidget::visible +*/ + +bool QTabWidget::tabBarAutoHide() const +{ + Q_D(const QTabWidget); + return d->tabs->autoHide(); +} + +void QTabWidget::setTabBarAutoHide(bool enabled) +{ + Q_D(QTabWidget); + return d->tabs->setAutoHide(enabled); +} + +/*! Removes all the pages, but does not delete them. Calling this function is equivalent to calling removeTab() until the tab widget is empty. */ diff --git a/src/widgets/widgets/qtabwidget.h b/src/widgets/widgets/qtabwidget.h index 83c2e31d28..046e544bf0 100644 --- a/src/widgets/widgets/qtabwidget.h +++ b/src/widgets/widgets/qtabwidget.h @@ -68,6 +68,7 @@ class Q_WIDGETS_EXPORT QTabWidget : public QWidget Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode) Q_PROPERTY(bool tabsClosable READ tabsClosable WRITE setTabsClosable) Q_PROPERTY(bool movable READ isMovable WRITE setMovable) + Q_PROPERTY(bool tabBarAutoHide READ tabBarAutoHide WRITE setTabBarAutoHide) public: explicit QTabWidget(QWidget *parent = 0); @@ -140,6 +141,9 @@ public: bool documentMode() const; void setDocumentMode(bool set); + bool tabBarAutoHide() const; + void setTabBarAutoHide(bool enabled); + void clear(); QTabBar* tabBar() const; diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp index a33ac0817f..f106b70c12 100644 --- a/src/widgets/widgets/qtextedit.cpp +++ b/src/widgets/widgets/qtextedit.cpp @@ -2122,6 +2122,8 @@ void QTextEdit::setReadOnly(bool ro) } d->control->setTextInteractionFlags(flags); setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this)); + QEvent event(QEvent::ReadOnlyChange); + QApplication::sendEvent(this, &event); } /*! diff --git a/src/widgets/widgets/qtoolbar.cpp b/src/widgets/widgets/qtoolbar.cpp index 1c05529cd6..834c2fe56b 100644 --- a/src/widgets/widgets/qtoolbar.cpp +++ b/src/widgets/widgets/qtoolbar.cpp @@ -720,6 +720,10 @@ void QToolBar::setIconSize(const QSize &iconSize) as \l{QAction}s. Note that if you add a QToolButton with the addWidget() method, it will not get this button style. + To have the style of toolbuttons follow the system settings, set this property to Qt::ToolButtonFollowStyle. + On Unix, the user settings from the desktop environment will be used. + On other platforms, Qt::ToolButtonFollowStyle means icon only. + The default is Qt::ToolButtonIconOnly. */ diff --git a/src/widgets/widgets/qtoolbutton.cpp b/src/widgets/widgets/qtoolbutton.cpp index 497bc52109..1b5ba8cd58 100644 --- a/src/widgets/widgets/qtoolbutton.cpp +++ b/src/widgets/widgets/qtoolbutton.cpp @@ -384,8 +384,9 @@ QSize QToolButton::minimumSizeHint() const The default is Qt::ToolButtonIconOnly. - To have the style of toolbuttons follow the system settings (as available - in GNOME and KDE desktop environments), set this property to Qt::ToolButtonFollowStyle. + To have the style of toolbuttons follow the system settings, set this property to Qt::ToolButtonFollowStyle. + On Unix, the user settings from the desktop environment will be used. + On other platforms, Qt::ToolButtonFollowStyle means icon only. QToolButton automatically connects this slot to the relevant signal in the QMainWindow in which is resides. diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp index 75f30599be..b927004773 100644 --- a/src/widgets/widgets/qwidgetlinecontrol.cpp +++ b/src/widgets/widgets/qwidgetlinecontrol.cpp @@ -355,8 +355,10 @@ void QWidgetLineControl::init(const QString &txt) m_text = txt; updateDisplayText(); m_cursor = m_text.length(); - if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { m_keyboardScheme = theme->themeHint(QPlatformTheme::KeyboardScheme).toInt(); + m_passwordMaskDelay = theme->themeHint(QPlatformTheme::PasswordMaskDelay).toInt(); + } // Generalize for X11 if (m_keyboardScheme == QPlatformTheme::KdeKeyboardScheme || m_keyboardScheme == QPlatformTheme::GnomeKeyboardScheme @@ -787,7 +789,7 @@ void QWidgetLineControl::internalInsert(const QString &s) if (m_echoMode == QLineEdit::Password) { if (m_passwordEchoTimer != 0) killTimer(m_passwordEchoTimer); - int delay = qGuiApp->styleHints()->passwordMaskDelay(); + int delay = m_passwordMaskDelay; #ifdef QT_BUILD_INTERNAL if (m_passwordMaskDelayOverride >= 0) delay = m_passwordMaskDelayOverride; diff --git a/src/widgets/widgets/qwidgetlinecontrol_p.h b/src/widgets/widgets/qwidgetlinecontrol_p.h index 1cee67bfd2..ba73e9e25e 100644 --- a/src/widgets/widgets/qwidgetlinecontrol_p.h +++ b/src/widgets/widgets/qwidgetlinecontrol_p.h @@ -90,6 +90,7 @@ public: m_tripleClickTimer(0), m_maskData(0), m_modifiedState(0), m_undoState(0), m_selstart(0), m_selend(0), m_passwordEchoEditing(false) , m_passwordEchoTimer(0) + , m_passwordMaskDelay(-1) #if defined(Q_WS_MAC) , m_threadChecks(false) , m_textLayoutThread(0) @@ -313,6 +314,9 @@ public: QChar passwordCharacter() const { return m_passwordCharacter; } void setPasswordCharacter(QChar character) { m_passwordCharacter = character; updateDisplayText(); } + int passwordMaskDelay() const { return m_passwordMaskDelay; } + void setPasswordMaskDelay(int delay) { m_passwordMaskDelay = delay; } + Qt::LayoutDirection layoutDirection() const { if (m_layoutDirection == Qt::LayoutDirectionAuto) { if (m_text.isEmpty()) @@ -481,6 +485,7 @@ private: bool m_passwordEchoEditing; QChar m_passwordCharacter; int m_passwordEchoTimer; + int m_passwordMaskDelay; void cancelPasswordEchoTimer() { if (m_passwordEchoTimer != 0) { diff --git a/src/widgets/widgets/qwidgetresizehandler.cpp b/src/widgets/widgets/qwidgetresizehandler.cpp index ba4e69126a..d4d44e60cd 100644 --- a/src/widgets/widgets/qwidgetresizehandler.cpp +++ b/src/widgets/widgets/qwidgetresizehandler.cpp @@ -58,6 +58,10 @@ QT_BEGIN_NAMESPACE static bool resizeHorizontalDirectionFixed = false; static bool resizeVerticalDirectionFixed = false; +// ### fixme: Qt 6: No longer export QWidgetResizeHandler and remove "Move" +// functionality. Currently, only the resize functionality is used by QDockWidget. +// Historically, the class was used in Qt 3's QWorkspace (predecessor to QMdiArea). + QWidgetResizeHandler::QWidgetResizeHandler(QWidget *parent, QWidget *cw) : QObject(parent), widget(parent), childWidget(cw ? cw : parent), fw(0), extrahei(0), buttonDown(false), moveResizeMode(false), sizeprotect(true), movingEnabled(true) diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 96438a0bdf..5f60bf02d0 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -645,13 +645,15 @@ void QWidgetTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someC void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, int charsAdded) { - Q_Q(QWidgetTextControl); #ifndef QT_NO_ACCESSIBILITY + Q_Q(QWidgetTextControl); if (QAccessible::isActive() && q->parent() && q->parent()->isWidgetType()) { QTextCursor tmp(doc); tmp.setPosition(from); - tmp.setPosition(from + charsAdded, QTextCursor::KeepAnchor); + // when setting a new text document the length is off + // QTBUG-32583 - characterCount is off by 1 requires the -1 + tmp.setPosition(qMin(doc->characterCount() - 1, from + charsAdded), QTextCursor::KeepAnchor); QString newText = tmp.selectedText(); // always report the right number of removed chars, but in lack of the real string use spaces @@ -668,6 +670,10 @@ void QWidgetTextControlPrivate::_q_contentsChanged(int from, int charsRemoved, i QAccessible::updateAccessibility(ev); delete ev; } +#else + Q_UNUSED(from) + Q_UNUSED(charsRemoved) + Q_UNUSED(charsAdded) #endif } |