summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java11
-rw-r--r--src/corelib/global/qcompilerdetection.qdoc12
-rw-r--r--src/corelib/global/qrandom.cpp4
-rw-r--r--src/corelib/itemmodels/qrangemodel.cpp248
-rw-r--r--src/corelib/itemmodels/qrangemodel.h22
-rw-r--r--src/corelib/itemmodels/qrangemodel_impl.h279
-rw-r--r--src/corelib/kernel/qvariant.h2
-rw-r--r--src/corelib/tools/qmultimap.qdoc2
-rw-r--r--src/gui/image/qicon.h2
-rw-r--r--src/gui/image/qicon_p.h5
-rw-r--r--src/gui/kernel/qevent.cpp5
-rw-r--r--src/gui/painting/qcoregraphics.mm34
-rw-r--r--src/gui/painting/qcoregraphics_p.h3
-rw-r--r--src/gui/painting/qdrawhelper_avx2.cpp4
-rw-r--r--src/gui/platform/darwin/qappleiconengine.mm16
-rw-r--r--src/gui/platform/darwin/qappleiconengine_p.h5
-rw-r--r--src/gui/rhi/qrhivulkan.cpp4
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp34
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp6
-rw-r--r--src/plugins/platforms/android/androidjnimain.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm18
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.mm20
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm25
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm4
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp11
-rw-r--r--src/plugins/platforms/ios/qiosdocumentpickercontroller.mm15
-rw-r--r--src/plugins/platforms/wasm/qwasmaccessibility.cpp54
-rw-r--r--src/plugins/styles/modernwindows/qwindows11style.cpp20
-rw-r--r--src/widgets/accessible/itemviews.cpp71
-rw-r--r--src/widgets/accessible/itemviews_p.h14
-rw-r--r--src/widgets/styles/qstylesheetstyle.cpp12
-rw-r--r--src/widgets/widgets/qmenu.cpp8
32 files changed, 829 insertions, 142 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java
index 1d4ec370d39..93407d52e95 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java
@@ -241,12 +241,15 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
return;
}
- final AccessibilityEvent event =
- obtainAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ final CharSequence className = getNodeForVirtualViewId(viewId).getClassName();
+ final int eventType =
+ className != null && className.equals("android.widget.ProgressBar")
+ ? AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ : AccessibilityEvent.TYPE_ANNOUNCEMENT;
+ final AccessibilityEvent event = obtainAccessibilityEvent(eventType);
event.setEnabled(true);
- event.setClassName(getNodeForVirtualViewId(viewId).getClassName());
-
+ event.setClassName(className);
event.setContentDescription(value);
if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) {
diff --git a/src/corelib/global/qcompilerdetection.qdoc b/src/corelib/global/qcompilerdetection.qdoc
index 9b49a1491ab..e64dca74667 100644
--- a/src/corelib/global/qcompilerdetection.qdoc
+++ b/src/corelib/global/qcompilerdetection.qdoc
@@ -277,6 +277,18 @@
\row \li \c{constexpr} \li C++11 \li yes \li required
\row \li \c{constinit} \li C++20 \li no \li required
\endtable
+
+ If declaration and definition of a Q_CONSTINIT variable are separate, this
+ macro goes only on the definition, and not on the declaration:
+
+ \code
+ class C {
+ ~~~~
+ static int count; // declaration, no Q_CONSTINIT here
+ };
+ ~~~
+ Q_CONSTINIT int C::count = 0; // definition
+ \endcode
*/
/*!
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index dc7d90f4b30..8d545242095 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -699,7 +699,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
Returns the minimum value that QRandomGenerator may ever generate. That is, 0.
- \sa max(), QRandomGenerator64::min()
+ \sa max()
*/
/*!
@@ -708,7 +708,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
Returns the maximum value that QRandomGenerator may ever generate. That is,
\c {std::numeric_limits<result_type>::max()}.
- \sa min(), QRandomGenerator64::max()
+ \sa min()
*/
/*!
diff --git a/src/corelib/itemmodels/qrangemodel.cpp b/src/corelib/itemmodels/qrangemodel.cpp
index b0c6c46b125..f533605c721 100644
--- a/src/corelib/itemmodels/qrangemodel.cpp
+++ b/src/corelib/itemmodels/qrangemodel.cpp
@@ -7,6 +7,8 @@
#include <QtCore/private/qabstractitemmodel_p.h>
+#include <variant>
+
QT_BEGIN_NAMESPACE
class QRangeModelPrivate : QAbstractItemModelPrivate
@@ -18,11 +20,95 @@ public:
: impl(std::move(impl))
{}
-private:
std::unique_ptr<QRangeModelImplBase, QRangeModelImplBase::Deleter> impl;
friend class QRangeModelImplBase;
+ static QRangeModelPrivate *get(QRangeModel *model) { return model->d_func(); }
+ static const QRangeModelPrivate *get(const QRangeModel *model) { return model->d_func(); }
+
mutable QHash<int, QByteArray> m_roleNames;
+ QRangeModel::AutoConnectPolicy m_autoConnectPolicy = QRangeModel::AutoConnectPolicy::None;
+ bool m_dataChangedDispatchBlocked = false;
+
+ static void emitDataChanged(const QModelIndex &index, int role)
+ {
+ const auto *model = static_cast<const QRangeModel *>(index.model());
+ if (!get(model)->m_dataChangedDispatchBlocked) {
+ const auto *emitter = QRangeModelImplBase::getImplementation(model);
+ const_cast<QRangeModelImplBase *>(emitter)->dataChanged(index, index, {role});
+ }
+ }
+};
+
+struct PropertyChangedHandler
+{
+ PropertyChangedHandler(const QPersistentModelIndex &index, int role)
+ : storage{Data{index, role}}
+ {}
+
+ // move-only
+ ~PropertyChangedHandler() = default;
+ PropertyChangedHandler(PropertyChangedHandler &&other) noexcept
+ : connection(std::move(other.connection)), storage(std::move(other.storage))
+ {
+ Q_ASSERT(std::holds_alternative<Data>(storage));
+ // A moved-from handler is essentially a reference to the moved-to
+ // handler (which lives inside QSlotObject/QCallableObject). This
+ // way we can update the stored handler with the created connection.
+ other.storage = this;
+ }
+ PropertyChangedHandler &operator=(PropertyChangedHandler &&) = delete;
+ PropertyChangedHandler(const PropertyChangedHandler &) = delete;
+ PropertyChangedHandler &operator=(const PropertyChangedHandler &) = delete;
+
+ // we can assign a connection to a moved-from handler to update the
+ // handler stored in the QSlotObject/QCallableObject.
+ PropertyChangedHandler &operator=(const QMetaObject::Connection &connection) noexcept
+ {
+ Q_ASSERT(std::holds_alternative<PropertyChangedHandler *>(storage));
+ std::get<PropertyChangedHandler *>(storage)->connection = connection;
+ return *this;
+ }
+
+ void operator()();
+
+private:
+ QMetaObject::Connection connection;
+ struct Data
+ {
+ QPersistentModelIndex index;
+ int role = -1;
+ };
+ std::variant<PropertyChangedHandler *, Data> storage;
+};
+
+void PropertyChangedHandler::operator()()
+{
+ Q_ASSERT(std::holds_alternative<Data>(storage));
+ const auto &data = std::get<Data>(storage);
+ if (!data.index.isValid()) {
+ if (!QObject::disconnect(connection))
+ qWarning() << "Failed to break connection for" << Qt::ItemDataRole(data.role);
+ } else {
+ QRangeModelPrivate::emitDataChanged(data.index, data.role);
+ }
+}
+
+struct ConstPropertyChangedHandler
+{
+ ConstPropertyChangedHandler(const QModelIndex &index, int role)
+ : index(index), role(role)
+ {}
+
+ // move-only
+ ~ConstPropertyChangedHandler() = default;
+ ConstPropertyChangedHandler(ConstPropertyChangedHandler &&other) noexcept = default;
+
+ void operator()() { QRangeModelPrivate::emitDataChanged(index, role); }
+
+private:
+ QModelIndex index;
+ int role = -1;
};
QRangeModel::QRangeModel(QRangeModelImplBase *impl, QObject *parent)
@@ -40,6 +126,96 @@ const QRangeModelImplBase *QRangeModelImplBase::getImplementation(const QRangeMo
return model->d_func()->impl.get();
}
+QScopedValueRollback<bool> QRangeModelImplBase::blockDataChangedDispatch()
+{
+ return QScopedValueRollback(m_rangeModel->d_func()->m_dataChangedDispatchBlocked, true);
+}
+
+/*!
+ \internal
+
+ Using \a metaObject, return a mapping of roles to the matching QMetaProperties.
+*/
+QHash<int, QMetaProperty> QRangeModelImplBase::roleProperties(const QAbstractItemModel &model,
+ const QMetaObject &metaObject)
+{
+ const auto roles = model.roleNames();
+ QHash<int, QMetaProperty> result;
+ for (auto &&[role, roleName] : roles.asKeyValueRange()) {
+ if (role == Qt::RangeModelDataRole)
+ continue;
+ result[role] = metaObject.property(metaObject.indexOfProperty(roleName));
+ }
+ return result;
+}
+
+template <auto Handler>
+static bool connectPropertiesHelper(const QModelIndex &index, QObject *item, QObject *context,
+ const QHash<int, QMetaProperty> &properties)
+{
+ if (!item)
+ return false;
+ for (auto &&[role, property] : properties.asKeyValueRange()) {
+ if (property.hasNotifySignal()) {
+ if (!Handler(index, item, context, role, property))
+ return false;
+ } else {
+ qWarning() << "Property" << property.name() << "for" << Qt::ItemDataRole(role)
+ << "has no notify signal";
+ }
+ }
+ return true;
+}
+
+bool QRangeModelImplBase::connectProperty(const QModelIndex &index, QObject *item, QObject *context,
+ int role, const QMetaProperty &property)
+{
+ if (!item)
+ return false;
+ PropertyChangedHandler handler{index, role};
+ auto connection = property.enclosingMetaObject()->connect(item, property.notifySignal(),
+ context, std::move(handler));
+ if (!connection) {
+ qWarning() << "Failed to connect to" << item << property.name();
+ return false;
+ } else {
+ // handler is now in moved-from state, and acts like a reference to
+ // the handler that is stored in the QSlotObject/QCallableObject.
+ // This assignment updates the stored handler's connection with the
+ // QMetaObject::Connection handle, and should look harmless for
+ // static analyzers.
+ handler = connection;
+ }
+ return true;
+}
+
+bool QRangeModelImplBase::connectProperties(const QModelIndex &index, QObject *item, QObject *context,
+ const QHash<int, QMetaProperty> &properties)
+{
+ return connectPropertiesHelper<QRangeModelImplBase::connectProperty>(index, item, context, properties);
+}
+
+bool QRangeModelImplBase::connectPropertyConst(const QModelIndex &index, QObject *item, QObject *context,
+ int role, const QMetaProperty &property)
+{
+ if (!item)
+ return false;
+ ConstPropertyChangedHandler handler{index, role};
+ if (!property.enclosingMetaObject()->connect(item, property.notifySignal(),
+ context, std::move(handler))) {
+ qWarning() << "Failed to connect to" << item << property.name();
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool QRangeModelImplBase::connectPropertiesConst(const QModelIndex &index, QObject *item, QObject *context,
+ const QHash<int, QMetaProperty> &properties)
+{
+ return connectPropertiesHelper<QRangeModelImplBase::connectPropertyConst>(index, item, context, properties);
+}
+
/*!
\class QRangeModel
\inmodule QtCore
@@ -1142,6 +1318,9 @@ void QRangeModel::setRoleNames(const QHash<int, QByteArray> &names)
return;
beginResetModel();
d->impl->call<QRangeModelImplBase::InvalidateCaches>();
+ if (d->m_autoConnectPolicy != AutoConnectPolicy::None)
+ d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
+
d->m_roleNames = names;
endResetModel();
Q_EMIT roleNamesChanged();
@@ -1153,6 +1332,73 @@ void QRangeModel::resetRoleNames()
}
/*!
+ \enum QRangeModel::AutoConnectPolicy
+ \since 6.11
+
+ This enum defines if and when QRangeModel auto-connects changed-signals for
+ properties to the \l{QAbstractItemModel::}{dataChanged()} signal of the
+ model. Only properties that match one of the \l{roleNames()}{role names}
+ are connected.
+
+ \value None No connections are made automatically.
+ \value Full The signals for all relevant properties are connected
+ automatically, for all QObject items. This includes QObject
+ items that are added to newly inserted rows and columns.
+ \value OnRead Signals for relevant properties are connected the first time
+ the model reads the property.
+
+ The memory overhead of making automatic connections can be substantial. A
+ Full auto-connection does not require any book-keeping in addition to the
+ connection itself, but each connection takes memory, and connecting all
+ properties of all objects can be very costly, especially if only a few
+ properties of a subset of objects will ever change.
+
+ The OnRead connection policy will not connect to objects or properties that
+ are never read from (for instance, never rendered in a view), but remembering
+ which connections have been made requires some book-keeping overhead, and
+ unpredictable memory growth over time. For instance, scrolling down a long
+ list of items can easily result in thousands of new connections being made.
+
+ \sa autoConnectPolicy, roleNames()
+*/
+
+/*!
+ \property QRangeModel::autoConnectPolicy
+ \brief if and when the model auto-connects to property changed notifications.
+
+ If QRangeModel operates on a data structure that holds the same type of
+ QObject subclass as its item type, then it can automatically connect the
+ properties of the QObjects to the dataChanged() signal. This is done for
+ those properties that match one of the \l{roleNames()}{role names}.
+
+ By default, the value of this property is \l{QRangeModel::AutoConnectPolicy::}
+ {None}, so no such connections are made. Changing the value of this property
+ always breaks all existing connections.
+
+ \note Connections are not broken or created if QObjects in the data
+ structure that QRangeModel operates on are swapped out.
+
+ \sa roleNames()
+*/
+
+QRangeModel::AutoConnectPolicy QRangeModel::autoConnectPolicy() const
+{
+ Q_D(const QRangeModel);
+ return d->m_autoConnectPolicy;
+}
+
+void QRangeModel::setAutoConnectPolicy(QRangeModel::AutoConnectPolicy policy)
+{
+ Q_D(QRangeModel);
+ if (d->m_autoConnectPolicy == policy)
+ return;
+
+ d->m_autoConnectPolicy = policy;
+ d->impl->call<QRangeModelImplBase::SetAutoConnectPolicy>();
+ Q_EMIT autoConnectPolicyChanged();
+}
+
+/*!
\reimp
*/
void QRangeModel::sort(int column, Qt::SortOrder order)
diff --git a/src/corelib/itemmodels/qrangemodel.h b/src/corelib/itemmodels/qrangemodel.h
index aacaf0f9e5a..d15f40d37a9 100644
--- a/src/corelib/itemmodels/qrangemodel.h
+++ b/src/corelib/itemmodels/qrangemodel.h
@@ -16,8 +16,17 @@ class Q_CORE_EXPORT QRangeModel : public QAbstractItemModel
Q_OBJECT
Q_PROPERTY(QHash<int, QByteArray> roleNames READ roleNames WRITE setRoleNames RESET resetRoleNames
NOTIFY roleNamesChanged FINAL)
+ Q_PROPERTY(AutoConnectPolicy autoConnectPolicy READ autoConnectPolicy WRITE setAutoConnectPolicy
+ NOTIFY autoConnectPolicyChanged FINAL)
public:
+ enum class AutoConnectPolicy {
+ None,
+ Full,
+ OnRead,
+ };
+ Q_ENUM(AutoConnectPolicy)
+
enum class RowCategory {
Default,
MultiRoleItem,
@@ -100,8 +109,12 @@ public:
Qt::DropActions supportedDragActions() const override;
Qt::DropActions supportedDropActions() const override;
+ AutoConnectPolicy autoConnectPolicy() const;
+ void setAutoConnectPolicy(AutoConnectPolicy policy);
+
Q_SIGNALS:
void roleNamesChanged();
+ void autoConnectPolicyChanged();
protected Q_SLOTS:
void resetInternalData() override;
@@ -128,6 +141,10 @@ void QRangeModelImplBase::changePersistentIndexList(const QModelIndexList &from,
{
m_rangeModel->changePersistentIndexList(from, to);
}
+QHash<int, QByteArray> QRangeModelImplBase::roleNames() const
+{
+ return m_rangeModel->roleNames();
+}
void QRangeModelImplBase::dataChanged(const QModelIndex &from, const QModelIndex &to,
const QList<int> &roles)
{
@@ -196,6 +213,11 @@ const QAbstractItemModel &QRangeModelImplBase::itemModel() const
return *m_rangeModel;
}
+QRangeModelImplBase::AutoConnectPolicy QRangeModelImplBase::autoConnectPolicy() const
+{
+ return QRangeModelImplBase::AutoConnectPolicy(m_rangeModel->autoConnectPolicy());
+}
+
// Helper templates that we can forward declare in the _impl header,
// where QRangeModel is not yet defined.
namespace QRangeModelDetails
diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h
index c2f473542d7..a635dd69379 100644
--- a/src/corelib/itemmodels/qrangemodel_impl.h
+++ b/src/corelib/itemmodels/qrangemodel_impl.h
@@ -20,6 +20,8 @@
#include <QtCore/qmetaobject.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmap.h>
+#include <QtCore/qscopedvaluerollback.h>
+#include <QtCore/qset.h>
#include <algorithm>
#include <functional>
@@ -856,7 +858,7 @@ namespace QRangeModelDetails
static constexpr bool is_default = is_any_of<protocol, ListProtocol, TableProtocol, DefaultTreeProtocol>();
};
- template <bool cacheProperties>
+ template <bool cacheProperties, bool itemsAreQObjects>
struct PropertyData {
static constexpr bool cachesProperties = false;
@@ -864,7 +866,7 @@ namespace QRangeModelDetails
};
template <>
- struct PropertyData<true>
+ struct PropertyData<true, false>
{
static constexpr bool cachesProperties = true;
mutable QHash<int, QMetaProperty> properties;
@@ -875,6 +877,32 @@ namespace QRangeModelDetails
}
};
+ template <>
+ struct PropertyData<true, true> : PropertyData<true, false>
+ {
+ struct Connection {
+ QObject *sender;
+ int role;
+
+ friend bool operator==(const Connection &lhs, const Connection &rhs) noexcept
+ {
+ return lhs.sender == rhs.sender && lhs.role == rhs.role;
+ }
+ friend size_t qHash(const Connection &c, size_t seed) noexcept
+ {
+ return qHashMulti(seed, c.sender, c.role);
+ }
+ };
+
+ QObject *context = nullptr;
+ mutable QSet<Connection> connections;
+
+ void invalidateCaches()
+ {
+ properties.clear();
+ }
+ };
+
// The storage of the model data. We might store it as a pointer, or as a
// (copied- or moved-into) value (or smart pointer). But we always return a
// raw pointer.
@@ -899,7 +927,8 @@ namespace QRangeModelDetails
template <typename ModelStorage, typename ItemType>
struct ModelData : Storage<ModelStorage>,
- PropertyData<has_metaobject_v<ItemType>>
+ PropertyData<has_metaobject_v<ItemType>,
+ std::is_base_of_v<QObject, std::remove_pointer_t<ItemType>>>
{
using WrappedStorage = Storage<wrapped_t<ModelStorage>>;
using iterator = typename WrappedStorage::iterator;
@@ -969,8 +998,14 @@ protected:
}
public:
- // overridable prototypes (quasi-pure-virtual methods)
+ // keep in sync with QRangeModel::AutoConnectPolicy
+ enum class AutoConnectPolicy {
+ None,
+ Full,
+ OnRead,
+ };
+ // overridable prototypes (quasi-pure-virtual methods)
void invalidateCaches();
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role);
bool setData(const QModelIndex &index, const QVariant &data, int role);
@@ -991,10 +1026,11 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QVariant data(const QModelIndex &index, int role) const;
QMap<int, QVariant> itemData(const QModelIndex &index) const;
- QHash<int, QByteArray> roleNames() const;
+ inline QHash<int, QByteArray> roleNames() const;
QModelIndex parent(const QModelIndex &child) const;
void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const;
+ void setAutoConnectPolicy();
// bindings for overriding
@@ -1023,6 +1059,7 @@ public:
// 6.11
using MultiData = Method<&Self::multiData>;
+ using SetAutoConnectPolicy = Method<&Self::setAutoConnectPolicy>;
template <typename C>
using MethodTemplates = std::tuple<
@@ -1048,7 +1085,8 @@ public:
typename C::Data,
typename C::ItemData,
typename C::RoleNames,
- typename C::MultiData
+ typename C::MultiData,
+ typename C::SetAutoConnectPolicy
>;
static Q_CORE_EXPORT QRangeModelImplBase *getImplementation(QRangeModel *model);
@@ -1056,6 +1094,8 @@ public:
private:
friend class QRangeModelPrivate;
+ friend struct PropertyChangedHandler;
+
QRangeModel *m_rangeModel;
protected:
@@ -1081,6 +1121,7 @@ protected:
inline bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast,
const QModelIndex &destParent, int destRow);
inline void endMoveRows();
+ inline AutoConnectPolicy autoConnectPolicy() const;
inline QAbstractItemModel &itemModel();
inline const QAbstractItemModel &itemModel() const;
@@ -1088,6 +1129,19 @@ protected:
Q_CORE_EXPORT static QHash<int, QByteArray> roleNamesForMetaObject(const QAbstractItemModel &model,
const QMetaObject &metaObject);
Q_CORE_EXPORT static QHash<int, QByteArray> roleNamesForSimpleType();
+
+ Q_CORE_EXPORT QScopedValueRollback<bool> blockDataChangedDispatch();
+
+ Q_CORE_EXPORT static QHash<int, QMetaProperty> roleProperties(const QAbstractItemModel &model,
+ const QMetaObject &metaObject);
+ Q_CORE_EXPORT static bool connectProperty(const QModelIndex &index, QObject *item, QObject *context,
+ int role, const QMetaProperty &property);
+ Q_CORE_EXPORT static bool connectPropertyConst(const QModelIndex &index, QObject *item, QObject *context,
+ int role, const QMetaProperty &property);
+ Q_CORE_EXPORT static bool connectProperties(const QModelIndex &index, QObject *item, QObject *context,
+ const QHash<int, QMetaProperty> &properties);
+ Q_CORE_EXPORT static bool connectPropertiesConst(const QModelIndex &index, QObject *item, QObject *context,
+ const QHash<int, QMetaProperty> &properties);
};
template <typename Structure, typename Range,
@@ -1161,6 +1215,16 @@ protected:
QRangeModelDetails::is_owning_or_raw_pointer<row_type>();
static constexpr int static_column_count = QRangeModelDetails::static_size_v<row_type>;
static constexpr bool one_dimensional_range = static_column_count == 0;
+ static constexpr bool itemsAreQObjects = std::is_base_of_v<QObject, std::remove_pointer_t<
+ typename row_traits::item_type>>;
+
+ auto maybeBlockDataChangedDispatch()
+ {
+ if constexpr (itemsAreQObjects)
+ return this->blockDataChangedDispatch();
+ else
+ return false;
+ }
// A row might be a value (or range of values), or a pointer.
// row_ptr is always a pointer, and const_row_ptr is a pointer to const.
@@ -1202,6 +1266,8 @@ protected:
|| std::is_copy_constructible_v<row_type>,
"The range holding a move-only row-type must support insert(pos, start, end)");
+ using AutoConnectPolicy = typename Ancestor::AutoConnectPolicy;
+
public:
static constexpr bool isMutable()
{
@@ -1341,8 +1407,9 @@ public:
{
QMap<int, QVariant> result;
bool tried = false;
- const auto readItemData = [this, &result, &tried](const auto &value){
+ const auto readItemData = [this, &index, &result, &tried](const auto &value){
Q_UNUSED(this);
+ Q_UNUSED(index);
using value_type = q20::remove_cvref_t<decltype(value)>;
using multi_role = QRangeModelDetails::is_multi_role<value_type>;
using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
@@ -1392,7 +1459,7 @@ public:
const int role = *it;
if (role == Qt::RangeModelDataRole)
continue;
- QVariant data = readRole(role, QRangeModelDetails::pointerTo(value));
+ QVariant data = readRole(index, role, QRangeModelDetails::pointerTo(value));
if (data.isValid())
result[role] = std::move(data);
}
@@ -1471,7 +1538,7 @@ public:
else
roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
} else {
- roleData.setData(readRole(roleData.role(),
+ roleData.setData(readRole(index, roleData.role(),
QRangeModelDetails::pointerTo(value)));
}
}
@@ -1518,6 +1585,8 @@ public:
? QList<int>{} : QList<int>{role});
}
});
+ // we emit dataChanged at the end, block dispatches from auto-connected properties
+ [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
const auto writeData = [this, column = index.column(), &data, role](auto &&target) -> bool {
using value_type = q20::remove_cvref_t<decltype(target)>;
@@ -1641,6 +1710,8 @@ public:
if (success)
Q_EMIT this->dataChanged(index, index, data.keys());
});
+ // we emit dataChanged at the end, block dispatches from auto-connected properties
+ [[maybe_unused]] auto dataChangedBlocker = maybeBlockDataChangedDispatch();
bool tried = false;
auto writeItemData = [this, &tried, &data](auto &target) -> bool {
@@ -1789,6 +1860,81 @@ public:
return this->itemModel().QAbstractItemModel::roleNames();
}
+ template <typename Fn>
+ bool forEachColumn(const row_type &row, int rowIndex, const QModelIndex &parent, Fn &&fn) const
+ {
+ const auto &model = this->itemModel();
+ if constexpr (one_dimensional_range) {
+ return fn(model.index(rowIndex, 0, parent), QRangeModelDetails::pointerTo(row));
+ } else if constexpr (dynamicColumns()) {
+ int columnIndex = -1;
+ return std::all_of(row.begin(), row.end(), [&](const auto &item) {
+ return fn(model.index(rowIndex, ++columnIndex, parent),
+ QRangeModelDetails::pointerTo(item));
+ });
+ } else { // tuple-like
+ int columnIndex = -1;
+ return std::apply([fn = std::forward<Fn>(fn), &model, rowIndex, &columnIndex, parent]
+ (const auto &...item) {
+ return (fn(model.index(rowIndex, ++columnIndex, parent),
+ QRangeModelDetails::pointerTo(item)) && ...);
+ }, row);
+ }
+ }
+
+ bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
+ {
+ if (!QRangeModelDetails::isValid(row))
+ return false;
+ return forEachColumn(row, rowIndex, parent, [this](const QModelIndex &index, QObject *item) {
+ if constexpr (isMutable())
+ return Self::connectProperties(index, item, m_data.context, m_data.properties);
+ else
+ return Self::connectPropertiesConst(index, item, m_data.context, m_data.properties);
+ });
+ }
+
+ void clearConnectionInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
+ {
+ if (!QRangeModelDetails::isValid(row))
+ return;
+ forEachColumn(row, rowIndex, parent, [this](const QModelIndex &, QObject *item) {
+ m_data.connections.removeIf([item](const auto &connection) {
+ return connection.sender == item;
+ });
+ return true;
+ });
+ }
+
+ void setAutoConnectPolicy()
+ {
+ // will be 'void' if columns don't all have the same type
+ if constexpr (itemsAreQObjects) {
+ using item_type = std::remove_pointer_t<typename row_traits::item_type>;
+
+ delete m_data.context;
+ m_data.connections = {};
+ switch (this->autoConnectPolicy()) {
+ case AutoConnectPolicy::None:
+ m_data.context = nullptr;
+ break;
+ case AutoConnectPolicy::Full:
+ m_data.context = new QObject(&this->itemModel());
+ m_data.properties = QRangeModelImplBase::roleProperties(this->itemModel(),
+ item_type::staticMetaObject);
+ if (!m_data.properties.isEmpty())
+ that().autoConnectPropertiesImpl();
+ break;
+ case AutoConnectPolicy::OnRead:
+ m_data.context = new QObject(&this->itemModel());
+ break;
+ }
+ } else {
+#ifndef QT_NO_DEBUG
+ qWarning("All items in the range must be QObject subclasses");
+#endif
+ }
+ }
template <typename InsertFn>
bool doInsertColumns(int column, int count, const QModelIndex &parent, InsertFn insertFn)
@@ -1808,6 +1954,23 @@ public:
this->endInsertColumns();
+ // endInsertColumns emits columnsInserted, at which point clients might
+ // have populated the new columns with objects (if the columns aren't objects
+ // themselves).
+ if constexpr (itemsAreQObjects) {
+ if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
+ for (int r = 0; r < that().rowCount(parent); ++r) {
+ for (int c = column; c < column + count; ++c) {
+ const QModelIndex index = that().index(r, c, parent);
+ writeAt(index, [this, &index](QObject *item){
+ return Self::connectProperties(index, item,
+ m_data.context, m_data.properties);
+ });
+ }
+ }
+ }
+ }
+
return true;
}
@@ -1833,6 +1996,22 @@ public:
if (!children)
return false;
+ if constexpr (itemsAreQObjects) {
+ if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
+ for (int r = 0; r < that().rowCount(parent); ++r) {
+ for (int c = column; c < column + count; ++c) {
+ const QModelIndex index = that().index(r, c, parent);
+ writeAt(index, [this](QObject *item){
+ m_data.connections.removeIf([item](const auto &connection) {
+ return connection.sender == item;
+ });
+ return true;
+ });
+ }
+ }
+ }
+ }
+
this->beginRemoveColumns(parent, column, column + count - 1);
for (auto &child : *children) {
const auto start = QRangeModelDetails::pos(child, column);
@@ -1896,6 +2075,19 @@ public:
this->endInsertRows();
+ // endInsertRows emits rowsInserted, at which point clients might
+ // have populated the new row with objects (if the rows aren't objects
+ // themselves).
+ if constexpr (itemsAreQObjects) {
+ if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
+ const auto begin = QRangeModelDetails::pos(children, row);
+ const auto end = std::next(begin, count);
+ int rowIndex = row;
+ for (auto it = begin; it != end; ++it, ++rowIndex)
+ autoConnectPropertiesInRow(*it, rowIndex, parent);
+ }
+ }
+
return true;
}
@@ -1933,6 +2125,16 @@ public:
if (!children)
return false;
+ if constexpr (itemsAreQObjects) {
+ if (m_data.context && this->autoConnectPolicy() == AutoConnectPolicy::OnRead) {
+ const auto begin = QRangeModelDetails::pos(children, row);
+ const auto end = std::next(begin, count);
+ int rowIndex = row;
+ for (auto it = begin; it != end; ++it, ++rowIndex)
+ clearConnectionInRow(*it, rowIndex, parent);
+ }
+ }
+
this->beginRemoveRows(parent, row, row + count - 1);
[[maybe_unused]] bool callEndRemoveColumns = false;
if constexpr (dynamicColumns()) {
@@ -2041,6 +2243,8 @@ public:
using MoveRows = Override<QRangeModelImplBase::MoveRows, &Self::moveRows>;
using MultiData = Override<QRangeModelImplBase::MultiData, &Self::multiData>;
+ using SetAutoConnectPolicy = Override<QRangeModelImplBase::SetAutoConnectPolicy,
+ &Self::setAutoConnectPolicy>;
protected:
~QRangeModelImpl()
@@ -2211,23 +2415,37 @@ protected:
}
template <typename ItemType>
- QVariant readRole(int role, ItemType *gadget) const
+ QVariant readRole(const QModelIndex &index, int role, ItemType *gadget) const
{
using item_type = std::remove_pointer_t<ItemType>;
QVariant result;
QMetaProperty prop = roleProperty<item_type>(role);
- if (!prop.isValid() && role == Qt::EditRole)
+ if (!prop.isValid() && role == Qt::EditRole) {
+ role = Qt::DisplayRole;
prop = roleProperty<item_type>(Qt::DisplayRole);
+ }
- if (prop.isValid())
+ if (prop.isValid()) {
+ if constexpr (itemsAreQObjects) {
+ const typename ModelData::Connection connection = {gadget, role};
+ if (prop.hasNotifySignal() && this->autoConnectPolicy() == AutoConnectPolicy::OnRead
+ && !m_data.connections.contains(connection)) {
+ if constexpr (isMutable())
+ Self::connectProperty(index, gadget, m_data.context, role, prop);
+ else
+ Self::connectPropertyConst(index, gadget, m_data.context, role, prop);
+ m_data.connections.insert(connection);
+ }
+ }
result = readProperty(prop, gadget);
+ }
return result;
}
template <typename ItemType>
- QVariant readRole(int role, const ItemType &gadget) const
+ QVariant readRole(const QModelIndex &index, int role, const ItemType &gadget) const
{
- return readRole(role, &gadget);
+ return readRole(index, role, &gadget);
}
template <typename ItemType>
@@ -2608,6 +2826,28 @@ protected:
}
}
+ bool autoConnectPropertiesRange(const range_type &range, const QModelIndex &parent) const
+ {
+ int rowIndex = 0;
+ for (const auto &row : range) {
+ if (!this->autoConnectPropertiesInRow(row, rowIndex, parent))
+ return false;
+ Q_ASSERT(QRangeModelDetails::isValid(row));
+ const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
+ if (!autoConnectPropertiesRange(children,
+ this->itemModel().index(rowIndex, 0, parent))) {
+ return false;
+ }
+ ++rowIndex;
+ }
+ return true;
+ }
+
+ bool autoConnectPropertiesImpl() const
+ {
+ return autoConnectPropertiesRange(*this->m_data.model(), {});
+ }
+
decltype(auto) rowDataImpl(const QModelIndex &index) const
{
const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
@@ -2818,6 +3058,17 @@ protected:
void resetParentInChildren(range_type *)
{
}
+
+ bool autoConnectPropertiesImpl() const
+ {
+ bool result = true;
+ int rowIndex = 0;
+ for (const auto &row : *this->m_data.model()) {
+ result &= this->autoConnectPropertiesInRow(row, rowIndex, {});
+ ++rowIndex;
+ }
+ return result;
+ }
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index 9b219d089b5..9117c827afe 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -377,7 +377,7 @@ public:
>::value)
: QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>())
{
- char *data = static_cast<char *>(const_cast<void *>(constData()));
+ void *data = const_cast<void *>(constData());
new (data) T(il, std::forward<Args>(args)...);
}
diff --git a/src/corelib/tools/qmultimap.qdoc b/src/corelib/tools/qmultimap.qdoc
index fdf65f28876..dc705fdb2eb 100644
--- a/src/corelib/tools/qmultimap.qdoc
+++ b/src/corelib/tools/qmultimap.qdoc
@@ -457,7 +457,7 @@
Returns the number of items associated with key \a key.
- \sa contains(), QMultiMap::count()
+ \sa contains(), QMultiMap::count(const Key &key, const T &value)
*/
/*! \fn template <class Key, class T> qsizetype QMultiMap<Key, T>::count(const Key &key, const T &value) const
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
index 1e16f42626b..f4b50b07d8a 100644
--- a/src/gui/image/qicon.h
+++ b/src/gui/image/qicon.h
@@ -265,6 +265,8 @@ private:
friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QIcon &);
#endif
+ friend class QIconPrivate;
+
public:
typedef QIconPrivate * DataPtr;
inline DataPtr &data_ptr() { return d; }
diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
index 60985d538dc..29d88f8f222 100644
--- a/src/gui/image/qicon_p.h
+++ b/src/gui/image/qicon_p.h
@@ -34,6 +34,11 @@ public:
delete engine;
}
+ static QIconPrivate *get(QIcon *icon) { return icon->d; }
+ static const QIconPrivate *get(const QIcon *icon) { return icon->d; }
+
+ enum IconEngineHook { PlatformIconHook = 1000 };
+
static qreal pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize);
QIconEngine *engine;
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index 7b1e76cc78f..a95f94dea7e 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -4211,7 +4211,8 @@ QDebug operator<<(QDebug dbg, const QEvent *e)
const Qt::MouseButtons buttons = spe->buttons();
dbg << eventClassName(type) << '(';
QtDebugUtils::formatQEnum(dbg, type);
- dbg << " ts=" << spe->timestamp();
+ if (dbg.verbosity() > QDebug::DefaultVerbosity)
+ dbg << " ts=" << spe->timestamp();
if (isMouse) {
if (type != QEvent::MouseMove && type != QEvent::NonClientAreaMouseMove) {
dbg << ' ';
@@ -4338,6 +4339,8 @@ QDebug operator<<(QDebug dbg, const QEvent *e)
const QNativeGestureEvent *ne = static_cast<const QNativeGestureEvent *>(e);
dbg << "QNativeGestureEvent(";
QtDebugUtils::formatQEnum(dbg, ne->gestureType());
+ if (dbg.verbosity() > QDebug::DefaultVerbosity)
+ dbg << ", ts=" << ne->timestamp();
dbg << ", fingerCount=" << ne->fingerCount() << ", localPos=";
QtDebugUtils::formatQPoint(dbg, ne->position());
if (!qIsNull(ne->value()))
diff --git a/src/gui/painting/qcoregraphics.mm b/src/gui/painting/qcoregraphics.mm
index dfdfa8f84e7..819f3c8e5d2 100644
--- a/src/gui/painting/qcoregraphics.mm
+++ b/src/gui/painting/qcoregraphics.mm
@@ -11,6 +11,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qcolorspace.h>
+#include <QtGui/private/qicon_p.h>
#if defined(Q_OS_MACOS)
# include <AppKit/AppKit.h>
@@ -303,6 +304,26 @@ QImage qt_mac_toQImage(CGImageRef cgImage)
return image;
}
+QImage qt_mac_padToSquareImage(const QImage &image)
+{
+ if (image.width() == image.height())
+ return image;
+
+ const int size = std::max(image.width(), image.height());
+ QImage squareImage(size, size, image.format());
+ squareImage.setDevicePixelRatio(image.devicePixelRatio());
+ squareImage.fill(Qt::transparent);
+
+ QPoint pos((size - image.width()) / (2.0 * image.devicePixelRatio()),
+ (size - image.height()) / (2.0 * image.devicePixelRatio()));
+
+ QPainter painter(&squareImage);
+ painter.drawImage(pos, image);
+ painter.end();
+
+ return squareImage;
+}
+
#ifdef Q_OS_MACOS
QT_END_NAMESPACE
@@ -383,6 +404,19 @@ QT_END_NAMESPACE
return nsImage;
}
+
++ (instancetype)internalImageFromQIcon:(const QT_PREPEND_NAMESPACE(QIcon) &)icon
+{
+ if (icon.isNull())
+ return nil;
+
+ // Check if the icon is backed by an NSImage. If so, we can use that directly.
+ auto *iconPrivate = QIconPrivate::get(&icon);
+ NSImage *iconImage = nullptr;
+ iconPrivate->engine->virtual_hook(QIconPrivate::PlatformIconHook, &iconImage);
+ return iconImage;
+}
+
@end
QT_BEGIN_NAMESPACE
diff --git a/src/gui/painting/qcoregraphics_p.h b/src/gui/painting/qcoregraphics_p.h
index 7bec19ffb98..9d176516d56 100644
--- a/src/gui/painting/qcoregraphics_p.h
+++ b/src/gui/painting/qcoregraphics_p.h
@@ -66,6 +66,7 @@ QT_END_NAMESPACE
withSize:(const QT_PREPEND_NAMESPACE(QSize) &)size
withMode:(QT_PREPEND_NAMESPACE(QIcon)::Mode)mode
withState:(QT_PREPEND_NAMESPACE(QIcon)::State)state;
++ (instancetype)internalImageFromQIcon:(const QT_PREPEND_NAMESPACE(QIcon) &)icon;
@end
QT_BEGIN_NAMESPACE
#endif // __OBJC__
@@ -74,6 +75,8 @@ QT_BEGIN_NAMESPACE
Q_GUI_EXPORT CGImageRef qt_mac_toCGImage(const QImage &qImage);
Q_GUI_EXPORT QImage qt_mac_toQImage(CGImageRef image);
+Q_GUI_EXPORT QImage qt_mac_padToSquareImage(const QImage &image);
+
Q_GUI_EXPORT void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage);
Q_GUI_EXPORT void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp
index d7496845197..a736b75dab2 100644
--- a/src/gui/painting/qdrawhelper_avx2.cpp
+++ b/src/gui/painting/qdrawhelper_avx2.cpp
@@ -38,6 +38,7 @@ BYTE_MUL_AVX2(__m256i &pixelVector, __m256i alphaChannel, __m256i colorMask, __m
pixelVector = _mm256_blendv_epi8(pixelVectorAG, pixelVectorRB, colorMask);
}
+#if QT_CONFIG(raster_64bit)
inline static void Q_DECL_VECTORCALL
BYTE_MUL_RGB64_AVX2(__m256i &pixelVector, __m256i alphaChannel, __m256i colorMask, __m256i half)
{
@@ -55,6 +56,7 @@ BYTE_MUL_RGB64_AVX2(__m256i &pixelVector, __m256i alphaChannel, __m256i colorMas
pixelVectorRB = _mm256_srli_epi32(pixelVectorRB, 16);
pixelVector = _mm256_blendv_epi8(pixelVectorAG, pixelVectorRB, colorMask);
}
+#endif
// See INTERPOLATE_PIXEL_255_SSE2 for details.
inline static void Q_DECL_VECTORCALL
@@ -79,6 +81,7 @@ INTERPOLATE_PIXEL_255_AVX2(__m256i srcVector, __m256i &dstVector, __m256i alphaC
dstVector = _mm256_blendv_epi8(finalAG, finalRB, colorMask);
}
+#if QT_CONFIG(raster_64bit)
inline static void Q_DECL_VECTORCALL
INTERPOLATE_PIXEL_RGB64_AVX2(__m256i srcVector, __m256i &dstVector, __m256i alphaChannel, __m256i oneMinusAlphaChannel, __m256i colorMask, __m256i half)
{
@@ -99,6 +102,7 @@ INTERPOLATE_PIXEL_RGB64_AVX2(__m256i srcVector, __m256i &dstVector, __m256i alph
finalRB = _mm256_srli_epi32(finalRB, 16);
dstVector = _mm256_blendv_epi8(finalAG, finalRB, colorMask);
}
+#endif
// See BLEND_SOURCE_OVER_ARGB32_SSE2 for details.
inline static void Q_DECL_VECTORCALL BLEND_SOURCE_OVER_ARGB32_AVX2(quint32 *dst, const quint32 *src, const int length)
diff --git a/src/gui/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm
index 3228b97fdb0..925135e9621 100644
--- a/src/gui/platform/darwin/qappleiconengine.mm
+++ b/src/gui/platform/darwin/qappleiconengine.mm
@@ -15,6 +15,7 @@
#include <QtGui/qstylehints.h>
#include <QtGui/private/qcoregraphics_p.h>
+#include <QtGui/private/qicon_p.h>
QT_BEGIN_NAMESPACE
@@ -302,6 +303,11 @@ QAppleIconEngine::~QAppleIconEngine()
[m_image release];
}
+QIcon QAppleIconEngine::fromTheme(const QString &iconName)
+{
+ return QIcon(new QAppleIconEngine(iconName));
+}
+
QIconEngine *QAppleIconEngine::clone() const
{
return new QAppleIconEngine(m_iconName);
@@ -476,4 +482,14 @@ void QAppleIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode m
#endif
}
+void QAppleIconEngine::virtual_hook(int hookIdentifier, void *data)
+{
+ // Expose underlying NSImage so we can pass it on to AppKit
+ // directly without flattening, preserving the symbol image.
+ if (hookIdentifier == QIconPrivate::PlatformIconHook)
+ *static_cast<decltype(m_image)*>(data) = m_image;
+ else
+ QIconEngine::virtual_hook(hookIdentifier, data);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/platform/darwin/qappleiconengine_p.h b/src/gui/platform/darwin/qappleiconengine_p.h
index 7d20896517b..b71386ca63a 100644
--- a/src/gui/platform/darwin/qappleiconengine_p.h
+++ b/src/gui/platform/darwin/qappleiconengine_p.h
@@ -31,6 +31,9 @@ class Q_GUI_EXPORT QAppleIconEngine : public QIconEngine
public:
QAppleIconEngine(const QString &iconName);
~QAppleIconEngine();
+
+ static QIcon fromTheme(const QString &iconName);
+
QIconEngine *clone() const override;
QString key() const override;
QString iconName() override;
@@ -44,6 +47,8 @@ public:
static QList<QSize> availableIconSizes(double aspectRatio = 1.0);
+ void virtual_hook(int hookIdentifier, void *data) override;
+
private:
const QString m_iconName;
#if defined(Q_OS_MACOS)
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 481ffd57b5d..a511eb854cb 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -406,7 +406,9 @@ static inline VmaAllocator toVmaAllocator(QVkAllocator a)
QByteArrayList QRhiVulkanInitParams::preferredInstanceExtensions()
{
return {
- QByteArrayLiteral("VK_KHR_get_physical_device_properties2")
+ QByteArrayLiteral("VK_KHR_get_physical_device_properties2"),
+ // to silence validation when e.g. on Wayland a surface format's colorspace is VK_COLOR_SPACE_PASS_THROUGH_EXT
+ QByteArrayLiteral("VK_EXT_swapchain_colorspace")
};
}
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index 6154f4121d2..4168f818a67 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -43,6 +43,11 @@ namespace QtAndroidAccessibility
static jmethodID m_setRangeInfoMethodID = 0;
static jmethodID m_setVisibleToUserMethodID = 0;
+ static int RANGE_TYPE_INT = 0;
+ static int RANGE_TYPE_FLOAT = 0;
+ static int RANGE_TYPE_PERCENT = 0;
+ static int RANGE_TYPE_INDETERMINATE = 0;
+
static bool m_accessibilityActivated = false;
// This object is needed to schedule the execution of the code that
@@ -510,7 +515,6 @@ namespace QtAndroidAccessibility
return QStringLiteral("android.widget.RadioButton");
case QAccessible::Role::ProgressBar:
return QStringLiteral("android.widget.ProgressBar");
- // Range information need to be filled to announce percentages
case QAccessible::Role::SpinBox:
return QStringLiteral("android.widget.NumberPicker");
case QAccessible::Role::WebDocument:
@@ -715,14 +719,14 @@ namespace QtAndroidAccessibility
if (info.hasValue && m_setRangeInfoMethodID) {
int valueType = info.currentValue.typeId();
- jint rangeType = 3; // RANGE_TYPE_INDETERMINATE
+ jint rangeType = RANGE_TYPE_INDETERMINATE;
switch (valueType) {
case QMetaType::Float:
case QMetaType::Double:
- rangeType = 1; // RANGE_TYPE_FLOAT
+ rangeType = RANGE_TYPE_FLOAT;
break;
case QMetaType::Int:
- rangeType = 0; // RANGE_TYPE_INT
+ rangeType = RANGE_TYPE_INT;
break;
}
@@ -730,7 +734,7 @@ namespace QtAndroidAccessibility
float max = info.maxValue.toFloat();
float current = info.currentValue.toFloat();
if (info.role == QAccessible::ProgressBar) {
- rangeType = 2; // RANGE_TYPE_PERCENT
+ rangeType = RANGE_TYPE_PERCENT;
current = 100 * (current - min) / (max - min);
min = 0.0f;
max = 100.0f;
@@ -800,6 +804,14 @@ namespace QtAndroidAccessibility
return false; \
}
+#define CHECK_AND_INIT_STATIC_FIELD(TYPE, VAR, CLASS, FIELD_NAME) \
+ if (env.findStaticField<TYPE>(CLASS, FIELD_NAME) == nullptr) { \
+ __android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), \
+ QtAndroid::staticFieldErrorMsgFmt(), FIELD_NAME); \
+ return false; \
+ } \
+ VAR = QJniObject::getStaticField<TYPE>(CLASS, FIELD_NAME);
+
bool registerNatives(QJniEnvironment &env)
{
if (!env.registerNativeMethods("org/qtproject/qt/android/QtNativeAccessibility",
@@ -829,6 +841,18 @@ namespace QtAndroidAccessibility
m_setRangeInfoMethodID, nodeInfoClass, "setRangeInfo",
"(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V");
+ jclass rangeInfoClass =
+ env->FindClass("android/view/accessibility/AccessibilityNodeInfo$RangeInfo");
+ CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_INT, rangeInfoClass, "RANGE_TYPE_INT");
+ CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_FLOAT, rangeInfoClass, "RANGE_TYPE_FLOAT");
+ CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_PERCENT, rangeInfoClass, "RANGE_TYPE_PERCENT");
+ if (QtAndroidPrivate::androidSdkVersion() >= 36) {
+ CHECK_AND_INIT_STATIC_FIELD(int, RANGE_TYPE_INDETERMINATE, rangeInfoClass,
+ "RANGE_TYPE_INDETERMINATE");
+ } else {
+ RANGE_TYPE_INDETERMINATE = RANGE_TYPE_FLOAT;
+ }
+
return true;
}
}
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 9059fab757b..90f39f19edb 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -86,6 +86,7 @@ static AndroidBackendRegister *m_backendRegister = nullptr;
static const char m_qtTag[] = "Qt";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
+static const char m_staticFieldErrorMsg[] = "Can't find static field \"%s\"";
Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
@@ -317,6 +318,11 @@ namespace QtAndroid
return m_methodErrorMsg;
}
+ const char *staticFieldErrorMsgFmt()
+ {
+ return m_staticFieldErrorMsg;
+ }
+
const char *qtTagText()
{
return m_qtTag;
diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h
index 447fda5035a..1f359423dea 100644
--- a/src/plugins/platforms/android/androidjnimain.h
+++ b/src/plugins/platforms/android/androidjnimain.h
@@ -66,6 +66,7 @@ namespace QtAndroid
const char *classErrorMsgFmt();
const char *methodErrorMsgFmt();
+ const char *staticFieldErrorMsgFmt();
const char *qtTagText();
QString deviceName();
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 13f1a19a360..705429e5ccf 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -441,10 +441,22 @@ QPlatformKeyMapper *QCocoaIntegration::keyMapper() const
void QCocoaIntegration::setApplicationIcon(const QIcon &icon) const
{
- // Fall back to a size that looks good on the highest resolution screen available
+ if (icon.isNull()) {
+ NSApp.applicationIconImage = nil;
+ return;
+ }
+
+ // Request a size that looks good on the highest resolution screen available
// for icon engines that don't have an intrinsic size (like SVG).
- auto fallbackSize = QSizeF::fromCGSize(NSApp.dockTile.size) * qGuiApp->devicePixelRatio();
- NSApp.applicationIconImage = [NSImage imageFromQIcon:icon withSize:fallbackSize.toSize()];
+ const auto dockTitleSize = QSizeF::fromCGSize(NSApp.dockTile.size).toSize();
+ auto image = icon.pixmap(dockTitleSize, qGuiApp->devicePixelRatio()).toImage();
+
+ // The assigned image is scaled by the system to fit into the tile,
+ // but without taking aspect ratio into account, so let's pad the
+ // image up front if it's not already square.
+ image = qt_mac_padToSquareImage(image);
+
+ NSApp.applicationIconImage = [NSImage imageFromQImage:image];
}
void QCocoaIntegration::setApplicationBadge(qint64 number)
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
index ca43ab421e0..a9a79665f7d 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
@@ -373,11 +373,21 @@ NSMenuItem *QCocoaMenuItem::sync()
m_native.keyEquivalentModifierMask = NSEventModifierFlagCommand;
}
- const QIcon::Mode mode = m_enabled ? QIcon::Normal : QIcon::Disabled;
- const QIcon::State state = m_checked ? QIcon::On : QIcon::Off;
- m_native.image = [NSImage imageFromQIcon:m_icon withSize:QSize(m_iconSize, m_iconSize)
- withMode:mode
- withState:state];
+ if (auto *image = [NSImage internalImageFromQIcon:m_icon]) {
+ // The icon is backed by QAppleIconEngine, in which case we
+ // want to pass on the underlying NSImage instead of flattening
+ // to a QImage, as AppKit takes care of requesting a symbol
+ // configuration that matches the size and look of the menu,
+ // which we can't replicate otherwise. Note that this ignores
+ // any possible explicitly set icon size of the menu item.
+ m_native.image = [[image copy] autorelease];
+ } else {
+ const QIcon::Mode mode = m_enabled ? QIcon::Normal : QIcon::Disabled;
+ const QIcon::State state = m_checked ? QIcon::On : QIcon::Off;
+ m_native.image = [NSImage imageFromQIcon:m_icon withSize:QSize(m_iconSize, m_iconSize)
+ withMode:mode
+ withState:state];
+ }
m_native.state = m_checked ? NSControlStateValueOn : NSControlStateValueOff;
return m_native;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index 1505357ee40..d5c5ed8c2e2 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -85,6 +85,19 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
if (!m_statusItem)
return;
+ if (auto *image = [NSImage internalImageFromQIcon:icon]) {
+ // The icon is backed by QAppleIconEngine, in which case we
+ // want to pass on the underlying NSImage instead of flattening
+ // to a QImage. This preserves the isTemplate property of the
+ // image, and allows AppKit to size and configure the icon for
+ // the status bar. We also enable NSVariableStatusItemLength,
+ // to match the behavior of SwiftUI's MenuBarExtra.
+ m_statusItem.button.image = [[image copy] autorelease];
+ m_statusItem.button.imageScaling = NSImageScaleProportionallyDown;
+ m_statusItem.length = NSVariableStatusItemLength;
+ return;
+ }
+
// The recommended maximum title bar icon height is 18 points
// (device independent pixels). The menu height on past and
// current OS X versions is 22 points. Provide some future-proofing
@@ -206,7 +219,17 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess
auto *notification = [[NSUserNotification alloc] init];
notification.title = title.toNSString();
notification.informativeText = message.toNSString();
- notification.contentImage = [NSImage imageFromQIcon:icon];
+
+ // Request a size that looks good on the highest resolution screen available
+ // for icon engines that don't have an intrinsic size (like SVG).
+ auto image = icon.pixmap(QSize(64, 64), qGuiApp->devicePixelRatio()).toImage();
+
+ // The assigned image is scaled by the system to fit into the tile,
+ // but without taking aspect ratio into account, so let's pad the
+ // image up front if it's not already square.
+ image = qt_mac_padToSquareImage(image);
+
+ notification.contentImage = [NSImage imageFromQImage:image];
NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter;
center.delegate = m_delegate;
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index f9094a8f665..52f449db08b 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -400,9 +400,9 @@ QPixmap QCocoaTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
iconType = kGenericDocumentIcon;
break;
case ToolBarHorizontalExtensionButton:
- return QIcon::fromTheme("chevron.forward.2").pixmap(size.toSize());
+ return QAppleIconEngine::fromTheme("chevron.forward.2").pixmap(size.toSize());
case ToolBarVerticalExtensionButton:
- return QIcon::fromTheme("chevron.down.2").pixmap(size.toSize());
+ return QAppleIconEngine::fromTheme("chevron.down.2").pixmap(size.toSize());
default:
break;
}
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
index 56fda45e905..ad5c498032c 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
@@ -28,6 +28,10 @@
#include <sys/ioctl.h>
#endif
+#if defined(Q_OS_VXWORKS)
+#include <fbdev.h>
+#endif
+
#include <private/qfactoryloader_p.h>
#include <private/qcore_unix_p.h>
@@ -270,6 +274,13 @@ void QEglFSDeviceIntegration::waitForVSync(QPlatformSurface *surface) const
if (ioctl(framebuffer, FBIO_WAITFORVSYNC, &arg) == -1)
qWarning("Could not wait for vsync.");
}
+#elif defined(Q_OS_VXWORKS) && defined(FB_IOCTL_VSYNC)
+ static const bool forceSync = qEnvironmentVariableIntValue("QT_QPA_EGLFS_FORCEVSYNC");
+ if (forceSync && framebuffer != -1) {
+ int arg = 0;
+ if (ioctl(framebuffer, FB_IOCTL_VSYNC, &arg) == -1)
+ qWarning("Could not wait for vsync.");
+ }
#endif
}
diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
index 6ca6554f673..c173aa426fc 100644
--- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
+++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
@@ -28,6 +28,19 @@
docTypes = [self computeAllowedFileTypes:results];
}
+ // FIXME: Handle security scoped URLs instead of copying resource
+ bool asCopy = [&]{
+ switch (fileDialog->options()->fileMode()) {
+ case QFileDialogOptions::AnyFile:
+ case QFileDialogOptions::ExistingFile:
+ case QFileDialogOptions::ExistingFiles:
+ return true;
+ default:
+ // Folders can't be imported
+ return false;
+ }
+ }();
+
if (!docTypes.count) {
switch (fileDialog->options()->fileMode()) {
case QFileDialogOptions::AnyFile:
@@ -45,7 +58,7 @@
}
}
- if (self = [super initForOpeningContentTypes:docTypes]) {
+ if (self = [super initForOpeningContentTypes:docTypes asCopy:asCopy]) {
m_fileDialog = fileDialog;
self.modalPresentationStyle = UIModalPresentationFormSheet;
self.delegate = self;
diff --git a/src/plugins/platforms/wasm/qwasmaccessibility.cpp b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
index d25474c9b3a..56a7c466dcd 100644
--- a/src/plugins/platforms/wasm/qwasmaccessibility.cpp
+++ b/src/plugins/platforms/wasm/qwasmaccessibility.cpp
@@ -377,15 +377,23 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac
addEventListener(iface, element, "click");
} break;
+ case QAccessible::Grouping:
case QAccessible::CheckBox: {
- // QGroupBox uses both CheckBox, and a group with subwidgets
- element = document.call<emscripten::val>("createElement", std::string("input"));
- setAttribute(element, "type", "checkbox");
- setAttribute(element, "checked", iface->state().checked);
- setProperty(element, "indeterminate", iface->state().checkStateMixed);
- addEventListener(iface, element, "change");
- if (iface->childCount() > 0) {
- auto checkbox = element;
+ // Three cases:
+ // 1) role=CheckBox, childCount() == 0 -> Checkbox
+ // 2) role=CheckBox, childCount() > 0 -> GroupBox w/checkbox
+ // 3) role=Grouping -> GroupBox w/label
+
+ emscripten::val checkbox = emscripten::val::undefined();
+ if (iface->role() == QAccessible::CheckBox) {
+ checkbox = document.call<emscripten::val>("createElement", std::string("input"));
+ setAttribute(checkbox, "type", "checkbox");
+ setAttribute(checkbox, "checked", iface->state().checked);
+ setProperty(checkbox, "indeterminate", iface->state().checkStateMixed);
+ addEventListener(iface, checkbox, "change");
+ }
+
+ if (iface->childCount() > 0 || iface->role() == QAccessible::Grouping) {
auto label = document.call<emscripten::val>("createElement", std::string("span"));
const std::string id = QString::asprintf("lbid%p", iface).toStdString();
@@ -393,11 +401,16 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac
element = document.call<emscripten::val>("createElement", std::string("div"));
element.call<void>("appendChild", label);
- element.call<void>("appendChild", checkbox);
setAttribute(element, "role", "group");
setAttribute(element, "aria-labelledby", id);
- addEventListener(iface, checkbox, "focus");
+
+ if (!checkbox.isUndefined()) {
+ element.call<void>("appendChild", checkbox);
+ addEventListener(iface, checkbox, "focus");
+ }
+ } else {
+ element = checkbox;
}
} break;
@@ -800,19 +813,25 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event)
void QWasmAccessibility::handleGroupBoxUpdate(QAccessibleEvent *event)
{
QAccessibleInterface *iface = event->accessibleInterface();
- const emscripten::val parent = getHtmlElement(iface);
- const emscripten::val label = parent["children"][0];
- const emscripten::val checkbox = parent["children"][1];
+
+ emscripten::val parent = getHtmlElement(iface);
+ emscripten::val label = parent["children"][0];
+ emscripten::val checkbox = emscripten::val::undefined();
+ if (iface->role() == QAccessible::CheckBox)
+ checkbox = parent["children"][1];
switch (event->type()) {
case QAccessible::Focus:
case QAccessible::NameChanged: {
setProperty(label, "innerText", iface->text(QAccessible::Name).toStdString());
- setAttribute(checkbox, "aria-label", iface->text(QAccessible::Name).toStdString());
+ if (!checkbox.isUndefined())
+ setAttribute(checkbox, "aria-label", iface->text(QAccessible::Name).toStdString());
} break;
case QAccessible::StateChanged: {
- setAttribute(checkbox, "checked", iface->state().checked);
- setProperty(checkbox, "indeterminate", iface->state().checkStateMixed);
+ if (!checkbox.isUndefined()) {
+ setAttribute(checkbox, "checked", iface->state().checked);
+ setProperty(checkbox, "indeterminate", iface->state().checkStateMixed);
+ }
} break;
default:
qCDebug(lcQpaAccessibility) << "TODO: implement handleCheckBoxUpdate for event" << event->type();
@@ -1309,6 +1328,9 @@ void QWasmAccessibility::handleUpdateByInterfaceRole(QAccessibleEvent *event)
case QAccessible::ScrollBar:
handleScrollBarUpdate(event);
break;
+ case QAccessible::Grouping:
+ handleGroupBoxUpdate(event);
+ break;
default:
qCDebug(lcQpaAccessibility) << "TODO: implement notifyAccessibilityUpdate for role" << iface->role();
};
diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp
index c8cd7c26f61..f47682ced67 100644
--- a/src/plugins/styles/modernwindows/qwindows11style.cpp
+++ b/src/plugins/styles/modernwindows/qwindows11style.cpp
@@ -993,7 +993,8 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption
const bool isMouseOver = state & State_MouseOver;
const bool hasFocus = state & State_HasFocus;
- if (isMouseOver && !hasFocus && !highContrastTheme)
+ const bool isEnabled = state & State_Enabled;
+ if (isMouseOver && isEnabled && hasFocus && !highContrastTheme)
drawRoundedRect(painter, frameRect, Qt::NoPen, winUI3Color(subtleHighlightColor));
}
break;
@@ -1399,6 +1400,13 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
const qreal offset = (int(rect.height()) % 2 == 0) ? 0.5f : 0.0f;
if (isIndeterminate) {
+#if QT_CONFIG(animation)
+ auto anim = d->animation(option->styleObject);
+ if (!anim) {
+ auto anim = new QStyleAnimation(option->styleObject);
+ anim->setFrameRate(QStyleAnimation::SixtyFps);
+ d->startAnimation(anim);
+ }
constexpr auto loopDurationMSec = 4000;
const auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now());
@@ -1406,14 +1414,20 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
const auto handleCenter = (elapsed % loopDurationMSec) / float(loopDurationMSec);
const auto isLongHandle = (elapsed / loopDurationMSec) % 2 == 0;
const auto lengthFactor = (isLongHandle ? 33.0f : 25.0f) / 100.0f;
+#else
+ constexpr auto handleCenter = 0.5f;
+ constexpr auto lengthFactor = 1;
+#endif
const auto begin = qMax(handleCenter * (1 + lengthFactor) - lengthFactor, 0.0f);
const auto end = qMin(handleCenter * (1 + lengthFactor), 1.0f);
const auto barBegin = begin * rect.width();
const auto barEnd = end * rect.width();
rect = QRectF(QPointF(rect.left() + barBegin, rect.top()),
QPointF(rect.left() + barEnd, rect.bottom()));
- const_cast<QWidget *>(widget)->update();
} else {
+#if QT_CONFIG(animation)
+ d->stopAnimation(option->styleObject);
+#endif
const auto fillPercentage = (float(baropt->progress - baropt->minimum))
/ (float(baropt->maximum - baropt->minimum));
rect.setWidth(rect.width() * fillPercentage);
@@ -2715,7 +2729,7 @@ void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const Q
: winUI3Color(frameColorLight);
drawRoundedRect(p, rect, frameCol, Qt::NoBrush);
- if (!isEditable)
+ if (!isEditable || StyleOptionHelper::isDisabled(o))
return;
QPainterStateGuard psg(p);
diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp
index 7e3dfbd68db..cc3a230f9b4 100644
--- a/src/widgets/accessible/itemviews.cpp
+++ b/src/widgets/accessible/itemviews.cpp
@@ -44,7 +44,7 @@ int QAccessibleTable::logicalIndex(const QModelIndex &index) const
return -1;
#if QT_CONFIG(listview)
- if (m_role == QAccessible::List) {
+ if (role() == QAccessible::List) {
if (index.column() != qobject_cast<const QListView*>(view())->modelColumn())
return -1;
else
@@ -59,35 +59,10 @@ int QAccessibleTable::logicalIndex(const QModelIndex &index) const
}
}
-QAccessibleTable::QAccessibleTable(QWidget *w)
- : QAccessibleWidgetV2(w)
+QAccessibleTable::QAccessibleTable(QWidget *w, QAccessible::Role role)
+ : QAccessibleWidgetV2(w, role)
{
Q_ASSERT(view());
-
-#if QT_CONFIG(tableview)
- if (qobject_cast<const QTableView*>(view())) {
- m_role = QAccessible::Table;
- } else
-#endif
-#if QT_CONFIG(treeview)
- if (qobject_cast<const QTreeView*>(view())) {
- m_role = QAccessible::Tree;
- } else
-#endif
-#if QT_CONFIG(listview)
- if (qobject_cast<const QListView*>(view())) {
- m_role = QAccessible::List;
- } else
-#endif
- {
- // is this our best guess?
- m_role = QAccessible::Table;
- }
-}
-
-bool QAccessibleTable::isValid() const
-{
- return view() && !qt_widget_private(view())->data.in_destructor;
}
QAccessibleTable::~QAccessibleTable()
@@ -161,7 +136,7 @@ int QAccessibleTable::columnCount() const
if (!theModel)
return 0;
const int modelColumnCount = theModel->columnCount(theView->rootIndex());
- return m_role == QAccessible::List ? qMin(1, modelColumnCount) : modelColumnCount;
+ return role() == QAccessible::List ? qMin(1, modelColumnCount) : modelColumnCount;
}
int QAccessibleTable::rowCount() const
@@ -491,36 +466,6 @@ bool QAccessibleTable::clear()
}
-QAccessible::Role QAccessibleTable::role() const
-{
- return m_role;
-}
-
-QAccessible::State QAccessibleTable::state() const
-{
- QAccessible::State state;
- const auto *w = view();
-
- if (w->testAttribute(Qt::WA_WState_Visible) == false)
- state.invisible = true;
- if (w->focusPolicy() != Qt::NoFocus)
- state.focusable = true;
- if (w->hasFocus())
- state.focused = true;
- if (!w->isEnabled())
- state.disabled = true;
- if (w->isWindow()) {
- if (w->windowFlags() & Qt::WindowSystemMenuHint)
- state.movable = true;
- if (w->minimumSize() != w->maximumSize())
- state.sizeable = true;
- if (w->isActiveWindow())
- state.active = true;
- }
-
- return state;
-}
-
QAccessibleInterface *QAccessibleTable::childAt(int x, int y) const
{
QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0));
@@ -589,14 +534,6 @@ int QAccessibleTable::indexOfChild(const QAccessibleInterface *iface) const
return -1;
}
-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()) {
diff --git a/src/widgets/accessible/itemviews_p.h b/src/widgets/accessible/itemviews_p.h
index e74be151115..ceb6b913a8d 100644
--- a/src/widgets/accessible/itemviews_p.h
+++ b/src/widgets/accessible/itemviews_p.h
@@ -36,12 +36,7 @@ class QAccessibleTable : public QAccessibleTableInterface,
public QAccessibleWidgetV2
{
public:
- explicit QAccessibleTable(QWidget *w);
- bool isValid() const override;
-
- QAccessible::Role role() const override;
- QAccessible::State state() const override;
- QRect rect() const override;
+ explicit QAccessibleTable(QWidget *w, QAccessible::Role role = QAccessible::Table);
QAccessibleInterface *childAt(int x, int y) const override;
QAccessibleInterface *focusChild() const override;
@@ -91,7 +86,7 @@ public:
protected:
inline QAccessible::Role cellRole() const {
- switch (m_role) {
+ switch (role()) {
case QAccessible::List:
return QAccessible::ListItem;
case QAccessible::Table:
@@ -116,7 +111,6 @@ protected:
private:
// the child index for a model index
inline int logicalIndex(const QModelIndex &index) const;
- QAccessible::Role m_role;
};
#if QT_CONFIG(treeview)
@@ -124,7 +118,7 @@ class QAccessibleTree :public QAccessibleTable
{
public:
explicit QAccessibleTree(QWidget *w)
- : QAccessibleTable(w)
+ : QAccessibleTable(w, QAccessible::Tree)
{}
@@ -153,7 +147,7 @@ class QAccessibleList :public QAccessibleTable
{
public:
explicit QAccessibleList(QWidget *w)
- : QAccessibleTable(w)
+ : QAccessibleTable(w, QAccessible::List)
{}
QAccessibleInterface *child(int index) const override;
diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp
index b2cfb27e814..d45515e0ecc 100644
--- a/src/widgets/styles/qstylesheetstyle.cpp
+++ b/src/widgets/styles/qstylesheetstyle.cpp
@@ -4658,7 +4658,14 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op
#if QT_CONFIG(scrollarea)
if (const QAbstractScrollArea *sa = qobject_cast<const QAbstractScrollArea *>(w)) {
const QAbstractScrollAreaPrivate *sap = sa->d_func();
- rule.drawBackground(p, opt->rect, sap->contentsOffset());
+ bool callBaseClass = true;
+ if (rule.hasBackground()) {
+ if (rule.baseStyleCanDraw())
+ baseStyle()->drawPrimitive(pe, opt, p, w);
+ else
+ rule.drawBackground(p, opt->rect, sap->contentsOffset());
+ callBaseClass = false;
+ }
if (rule.hasBorder()) {
QRect brect = rule.borderRect(opt->rect);
if (styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, opt, w)) {
@@ -4667,7 +4674,10 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op
brect = QStyle::visualRect(opt->direction, brect, r);
}
rule.drawBorder(p, brect);
+ callBaseClass = false;
}
+ if (!callBaseClass)
+ return;
break;
}
#endif
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index 92ff14dd44f..26d9bcc9f24 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -2543,14 +2543,16 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po
q->create();
if (auto waylandWindow = dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(q->windowHandle()->handle())) {
if (causedButton) {
+ const QRect controlGeometry(causedButton->mapTo(causedButton->window(), QPoint(0, 0)), causedButton->size());
+ waylandWindow->setParentControlGeometry(controlGeometry);
waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
- waylandWindow->setParentControlGeometry(causedButton->geometry());
} else if (caused) {
- waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
waylandWindow->setParentControlGeometry(caused->d_func()->actionRect(caused->d_func()->currentAction));
+ waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::SubMenu);
} else if (auto menubar = qobject_cast<QMenuBar*>(causedPopup.widget)) {
+ QPoint menuBarWindowPosition = menubar->mapTo(menubar->window(), QPoint(0, 0));
+ waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action).translated(menuBarWindowPosition));
waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::Menu);
- waylandWindow->setParentControlGeometry(menubar->actionGeometry(causedPopup.action));
}
}
#endif