aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/imports/tooling/Member.qml1
-rw-r--r--src/labs/models/qqmlabstractcolumnmodel.cpp122
-rw-r--r--src/labs/models/qqmlabstractcolumnmodel_p.h9
-rw-r--r--src/labs/models/qqmltablemodel.cpp141
-rw-r--r--src/labs/models/qqmltablemodel_p.h7
-rw-r--r--src/labs/models/qqmltreemodel.cpp139
-rw-r--r--src/labs/models/qqmltreemodel_p.h9
-rw-r--r--src/qml/doc/snippets/qmltc/CMakeLists.txt1
-rw-r--r--src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc3
-rw-r--r--src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp26
-rw-r--r--src/qml/jsruntime/qv4engine_p.h1
-rw-r--r--src/qml/jsruntime/qv4referenceobject_p.h104
-rw-r--r--src/qml/jsruntime/qv4variantassociationobject.cpp507
-rw-r--r--src/qml/jsruntime/qv4variantassociationobject_p.h123
-rw-r--r--src/qml/qml/qqml.cpp38
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp12
-rw-r--r--src/qmlcompiler/qcoloroutput.cpp22
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp1
-rw-r--r--src/qmlcompiler/qqmljslinter.cpp17
-rw-r--r--src/qmlcompiler/qqmljslogger.cpp14
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h4
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp25
-rw-r--r--src/qmlformat/qqmlformatoptions.cpp32
-rw-r--r--src/qmlformat/qqmlformatoptions_p.h7
-rw-r--r--src/qmlls/documentSymbolSupport/qqmldocumentsymbolsupport.cpp2
-rw-r--r--src/qmlls/qmllsmain.cpp7
-rw-r--r--src/qmlls/qqmlcodemodel.cpp7
-rw-r--r--src/qmlls/qqmlcodemodel_p.h4
-rw-r--r--src/qmlls/qqmlcodemodelmanager.cpp7
-rw-r--r--src/qmlls/qqmlcodemodelmanager_p.h3
-rw-r--r--src/qmlls/qqmllshelputils.cpp1
-rw-r--r--src/qmlls/qqmllsutils.cpp157
-rw-r--r--src/qmlls/qqmllsutils_p.h3
-rw-r--r--src/qmlls/qqmlsemantictokens.cpp1
-rw-r--r--src/qmlmodels/sfpm/filters/qqmlfilterbase_p.h1
-rw-r--r--src/qmlmodels/sfpm/filters/qqmlfiltercompositor_p.h4
-rw-r--r--src/qmlmodels/sfpm/filters/qqmlfunctionfilter_p.h1
-rw-r--r--src/qmlmodels/sfpm/filters/qqmlrolefilter_p.h1
-rw-r--r--src/qmlmodels/sfpm/filters/qqmlvaluefilter_p.h1
-rw-r--r--src/qmlmodels/sfpm/sorters/qqmlfunctionsorter_p.h1
-rw-r--r--src/qmlmodels/sfpm/sorters/qqmlrolesorter_p.h1
-rw-r--r--src/qmlmodels/sfpm/sorters/qqmlsorterbase_p.h3
-rw-r--r--src/qmlmodels/sfpm/sorters/qqmlsortercompositor_p.h4
-rw-r--r--src/qmlmodels/sfpm/sorters/qqmlstringsorter_p.h1
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings.cpp72
-rw-r--r--src/qmltoolingsettings/qqmltoolingsettings_p.h23
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor.cpp3
-rw-r--r--src/qmltyperegistrar/qmetatypesjsonprocessor_p.h3
-rw-r--r--src/qmltyperegistrar/qqmltypescreator.cpp10
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp9
-rw-r--r--src/quick/accessible/qaccessiblequickitem_p.h1
-rw-r--r--src/quick/doc/src/cmake/qt_target_qml_from_svg.qdoc65
-rw-r--r--src/quick/items/qquickitem.cpp13
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp16
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h1
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp8
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp21
-rw-r--r--src/quick/util/qquickdeliveryagent_p_p.h1
-rw-r--r--src/quickcontrols/doc/src/includes/varying-delegate-heights-section.qdocinc8
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp109
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h8
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp68
-rw-r--r--src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h6
-rw-r--r--src/quickcontrols/windows/CMakeLists.txt7
-rw-r--r--src/quickcontrols/windows/SearchField.qml109
-rw-r--r--src/quickcontrols/windows/images/close_big.pngbin0 -> 205 bytes
-rw-r--r--src/quickcontrols/windows/images/close_big@2x.pngbin0 -> 342 bytes
-rw-r--r--src/quickcontrols/windows/images/close_big@3x.pngbin0 -> 452 bytes
-rw-r--r--src/quickcontrols/windows/images/search-magnifier.pngbin0 -> 332 bytes
-rw-r--r--src/quickcontrols/windows/images/search-magnifier@2x.pngbin0 -> 588 bytes
-rw-r--r--src/quickcontrols/windows/images/search-magnifier@3x.pngbin0 -> 829 bytes
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml14
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/SideBar.qml19
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml10
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/SideBar.qml5
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml12
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Material/SideBar.qml8
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml10
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/+Universal/SideBar.qml8
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml10
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qml/SideBar.qml9
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp20
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp15
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquicksidebar.cpp103
-rw-r--r--src/quickdialogs/quickdialogsquickimpl/qquicksidebar_p_p.h10
-rw-r--r--src/quicklayouts/qquickflexboxlayout.cpp7
-rw-r--r--src/quicklayouts/qquickflexboxlayout_p.h2
-rw-r--r--src/quicknativestyle/qstyle/qquickcommonstyle.cpp52
-rw-r--r--src/quicknativestyle/qstyle/qquickstyle.h1
-rw-r--r--src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp26
-rw-r--r--src/quicktemplates/qquickscrollbar.cpp2
-rw-r--r--src/quicktemplates/qquickscrollindicator.cpp2
-rw-r--r--src/quicktemplates/qquickscrollview.cpp2
-rw-r--r--src/quickvectorimage/generator/qquickanimatedproperty_p.h11
-rw-r--r--src/quickvectorimage/generator/qquickqmlgenerator.cpp67
96 files changed, 1626 insertions, 937 deletions
diff --git a/src/imports/tooling/Member.qml b/src/imports/tooling/Member.qml
index 9df4fa76a2..872083e099 100644
--- a/src/imports/tooling/Member.qml
+++ b/src/imports/tooling/Member.qml
@@ -5,4 +5,5 @@ import QML
QtObject {
required property string name
+ property int lineNumber: 0
}
diff --git a/src/labs/models/qqmlabstractcolumnmodel.cpp b/src/labs/models/qqmlabstractcolumnmodel.cpp
index ddec8e9b3d..87cce497e2 100644
--- a/src/labs/models/qqmlabstractcolumnmodel.cpp
+++ b/src/labs/models/qqmlabstractcolumnmodel.cpp
@@ -77,6 +77,128 @@ void QQmlAbstractColumnModel::columns_removeLast(QQmlListProperty<QQmlTableModel
model->mColumns.removeLast();
}
+QVariant QQmlAbstractColumnModel::data(const QModelIndex &index, const QString &role) const
+{
+ const int iRole = mRoleNames.key(role.toUtf8(), -1);
+ if (iRole >= 0)
+ return data(index, iRole);
+ return {};
+}
+
+QVariant QQmlAbstractColumnModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ qmlWarning(this) << "data(): invalid QModelIndex";
+ return {};
+ }
+
+ const int row = index.row();
+ if (row < 0 || row >= rowCount(parent(index))) {
+ qmlWarning(this) << "data(): invalid row specified in QModelIndex";
+ return {};
+ }
+
+ const int column = index.column();
+ if (column < 0 || column >= columnCount(parent(index))) {
+ qmlWarning(this) << "data(): invalid column specified in QModelIndex";
+ return {};
+ }
+
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(column);
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
+ if (!columnMetadata.roles.contains(roleName)) {
+ qmlWarning(this) << "data(): no role named " << roleName
+ << " at column index " << column << ". The available roles for that column are: "
+ << columnMetadata.roles.keys();
+ return {};
+ }
+
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ if (roleData.columnRole == ColumnRole::StringRole) {
+ // We know the data structure, so we can get the data for the user.
+ return dataPrivate(index, roleName);
+ }
+
+ // We don't know the data structure, so the user has to modify their data themselves.
+ // First, find the getter for this column and role.
+ QJSValue getter = mColumns.at(column)->getterAtRole(roleName);
+
+ // Then, call it and return what it returned.
+ const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index);
+ return getter.call(args).toVariant();
+}
+
+bool QQmlAbstractColumnModel::setData(const QModelIndex &index, const QString &role, const QVariant &value)
+{
+ const int intRole = mRoleNames.key(role.toUtf8(), -1);
+ if (intRole >= 0)
+ return setData(index, value, intRole);
+ return false;
+}
+
+bool QQmlAbstractColumnModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_ASSERT(index.isValid());
+
+ const int row = index.row();
+ if (row < 0 || row >= rowCount(parent(index)))
+ return false;
+
+ const int column = index.column();
+ if (column < 0 || column >= columnCount(parent(index)))
+ return false;
+
+ const QString roleName = QString::fromUtf8(mRoleNames.value(role));
+
+ qCDebug(lcColumnModel).nospace() << "setData() called with index "
+ << index << ", value " << value << " and role " << roleName;
+
+ // Verify that the role exists for this column.
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ if (!columnMetadata.roles.contains(roleName)) {
+ qmlWarning(this) << "setData(): no role named \"" << roleName
+ << "\" at column index " << column << ". The available roles for that column are: "
+ << columnMetadata.roles.keys();
+ return false;
+ }
+
+ // Verify that the type of the value is what we expect.
+ // If the value set is not of the expected type, we can try to convert it automatically.
+ const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
+ QVariant effectiveValue = value;
+ if (value.userType() != roleData.type) {
+ if (!value.canConvert(QMetaType(roleData.type))) {
+ qmlWarning(this).nospace() << "setData(): the value " << value
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " cannot be converted to " << roleData.typeName;
+ return false;
+ }
+
+ if (!effectiveValue.convert(QMetaType(roleData.type))) {
+ qmlWarning(this).nospace() << "setData(): failed converting value " << value
+ << " set at row " << row << " column " << column << " with role " << roleName
+ << " to " << roleData.typeName;
+ return false;
+ }
+ }
+
+ if (roleData.columnRole == ColumnRole::StringRole) {
+ // We know the data structure, so we can set it for the user.
+ setDataPrivate(index, roleData.name, value);
+ } else {
+ qmlWarning(this).nospace() << "setData(): manipulation of complex row "
+ << "structures is not supported";
+ return false;
+ }
+
+ QVector<int> rolesChanged;
+ rolesChanged.append(role);
+ emit dataChanged(index, index, rolesChanged);
+ emit rowsChanged();
+
+ return true;
+}
+
QHash<int, QByteArray> QQmlAbstractColumnModel::roleNames() const
{
return mRoleNames;
diff --git a/src/labs/models/qqmlabstractcolumnmodel_p.h b/src/labs/models/qqmlabstractcolumnmodel_p.h
index e693cd005e..8f367c98e4 100644
--- a/src/labs/models/qqmlabstractcolumnmodel_p.h
+++ b/src/labs/models/qqmlabstractcolumnmodel_p.h
@@ -46,11 +46,17 @@ public:
static void columns_replace(QQmlListProperty<QQmlTableModelColumn> *property, qsizetype index, QQmlTableModelColumn *value);
static void columns_removeLast(QQmlListProperty<QQmlTableModelColumn> *property);
+ Q_INVOKABLE QVariant data(const QModelIndex &index, const QString &role) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ Q_INVOKABLE bool setData(const QModelIndex &index, const QString &role, const QVariant &value);
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
+
QHash<int, QByteArray> roleNames() const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
Q_SIGNALS:
void columnCountChanged();
+ void rowsChanged();
protected:
void classBegin() override;
@@ -59,6 +65,9 @@ protected:
virtual QVariant firstRow() const = 0;
virtual void setInitialRows() = 0;
+ virtual QVariant dataPrivate(const QModelIndex &index, const QString &roleName) const = 0;
+ virtual void setDataPrivate(const QModelIndex &index, const QString &roleName, QVariant value) = 0;
+
enum class ColumnRole : quint8
{
StringRole,
diff --git a/src/labs/models/qqmltablemodel.cpp b/src/labs/models/qqmltablemodel.cpp
index b18f9bc33f..00e514c114 100644
--- a/src/labs/models/qqmltablemodel.cpp
+++ b/src/labs/models/qqmltablemodel.cpp
@@ -217,6 +217,22 @@ void QQmlTableModel::setRowsPrivate(const QVariantList &rowsAsVariantList)
emit rowCountChanged();
}
+QVariant QQmlTableModel::dataPrivate(const QModelIndex &index, const QString &roleName) const
+{
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ const QString propertyName = columnMetadata.roles.value(roleName).name;
+ const QVariantMap rowData = mRows.at(index.row()).toMap();
+ return rowData.value(propertyName);
+}
+
+void QQmlTableModel::setDataPrivate(const QModelIndex &index, const QString &roleName, QVariant value)
+{
+ int row = index.row();
+ QVariantMap modifiedRow = mRows.at(row).toMap();
+ modifiedRow[roleName] = value;
+ mRows[row] = modifiedRow;
+}
+
// TODO: Turn this into a snippet that compiles in CI
/*!
\qmlmethod TableModel::appendRow(object row)
@@ -601,58 +617,6 @@ int QQmlTableModel::columnCount(const QModelIndex &parent) const
\sa index(), setData()
*/
-QVariant QQmlTableModel::data(const QModelIndex &index, const QString &role) const
-{
- const int iRole = mRoleNames.key(role.toUtf8(), -1);
- if (iRole >= 0)
- return data(index, iRole);
- return {};
-}
-
-QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid()) {
- qmlWarning(this) << "data(): invalid QModelIndex";
- return {};
- }
-
- const int row = index.row();
- if (row < 0 || row >= rowCount()) {
- qmlWarning(this) << "data(): invalid row specified in QModelIndex";
- return {};
- }
-
- const int column = index.column();
- if (column < 0 || column >= columnCount()) {
- qmlWarning(this) << "data(): invalid column specified in QModelIndex";
- return {};
- }
-
- const ColumnMetadata columnMetadata = mColumnMetadata.at(column);
- const QString roleName = QString::fromUtf8(mRoleNames.value(role));
- if (!columnMetadata.roles.contains(roleName)) {
- qmlWarning(this) << "data(): no role named " << roleName
- << " at column index " << column << ". The available roles for that column are: "
- << columnMetadata.roles.keys();
- return {};
- }
-
- const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
- if (roleData.columnRole == ColumnRole::StringRole) {
- // We know the data structure, so we can get the data for the user.
- const QString propertyName = columnMetadata.roles.value(roleName).name;
- const QVariantMap rowData = mRows.at(row).toMap();
- return rowData.value(propertyName);
- }
-
- // We don't know the data structure, so the user has to modify their data themselves.
- // First, find the getter for this column and role.
- QJSValue getter = mColumns.at(column)->getterAtRole(roleName);
-
- // Then, call it and return what it returned.
- const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index);
- return getter.call(args).toVariant();
-}
/*!
\qmlmethod bool TableModel::setData(QModelIndex index, string role, variant value)
@@ -662,79 +626,6 @@ QVariant QQmlTableModel::data(const QModelIndex &index, int role) const
\sa data(), index()
*/
-bool QQmlTableModel::setData(const QModelIndex &index, const QString &role, const QVariant &value)
-{
- const int intRole = mRoleNames.key(role.toUtf8(), -1);
- if (intRole >= 0)
- return setData(index, value, intRole);
- return false;
-}
-
-bool QQmlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- Q_ASSERT(index.isValid());
-
- const int row = index.row();
- if (row < 0 || row >= rowCount())
- return false;
-
- const int column = index.column();
- if (column < 0 || column >= columnCount())
- return false;
-
- const QString roleName = QString::fromUtf8(mRoleNames.value(role));
-
- qCDebug(lcTableModel).nospace() << "setData() called with index "
- << index << ", value " << value << " and role " << roleName;
-
- // Verify that the role exists for this column.
- const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
- if (!columnMetadata.roles.contains(roleName)) {
- qmlWarning(this) << "setData(): no role named \"" << roleName
- << "\" at column index " << column << ". The available roles for that column are: "
- << columnMetadata.roles.keys();
- return false;
- }
-
- // Verify that the type of the value is what we expect.
- // If the value set is not of the expected type, we can try to convert it automatically.
- const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
- QVariant effectiveValue = value;
- if (value.userType() != roleData.type) {
- if (!value.canConvert(QMetaType(roleData.type))) {
- qmlWarning(this).nospace() << "setData(): the value " << value
- << " set at row " << row << " column " << column << " with role " << roleName
- << " cannot be converted to " << roleData.typeName;
- return false;
- }
-
- if (!effectiveValue.convert(QMetaType(roleData.type))) {
- qmlWarning(this).nospace() << "setData(): failed converting value " << value
- << " set at row " << row << " column " << column << " with role " << roleName
- << " to " << roleData.typeName;
- return false;
- }
- }
-
- if (roleData.columnRole == ColumnRole::StringRole) {
- // We know the data structure, so we can set it for the user.
- QVariantMap modifiedRow = mRows.at(row).toMap();
- modifiedRow[roleData.name] = value;
-
- mRows[row] = modifiedRow;
- } else {
- qmlWarning(this).nospace() << "setData(): manipulation of complex row "
- << "structures is not supported";
- return false;
- }
-
- QVector<int> rolesChanged;
- rolesChanged.append(role);
- emit dataChanged(index, index, rolesChanged);
- emit rowsChanged();
-
- return true;
-}
bool QQmlTableModel::validateRowType(QLatin1StringView functionName, const QVariant &row) const
{
diff --git a/src/labs/models/qqmltablemodel_p.h b/src/labs/models/qqmltablemodel_p.h
index 0e10cf18e2..0aae80cb36 100644
--- a/src/labs/models/qqmltablemodel_p.h
+++ b/src/labs/models/qqmltablemodel_p.h
@@ -58,15 +58,10 @@ public:
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
- Q_INVOKABLE QVariant data(const QModelIndex &index, const QString &role) const;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- Q_INVOKABLE bool setData(const QModelIndex &index, const QString &role, const QVariant &value);
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
QModelIndex parent(const QModelIndex &index) const override;
Q_SIGNALS:
void rowCountChanged();
- void rowsChanged();
protected:
QVariant firstRow() const override;
@@ -81,6 +76,8 @@ private:
};
void setRowsPrivate(const QVariantList &rowsAsVariantList);
+ QVariant dataPrivate(const QModelIndex &index, const QString &roleName) const override;
+ void setDataPrivate(const QModelIndex &index, const QString &roleName, QVariant value) override;
bool validateRowType(QLatin1StringView functionName, const QVariant &row) const;
bool validateNewRow(QLatin1StringView functionName, const QVariant &row,
diff --git a/src/labs/models/qqmltreemodel.cpp b/src/labs/models/qqmltreemodel.cpp
index e5c80fedeb..0e9be6d616 100644
--- a/src/labs/models/qqmltreemodel.cpp
+++ b/src/labs/models/qqmltreemodel.cpp
@@ -13,8 +13,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_STATIC_LOGGING_CATEGORY(lcTreeModel, "qt.qml.treemodel")
-
static const QString ROWS_PROPERTY_NAME = u"rows"_s;
/*!
@@ -141,6 +139,20 @@ void QQmlTreeModel::setRowsPrivate(const QVariantList &rowsAsVariantList)
emit rowsChanged();
}
+QVariant QQmlTreeModel::dataPrivate(const QModelIndex &index, const QString &roleName) const
+{
+ const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
+ const QString propertyName = columnMetadata.roles.value(roleName).name;
+ const auto *thisRow = static_cast<const QQmlTreeRow *>(index.internalPointer());
+ return thisRow->data(propertyName);
+}
+
+void QQmlTreeModel::setDataPrivate(const QModelIndex &index, const QString &roleName, QVariant value)
+{
+ auto *row = static_cast<QQmlTreeRow *>(index.internalPointer());
+ row->setField(roleName, value);
+}
+
// TODO: Turn this into a snippet that compiles in CI
/*!
\qmlmethod TreeModel::appendRow(QModelIndex parent, object treeRow)
@@ -500,58 +512,6 @@ int QQmlTreeModel::columnCount(const QModelIndex &parent) const
\sa index(), setData()
*/
-QVariant QQmlTreeModel::data(const QModelIndex &index, const QString &role) const
-{
- const int iRole = mRoleNames.key(role.toUtf8(), -1);
- if (iRole >= 0)
- return data(index, iRole);
- return {};
-}
-
-QVariant QQmlTreeModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid()) {
- qmlWarning(this) << "data(): invalid QModelIndex";
- return {};
- }
-
- const int row = index.row();
- if (row < 0 || row >= rowCount(parent(index))) {
- qmlWarning(this) << "data(): invalid row specified in QModelIndex";
- return {};
- }
-
- const int column = index.column();
- if (column < 0 || column >= columnCount(parent(index))) {
- qmlWarning(this) << "data(): invalid column specified in QModelIndex";
- return {};
- }
-
- const ColumnMetadata columnMetadata = mColumnMetadata.at(column);
- const QString roleName = QString::fromUtf8(mRoleNames.value(role));
- if (!columnMetadata.roles.contains(roleName)) {
- qmlWarning(this) << "data(): no role named " << roleName
- << " at column index " << column << ". The available roles for that column are: "
- << columnMetadata.roles.keys();
- return {};
- }
-
- const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
- if (roleData.columnRole == ColumnRole::StringRole) {
- // We know the data structure, so we can get the data for the user.
- const QString propertyName = columnMetadata.roles.value(roleName).name;
- const auto *thisRow = static_cast<const QQmlTreeRow *>(index.internalPointer());
- return thisRow->data(propertyName);
- }
-
- // We don't know the data structure, so the user has to modify their data themselves.
- // First, find the getter for this column and role.
- QJSValue getter = mColumns.at(column)->getterAtRole(roleName);
-
- // Then, call it and return what it returned.
- const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index);
- return getter.call(args).toVariant();
-}
/*!
\qmlmethod bool TreeModel::setData(QModelIndex index, string role, variant value)
@@ -561,77 +521,6 @@ QVariant QQmlTreeModel::data(const QModelIndex &index, int role) const
\sa data(), index()
*/
-bool QQmlTreeModel::setData(const QModelIndex &index, const QString &role, const QVariant &value)
-{
- const int intRole = mRoleNames.key(role.toUtf8(), -1);
- if (intRole >= 0)
- return setData(index, value, intRole);
- return false;
-}
-
-bool QQmlTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- Q_ASSERT(index.isValid());
-
- const int row = index.row();
- if (row < 0 || row >= rowCount(parent(index)))
- return false;
-
- const int column = index.column();
- if (column < 0 || column >= columnCount(parent(index)))
- return false;
-
- const QString roleName = QString::fromUtf8(mRoleNames.value(role));
-
- qCDebug(lcTreeModel).nospace() << "setData() called with index "
- << index << ", value " << value << " and role " << roleName;
-
- // Verify that the role exists for this column.
- const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
- if (!columnMetadata.roles.contains(roleName)) {
- qmlWarning(this) << "setData(): no role named \"" << roleName
- << "\" at column index " << column << ". The available roles for that column are: "
- << columnMetadata.roles.keys();
- return false;
- }
-
- // Verify that the type of the value is what we expect.
- // If the value set is not of the expected type, we can try to convert it automatically.
- const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName);
- QVariant effectiveValue = value;
- if (value.userType() != roleData.type) {
- if (!value.canConvert(QMetaType(roleData.type))) {
- qmlWarning(this).nospace() << "setData(): the value " << value
- << " set at row " << row << " column " << column << " with role " << roleName
- << " cannot be converted to " << roleData.typeName;
- return false;
- }
-
- if (!effectiveValue.convert(QMetaType(roleData.type))) {
- qmlWarning(this).nospace() << "setData(): failed converting value " << value
- << " set at row " << row << " column " << column << " with role " << roleName
- << " to " << roleData.typeName;
- return false;
- }
- }
-
- if (roleData.columnRole == ColumnRole::StringRole) {
- // We know the data structure, so we can set it for the user.
- auto *row = static_cast<QQmlTreeRow *>(index.internalPointer());
- row->setField(roleData.name, value);
- } else {
- qmlWarning(this).nospace() << "setData(): manipulation of complex row "
- << "structures is not supported";
- return false;
- }
-
- QVector<int> rolesChanged;
- rolesChanged.append(role);
- emit dataChanged(index, index, rolesChanged);
- emit rowsChanged();
-
- return true;
-}
bool QQmlTreeModel::validateRowType(QLatin1StringView functionName, const QVariant &row) const
{
diff --git a/src/labs/models/qqmltreemodel_p.h b/src/labs/models/qqmltreemodel_p.h
index 1401f8eb4c..9172ac0850 100644
--- a/src/labs/models/qqmltreemodel_p.h
+++ b/src/labs/models/qqmltreemodel_p.h
@@ -60,15 +60,8 @@ public:
QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override;
int rowCount(const QModelIndex &parent = {}) const override;
int columnCount(const QModelIndex &parent = {}) const override;
- Q_INVOKABLE QVariant data(const QModelIndex &index, const QString &role) const;
- QVariant data(const QModelIndex &index, int role) const override;
- Q_INVOKABLE bool setData(const QModelIndex &index, const QString &role, const QVariant &value);
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override;
QModelIndex parent(const QModelIndex &index) const override;
-Q_SIGNALS:
- void rowsChanged();
-
protected:
QVariant firstRow() const override;
void setInitialRows() override;
@@ -86,6 +79,8 @@ private:
};
void setRowsPrivate(const QVariantList &rowsAsVariantList);
+ QVariant dataPrivate(const QModelIndex &index, const QString &roleName) const override;
+ void setDataPrivate(const QModelIndex &index, const QString &roleName, QVariant value) override;
bool validateRowType(QLatin1StringView functionName, const QVariant &row) const;
bool validateNewRow(QLatin1StringView functionName, const QVariant &row,
diff --git a/src/qml/doc/snippets/qmltc/CMakeLists.txt b/src/qml/doc/snippets/qmltc/CMakeLists.txt
index 4574a44795..93477a70cc 100644
--- a/src/qml/doc/snippets/qmltc/CMakeLists.txt
+++ b/src/qml/doc/snippets/qmltc/CMakeLists.txt
@@ -59,5 +59,6 @@ qt6_add_qml_module(${application_name}
#! [qmltc-compile-to-cpp]
# (qmltc-specific) Link *private* libraries that correspond to QML modules:
+find_package(Qt6 COMPONENTS QmlPrivate QuickPrivate)
target_link_libraries(${application_name} PRIVATE Qt::QmlPrivate Qt::QuickPrivate)
#! [qmltc-compile-to-cpp]
diff --git a/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc b/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc
index 7ff01654e3..a76e746c16 100644
--- a/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc
+++ b/src/qml/doc/src/tools/qtqml-tooling-svgtoqml.qdoc
@@ -60,6 +60,9 @@ In addition, it supports the following options:
\li Display a preview of the Qt Quick item as it will be generated.
\endtable
+The tool can be invoked automatically from the build by using the \l{qt_target_qml_from_svg()}
+cmake function.
+
\section1 Comparison to other options
There are multiple options for including SVG content in Qt Quick. The following will give an
overview of where \c svgtoqml fits into the story.
diff --git a/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc
index 174768d28d..d31a7a8f10 100644
--- a/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc
+++ b/src/qml/doc/src/tools/qtquickcompiler/qtqml-qml-script-compiler.qdoc
@@ -88,6 +88,8 @@ To enable direct mode, you should consider the followings:
\li Link all the relavant private Qt modules instead of their public counterparts.
\badcode
+ find_package(Qt6 COMPONENTS QmlPrivate QuickPrivate)
+
qt_add_qml_module(someTarget
...
)
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 196ee7e76e..c963d6689b 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1699,10 +1699,12 @@ QVariant ExecutionEngine::toVariant(
return ::toVariant(value, typeHint, behavior, nullptr);
}
-static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visitedObjects,
- JSToQVariantConversionBehavior conversionBehvior)
+template<typename Association>
+Association objectToVariantAssociation(
+ const QV4::Object *o, V4ObjectSet *visitedObjects,
+ JSToQVariantConversionBehavior conversionBehvior)
{
- QVariantMap map;
+ Association association;
QV4::Scope scope(o->engine());
QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedValue name(scope);
@@ -1713,11 +1715,11 @@ static QVariantMap objectToVariantMap(const QV4::Object *o, V4ObjectSet *visited
break;
QString key = name->toQStringNoThrow();
- map.insert(key, ::toVariant(
+ association.insert(key, ::toVariant(
val, /*type hint*/ QMetaType {},
conversionBehvior, visitedObjects));
}
- return map;
+ return association;
}
static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObjects,
@@ -1762,7 +1764,7 @@ static QVariant objectToVariant(const QV4::Object *o, V4ObjectSet *visitedObject
But the Aggressive path is used only in QJSValue::toVariant
which is documented to be lossy
*/
- result = objectToVariantMap(o, visitedObjects, conversionBehvior);
+ result = objectToVariantAssociation<QVariantMap>(o, visitedObjects, conversionBehvior);
} else {
// If it's not a plain object, we can only save it as QJSValue.
result = QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue()));
@@ -1995,7 +1997,17 @@ QVariantMap ExecutionEngine::variantMapFromJS(const Object *o)
Q_ASSERT(o);
V4ObjectSet visitedObjects;
visitedObjects.insert(o->d());
- return objectToVariantMap(o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
+ return objectToVariantAssociation<QVariantMap>(
+ o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
+}
+
+QVariantHash ExecutionEngine::variantHashFromJS(const Object *o)
+{
+ Q_ASSERT(o);
+ V4ObjectSet visitedObjects;
+ visitedObjects.insert(o->d());
+ return objectToVariantAssociation<QVariantHash>(
+ o, &visitedObjects, JSToQVariantConversionBehavior::Safish);
}
// Converts the meta-type defined by the given type and data to JS.
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index ea06e08bf4..6cb4609450 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -695,6 +695,7 @@ public:
const QVariant &variant, Heap::Object *parent, int property, uint flags);
static QVariantMap variantMapFromJS(const QV4::Object *o);
+ static QVariantHash variantHashFromJS(const QV4::Object *o);
static bool metaTypeFromJS(const Value &value, QMetaType type, void *data);
QV4::ReturnedValue metaTypeToJS(QMetaType type, const void *data);
diff --git a/src/qml/jsruntime/qv4referenceobject_p.h b/src/qml/jsruntime/qv4referenceobject_p.h
index 35690a7228..fa019f5db0 100644
--- a/src/qml/jsruntime/qv4referenceobject_p.h
+++ b/src/qml/jsruntime/qv4referenceobject_p.h
@@ -226,71 +226,71 @@ DECLARE_HEAP_OBJECT(ReferenceObject, Object) {
delete bindableNotifier;
}
- private:
+private:
- bool hasFlag(Flag flag) const
- {
- return m_flags & quint8(flag);
- }
+ bool hasFlag(Flag flag) const
+ {
+ return m_flags & quint8(flag);
+ }
- void setFlag(Flag flag, bool set)
- {
- m_flags = set ? (m_flags | quint8(flag)) : (m_flags & ~quint8(flag));
- }
+ void setFlag(Flag flag, bool set)
+ {
+ m_flags = set ? (m_flags | quint8(flag)) : (m_flags & ~quint8(flag));
+ }
- const Function *m_function;
- int m_property;
- quint16 m_statementIndex;
- quint8 m_flags;
- ReferenceObjectEndpoint* referenceEndpoint;
- QPropertyNotifier* bindableNotifier;
- // We need to store an handle if we connect to the destroyed
- // signal so that we can disconnect from it. To avoid yet
- // another allocation, considering that
- // QMetaObject::Connection is not trivial, we store it in
- // block memory.
- alignas(alignof(QMetaObject::Connection))
- std::byte onDelete[sizeof(QMetaObject::Connection)];
- };
+ const Function *m_function;
+ int m_property;
+ quint16 m_statementIndex;
+ quint8 m_flags;
+ ReferenceObjectEndpoint* referenceEndpoint;
+ QPropertyNotifier* bindableNotifier;
+ // We need to store an handle if we connect to the destroyed
+ // signal so that we can disconnect from it. To avoid yet
+ // another allocation, considering that
+ // QMetaObject::Connection is not trivial, we store it in
+ // block memory.
+ alignas(alignof(QMetaObject::Connection))
+ std::byte onDelete[sizeof(QMetaObject::Connection)];
+};
- Q_DECLARE_OPERATORS_FOR_FLAGS(ReferenceObject::Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(ReferenceObject::Flags)
- } // namespace Heap
+} // namespace Heap
- struct ReferenceObject : public Object
- {
- V4_OBJECT2(ReferenceObject, Object)
- Q_MANAGED_TYPE(V4ReferenceObject)
- V4_NEEDS_DESTROY
+struct ReferenceObject : public Object
+{
+ V4_OBJECT2(ReferenceObject, Object)
+ Q_MANAGED_TYPE(V4ReferenceObject)
+ V4_NEEDS_DESTROY
- public:
- static constexpr const int AllProperties = -1;
+public:
+ static constexpr const int AllProperties = -1;
- template<typename HeapObject>
- static bool readReference(HeapObject *ref)
- {
- if (!ref->object())
- return false;
+ template<typename HeapObject>
+ static bool readReference(HeapObject *ref)
+ {
+ if (!ref->object())
+ return false;
- if (!ref->isDirty())
- return true;
+ if (!ref->isDirty())
+ return true;
- QV4::Scope scope(ref->internalClass->engine);
- QV4::ScopedObject object(scope, ref->object());
+ QV4::Scope scope(ref->internalClass->engine);
+ QV4::ScopedObject object(scope, ref->object());
- bool wasRead = false;
- if (ref->isVariant()) {
- QVariant variant;
- void *a[] = { &variant };
- wasRead = object->metacall(QMetaObject::ReadProperty, ref->property(), a)
- && ref->setVariant(variant);
- } else {
- void *a[] = { ref->storagePointer() };
- wasRead = object->metacall(QMetaObject::ReadProperty, ref->property(), a);
- }
+ bool wasRead = false;
+ if (ref->isVariant()) {
+ QVariant variant;
+ void *a[] = { &variant };
+ wasRead = object->metacall(QMetaObject::ReadProperty, ref->property(), a)
+ && ref->setVariant(variant);
+ } else {
+ void *a[] = { ref->storagePointer() };
+ wasRead = object->metacall(QMetaObject::ReadProperty, ref->property(), a);
+ }
- ref->setDirty(!ref->isConnected() || !wasRead);
+ ref->setDirty(!ref->isConnected() || !wasRead);
return wasRead;
}
diff --git a/src/qml/jsruntime/qv4variantassociationobject.cpp b/src/qml/jsruntime/qv4variantassociationobject.cpp
index 150c270e66..a60fe61ee6 100644
--- a/src/qml/jsruntime/qv4variantassociationobject.cpp
+++ b/src/qml/jsruntime/qv4variantassociationobject.cpp
@@ -9,10 +9,9 @@ QT_BEGIN_NAMESPACE
template<typename Return, typename MapCallable, typename HashCallable>
Return visitVariantAssociation(
- const QV4::Heap::VariantAssociationObject* association,
- MapCallable&& mapCallable,
- HashCallable&& hashCallable
-) {
+ const QV4::Heap::VariantAssociationObject *association,
+ MapCallable &&mapCallable, HashCallable &&hashCallable)
+{
switch (association->m_type) {
case QV4::Heap::VariantAssociationObject::AssociationType::VariantMap:
return std::invoke(
@@ -28,10 +27,9 @@ Return visitVariantAssociation(
template<typename Return, typename MapCallable, typename HashCallable>
Return visitVariantAssociation(
- QV4::Heap::VariantAssociationObject* association,
- MapCallable&& mapCallable,
- HashCallable&& hashCallable
-) {
+ QV4::Heap::VariantAssociationObject *association,
+ MapCallable &&mapCallable, HashCallable &&hashCallable)
+{
switch (association->m_type) {
case QV4::Heap::VariantAssociationObject::AssociationType::VariantMap:
return std::invoke(
@@ -47,21 +45,20 @@ Return visitVariantAssociation(
template<typename Return, typename Callable>
Return visitVariantAssociation(
- const QV4::Heap::VariantAssociationObject* association,
- Callable&& callable
-) {
+ const QV4::Heap::VariantAssociationObject *association, Callable &&callable)
+{
return visitVariantAssociation<Return>(association, callable, callable);
}
template<typename Return, typename Callable>
Return visitVariantAssociation(
- QV4::Heap::VariantAssociationObject* association,
- Callable&& callable
-) {
+ QV4::Heap::VariantAssociationObject *association, Callable &&callable)
+{
return visitVariantAssociation<Return>(association, callable, callable);
}
-static void mapPropertyKey(QV4::ArrayObject* mapping, QV4::Value* key) {
+static void mapPropertyKey(QV4::ArrayObject *mapping, QV4::Value *key)
+{
QString qKey = key->toQString();
QV4::Scope scope(mapping->engine());
@@ -74,7 +71,8 @@ static void mapPropertyKey(QV4::ArrayObject* mapping, QV4::Value* key) {
mapping->push_back(*key);
}
-static int keyToIndex(const QV4::ArrayObject* mapping, const QV4::Value* key) {
+static int keyToIndex(const QV4::ArrayObject *mapping, const QV4::Value *key)
+{
QString qKey = key->toQString();
QV4::Scope scope(mapping->engine());
@@ -87,7 +85,8 @@ static int keyToIndex(const QV4::ArrayObject* mapping, const QV4::Value* key) {
return -1;
}
-static QV4::ReturnedValue indexToKey(QV4::ArrayObject* mapping, uint index) {
+static QV4::ReturnedValue indexToKey(QV4::ArrayObject *mapping, uint index)
+{
Q_ASSERT(index < mapping->arrayData()->length());
return mapping->arrayData()->get(index);
@@ -95,308 +94,310 @@ static QV4::ReturnedValue indexToKey(QV4::ArrayObject* mapping, uint index) {
namespace QV4 {
- DEFINE_OBJECT_VTABLE(VariantAssociationObject);
+DEFINE_OBJECT_VTABLE(VariantAssociationObject);
- ReturnedValue VariantAssociationPrototype::fromQVariantMap(
- ExecutionEngine *engine,
- const QVariantMap& variantMap,
- QV4::Heap::Object* container,
+ReturnedValue VariantAssociationPrototype::fromQVariantMap(
+ ExecutionEngine *engine, const QVariantMap &variantMap, QV4::Heap::Object *container,
int property, Heap::ReferenceObject::Flags flags)
- {
- return engine->memoryManager->allocate<VariantAssociationObject>(
- variantMap, container, property, flags)->asReturnedValue();
- }
+{
+ return engine->memoryManager->allocate<VariantAssociationObject>(
+ variantMap, container, property, flags)->asReturnedValue();
+}
- ReturnedValue VariantAssociationPrototype::fromQVariantHash(
- ExecutionEngine *engine,
- const QVariantHash& variantHash,
- QV4::Heap::Object* container,
+ReturnedValue VariantAssociationPrototype::fromQVariantHash(
+ ExecutionEngine *engine, const QVariantHash &variantHash, QV4::Heap::Object *container,
int property, Heap::ReferenceObject::Flags flags)
- {
- return engine->memoryManager->allocate<VariantAssociationObject>(
- variantHash, container, property, flags)->asReturnedValue();
- }
+{
+ return engine->memoryManager->allocate<VariantAssociationObject>(
+ variantHash, container, property, flags)->asReturnedValue();
+}
- namespace Heap {
- void VariantAssociationObject::init(
- const QVariantMap& variantMap,
- QV4::Heap::Object* container,
- int property, Heap::ReferenceObject::Flags flags)
- {
- ReferenceObject::init(container, property, flags);
+namespace Heap {
+void VariantAssociationObject::init(
+ const QVariantMap &variantMap, QV4::Heap::Object *container, int property,
+ Heap::ReferenceObject::Flags flags)
+{
+ ReferenceObject::init(container, property, flags);
- new(m_variantAssociation) QVariantMap(variantMap);
- m_type = AssociationType::VariantMap;
- }
+ new (m_variantAssociation) QVariantMap(variantMap);
+ m_type = AssociationType::VariantMap;
+}
- void VariantAssociationObject::init(
- const QVariantHash& variantHash,
- QV4::Heap::Object* container,
- int property, Heap::ReferenceObject::Flags flags)
- {
- ReferenceObject::init(container, property, flags);
+void VariantAssociationObject::init(
+ const QVariantHash &variantHash, QV4::Heap::Object *container, int property,
+ Heap::ReferenceObject::Flags flags)
+{
+ ReferenceObject::init(container, property, flags);
- new(m_variantAssociation) QVariantHash(variantHash);
- m_type = AssociationType::VariantHash;
- }
+ new (m_variantAssociation) QVariantHash(variantHash);
+ m_type = AssociationType::VariantHash;
+}
- void VariantAssociationObject::destroy() {
- visitVariantAssociation<void>(
- this,
- std::destroy_at<QVariantMap>,
- std::destroy_at<QVariantHash>);
- ReferenceObject::destroy();
- }
+void VariantAssociationObject::destroy()
+{
+ visitVariantAssociation<void>(
+ this, std::destroy_at<QVariantMap>, std::destroy_at<QVariantHash>);
+ ReferenceObject::destroy();
+}
- QVariant VariantAssociationObject::toVariant() const
- {
- return visitVariantAssociation<QVariant>(
- this, [](auto association){ return QVariant(*association); });
- }
+QVariant VariantAssociationObject::toVariant() const
+{
+ return visitVariantAssociation<QVariant>(
+ this, [](auto association){ return QVariant(*association); });
+}
- bool VariantAssociationObject::setVariant(const QVariant &variant)
- {
- auto metatypeId = variant.metaType().id();
-
- if (metatypeId != QMetaType::QVariantMap && metatypeId != QMetaType::QVariantHash)
- return false;
-
- if (metatypeId == QMetaType::QVariantMap && m_type == AssociationType::VariantMap) {
- *reinterpret_cast<QVariantMap *>(&m_variantAssociation) = variant.toMap();
- } else if (metatypeId == QMetaType::QVariantMap && m_type == AssociationType::VariantHash) {
- std::destroy_at(reinterpret_cast<QVariantHash *>(&m_variantAssociation));
- new(m_variantAssociation) QVariantMap(variant.toMap());
- m_type = AssociationType::VariantMap;
- } else if (metatypeId == QMetaType::QVariantHash && m_type == AssociationType::VariantHash) {
- *reinterpret_cast<QVariantHash *>(&m_variantAssociation) = variant.toHash();
- } else if (metatypeId == QMetaType::QVariantHash && m_type == AssociationType::VariantMap) {
- std::destroy_at(reinterpret_cast<QVariantMap *>(&m_variantAssociation));
- new(m_variantAssociation) QVariantHash(variant.toHash());
- m_type = AssociationType::VariantHash;
- }
+bool VariantAssociationObject::setVariant(const QVariant &variant)
+{
+ auto metatypeId = variant.metaType().id();
+
+ if (metatypeId != QMetaType::QVariantMap && metatypeId != QMetaType::QVariantHash)
+ return false;
+
+ if (metatypeId == QMetaType::QVariantMap && m_type == AssociationType::VariantMap) {
+ *reinterpret_cast<QVariantMap *>(&m_variantAssociation) = variant.toMap();
+ } else if (metatypeId == QMetaType::QVariantMap && m_type == AssociationType::VariantHash) {
+ std::destroy_at(reinterpret_cast<QVariantHash *>(&m_variantAssociation));
+ new (m_variantAssociation) QVariantMap(variant.toMap());
+ m_type = AssociationType::VariantMap;
+ } else if (metatypeId == QMetaType::QVariantHash && m_type == AssociationType::VariantHash) {
+ *reinterpret_cast<QVariantHash *>(&m_variantAssociation) = variant.toHash();
+ } else if (metatypeId == QMetaType::QVariantHash && m_type == AssociationType::VariantMap) {
+ std::destroy_at(reinterpret_cast<QVariantMap *>(&m_variantAssociation));
+ new (m_variantAssociation) QVariantHash(variant.toHash());
+ m_type = AssociationType::VariantHash;
+ }
- return true;
- }
+ return true;
+}
- VariantAssociationObject *VariantAssociationObject::detached() const
- {
- return visitVariantAssociation<VariantAssociationObject*>(
- this,
- [engine = internalClass->engine](auto association){
- return engine->memoryManager->allocate<QV4::VariantAssociationObject>(
- *association, nullptr, -1, ReferenceObject::Flag::NoFlag);
- }
- );
+VariantAssociationObject *VariantAssociationObject::detached() const
+{
+ return visitVariantAssociation<VariantAssociationObject *>(
+ this, [engine = internalClass->engine](auto association) {
+ return engine->memoryManager->allocate<QV4::VariantAssociationObject>(
+ *association, nullptr, -1, ReferenceObject::Flag::NoFlag);
}
+ );
+}
- } // namespace Heap
-
- ReturnedValue VariantAssociationObject::virtualGet(const Managed *that, PropertyKey id, const Value *, bool * hasProperty)
- {
- QString key = id.toQString();
- const VariantAssociationObject *self = static_cast<const VariantAssociationObject *>(that);
+} // namespace Heap
- bool found = false;
- if (ReturnedValue result = self->getElement(key, &found); found) {
- if (hasProperty)
- *hasProperty = true;
- return result;
- }
+ReturnedValue VariantAssociationObject::virtualGet(
+ const Managed *that, PropertyKey id, const Value *, bool *hasProperty)
+{
+ QString key = id.toQString();
+ const VariantAssociationObject *self = static_cast<const VariantAssociationObject *>(that);
- return ReferenceObject::virtualGet(that, id, that, hasProperty);
+ bool found = false;
+ if (ReturnedValue result = self->getElement(key, &found); found) {
+ if (hasProperty)
+ *hasProperty = true;
+ return result;
}
- bool VariantAssociationObject::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *)
- {
- QString key = id.toQString();
- return static_cast<VariantAssociationObject *>(that)->putElement(key, value);
- }
+ return ReferenceObject::virtualGet(that, id, that, hasProperty);
+}
- bool VariantAssociationObject::virtualDeleteProperty(Managed *that, PropertyKey id)
- {
- QString key = id.toQString();
- return static_cast<VariantAssociationObject *>(that)->deleteElement(key);
- }
+bool VariantAssociationObject::virtualPut(
+ Managed *that, PropertyKey id, const Value &value, Value *)
+{
+ QString key = id.toQString();
+ return static_cast<VariantAssociationObject *>(that)->putElement(key, value);
+}
- OwnPropertyKeyIterator *VariantAssociationObject::virtualOwnPropertyKeys(
- const Object *m, Value *target
- ) {
- struct VariantAssociationOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
- {
- QStringList keys;
+bool VariantAssociationObject::virtualDeleteProperty(Managed *that, PropertyKey id)
+{
+ QString key = id.toQString();
+ return static_cast<VariantAssociationObject *>(that)->deleteElement(key);
+}
- ~VariantAssociationOwnPropertyKeyIterator() override = default;
+OwnPropertyKeyIterator *VariantAssociationObject::virtualOwnPropertyKeys(
+ const Object *m, Value *target)
+{
+ struct VariantAssociationOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+ {
+ QStringList keys;
- PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
- {
- const VariantAssociationObject *variantAssociation =
- static_cast<const VariantAssociationObject *>(o);
+ ~VariantAssociationOwnPropertyKeyIterator() override = default;
- if (memberIndex == 0) {
- keys = variantAssociation->keys();
- keys.sort();
- }
+ PropertyKey next(
+ const Object *o, Property *pd = nullptr,
+ PropertyAttributes *attrs = nullptr) override
+ {
+ const VariantAssociationObject *variantAssociation =
+ static_cast<const VariantAssociationObject *>(o);
- if (static_cast<qsizetype>(memberIndex) < keys.count()) {
- Scope scope(variantAssociation->engine());
- ScopedString propertyName(scope, scope.engine->newString(keys[memberIndex]));
- ScopedPropertyKey id(scope, propertyName->toPropertyKey());
+ if (memberIndex == 0) {
+ keys = variantAssociation->keys();
+ keys.sort();
+ }
- if (attrs)
- *attrs = QV4::Attr_Data;
- if (pd)
- pd->value = variantAssociation->getElement(keys[memberIndex]);
+ if (static_cast<qsizetype>(memberIndex) < keys.count()) {
+ Scope scope(variantAssociation->engine());
+ ScopedString propertyName(scope, scope.engine->newString(keys[memberIndex]));
+ ScopedPropertyKey id(scope, propertyName->toPropertyKey());
- ++memberIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = variantAssociation->getElement(keys[memberIndex]);
- return id;
- }
+ ++memberIndex;
- return PropertyKey::invalid();
+ return id;
}
- };
- QV4::ReferenceObject::readReference(static_cast<const VariantAssociationObject *>(m)->d());
-
- *target = *m;
- return new VariantAssociationOwnPropertyKeyIterator;
- }
-
- PropertyAttributes VariantAssociationObject::virtualGetOwnProperty(
- const Managed *m, PropertyKey id, Property *p
- ) {
- auto variantAssociation = static_cast<const VariantAssociationObject *>(m);
+ return PropertyKey::invalid();
+ }
+ };
- bool hasElement = false;
- Scope scope(variantAssociation->engine());
- ScopedValue element(scope, variantAssociation->getElement(id.toQString(), &hasElement));
+ QV4::ReferenceObject::readReference(static_cast<const VariantAssociationObject *>(m)->d());
- if (!hasElement)
- return Attr_Invalid;
+ *target = *m;
+ return new VariantAssociationOwnPropertyKeyIterator;
+}
- if (p)
- p->value = element->asReturnedValue();
+PropertyAttributes VariantAssociationObject::virtualGetOwnProperty(
+ const Managed *m, PropertyKey id, Property *p)
+{
+ auto variantAssociation = static_cast<const VariantAssociationObject *>(m);
- return Attr_Data;
- }
+ bool hasElement = false;
+ Scope scope(variantAssociation->engine());
+ ScopedValue element(scope, variantAssociation->getElement(id.toQString(), &hasElement));
- int VariantAssociationObject::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
- {
- VariantAssociationObject *variantAssociation = static_cast<VariantAssociationObject *>(object);
- Q_ASSERT(variantAssociation);
+ if (!hasElement)
+ return Attr_Invalid;
- Heap::VariantAssociationObject *heapAssociation = variantAssociation->d();
+ if (p)
+ p->value = element->asReturnedValue();
- Q_ASSERT(heapAssociation->propertyIndexMapping);
+ return Attr_Data;
+}
- switch (call) {
- case QMetaObject::ReadProperty: {
- QV4::ReferenceObject::readReference(heapAssociation);
+int VariantAssociationObject::virtualMetacall(
+ Object *object, QMetaObject::Call call, int index, void **a)
+{
+ VariantAssociationObject *variantAssociation = static_cast<VariantAssociationObject *>(object);
+ Q_ASSERT(variantAssociation);
- if (index < 0 || index >= static_cast<int>(heapAssociation->propertyIndexMapping->arrayData->length()))
- return 0;
+ Heap::VariantAssociationObject *heapAssociation = variantAssociation->d();
- Scope scope(variantAssociation->engine());
- ScopedArrayObject mapping(scope, heapAssociation->propertyIndexMapping);
- ScopedString scopedKey(scope, indexToKey(mapping, index));
- const QString key = scopedKey->toQString();
+ Q_ASSERT(heapAssociation->propertyIndexMapping);
- if (!visitVariantAssociation<bool>(heapAssociation, [key](auto association) {
- return association->contains(key);
- })) {
- return 0;
- }
+ switch (call) {
+ case QMetaObject::ReadProperty: {
+ QV4::ReferenceObject::readReference(heapAssociation);
- visitVariantAssociation<void>(heapAssociation, [a, key](auto association) {
- *static_cast<QVariant*>(a[0]) = association->value(key);
- });
+ if (index < 0 || index >= static_cast<int>(heapAssociation->propertyIndexMapping->arrayData->length()))
+ return 0;
- break;
+ Scope scope(variantAssociation->engine());
+ ScopedArrayObject mapping(scope, heapAssociation->propertyIndexMapping);
+ ScopedString scopedKey(scope, indexToKey(mapping, index));
+ const QString key = scopedKey->toQString();
+
+ if (!visitVariantAssociation<bool>(heapAssociation, [key](auto association) {
+ return association->contains(key);
+ })) {
+ return 0;
}
- case QMetaObject::WriteProperty: {
- if (index < 0 || index >= static_cast<int>(heapAssociation->propertyIndexMapping->arrayData->length()))
- return 0;
- Scope scope(variantAssociation->engine());
- ScopedArrayObject mapping(scope, heapAssociation->propertyIndexMapping);
- ScopedString scopedKey(scope, indexToKey(mapping, index));
- const QString key = scopedKey->toQString();
+ visitVariantAssociation<void>(heapAssociation, [a, key](auto association) {
+ *static_cast<QVariant*>(a[0]) = association->value(key);
+ });
+
+ break;
+ }
+ case QMetaObject::WriteProperty: {
+ if (index < 0 || index >= static_cast<int>(heapAssociation->propertyIndexMapping->arrayData->length()))
+ return 0;
- visitVariantAssociation<void>(heapAssociation, [a, key](auto association) {
- if (association->contains(key))
- association->insert(key, *static_cast<QVariant*>(a[0]));
- });
+ Scope scope(variantAssociation->engine());
+ ScopedArrayObject mapping(scope, heapAssociation->propertyIndexMapping);
+ ScopedString scopedKey(scope, indexToKey(mapping, index));
+ const QString key = scopedKey->toQString();
- QV4::ReferenceObject::writeBack(heapAssociation);
+ visitVariantAssociation<void>(heapAssociation, [a, key](auto association) {
+ if (association->contains(key))
+ association->insert(key, *static_cast<QVariant*>(a[0]));
+ });
- break;
- }
- default:
- return 0; // not supported
- }
+ QV4::ReferenceObject::writeBack(heapAssociation);
- return -1;
+ break;
+ }
+ default:
+ return 0; // not supported
}
- QV4::ReturnedValue VariantAssociationObject::getElement(const QString& key, bool *hasProperty) const {
- QV4::ReferenceObject::readReference(d());
+ return -1;
+}
- return visitVariantAssociation<QV4::ReturnedValue>(
- d(),
- [engine = engine(), this, key, hasProperty](auto* association) {
- bool hasElement = association->contains(key);
- if (hasProperty)
- *hasProperty = hasElement;
+QV4::ReturnedValue VariantAssociationObject::getElement(
+ const QString &key, bool *hasProperty) const
+{
+ QV4::ReferenceObject::readReference(d());
- if (hasElement) {
- if (!d()->propertyIndexMapping.heapObject())
- d()->propertyIndexMapping.set(engine, engine->newArrayObject(1));
+ return visitVariantAssociation<QV4::ReturnedValue>(
+ d(),
+ [engine = engine(), this, key, hasProperty](auto *association) {
+ bool hasElement = association->contains(key);
+ if (hasProperty)
+ *hasProperty = hasElement;
- Scope scope(engine);
- ScopedString scopedKey(scope, scope.engine->newString(key));
- ScopedArrayObject mapping(scope, d()->propertyIndexMapping);
+ if (hasElement) {
+ if (!d()->propertyIndexMapping.heapObject())
+ d()->propertyIndexMapping.set(engine, engine->newArrayObject(1));
- mapPropertyKey(mapping, scopedKey);
+ Scope scope(engine);
+ ScopedString scopedKey(scope, scope.engine->newString(key));
+ ScopedArrayObject mapping(scope, d()->propertyIndexMapping);
- return engine->fromVariant(
- association->value(key),
- d(), keyToIndex(mapping.getPointer(), scopedKey.getPointer()),
- Heap::ReferenceObject::Flag::CanWriteBack |
- Heap::ReferenceObject::Flag::IsVariant);
- }
+ mapPropertyKey(mapping, scopedKey);
- return Encode::undefined();
+ return engine->fromVariant(
+ association->value(key),
+ d(), keyToIndex(mapping.getPointer(), scopedKey.getPointer()),
+ Heap::ReferenceObject::Flag::CanWriteBack |
+ Heap::ReferenceObject::Flag::IsVariant);
}
- );
- }
- bool VariantAssociationObject::putElement(const QString& key, const Value& value) {
- Heap::VariantAssociationObject *heapAssociation = d();
+ return Encode::undefined();
+ }
+ );
+}
- visitVariantAssociation<void>(heapAssociation, [engine = engine(), value, key](auto association){
- association->insert(key, engine->toVariant(value, QMetaType{}, false));
- });
+bool VariantAssociationObject::putElement(const QString &key, const Value &value)
+{
+ Heap::VariantAssociationObject *heapAssociation = d();
- QV4::ReferenceObject::writeBack(heapAssociation);
- return true;
- }
+ visitVariantAssociation<void>(heapAssociation, [engine = engine(), value, key](auto association){
+ association->insert(key, engine->toVariant(value, QMetaType{}, false));
+ });
- bool VariantAssociationObject::deleteElement(const QString& key) {
- bool result = visitVariantAssociation<bool>(d(), [key](auto association) {
- return association->remove(key);
- });
+ QV4::ReferenceObject::writeBack(heapAssociation);
+ return true;
+}
- if (result)
- QV4::ReferenceObject::writeBack(d());
+bool VariantAssociationObject::deleteElement(const QString &key)
+{
+ bool result = visitVariantAssociation<bool>(d(), [key](auto association) {
+ return association->remove(key);
+ });
- return result;
- }
+ if (result)
+ QV4::ReferenceObject::writeBack(d());
+
+ return result;
+}
+
+QStringList VariantAssociationObject::keys() const {
+ return visitVariantAssociation<QStringList>(d(), [](auto association){
+ return association->keys();
+ });
+}
- QStringList VariantAssociationObject::keys() const {
- return visitVariantAssociation<QStringList>(d(), [](auto association){
- return association->keys();
- });
- }
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4variantassociationobject_p.h b/src/qml/jsruntime/qv4variantassociationobject_p.h
index c0813a0f40..dce2c27409 100644
--- a/src/qml/jsruntime/qv4variantassociationobject_p.h
+++ b/src/qml/jsruntime/qv4variantassociationobject_p.h
@@ -26,96 +26,89 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
- struct Q_QML_EXPORT VariantAssociationPrototype : public QV4::Object
- {
- V4_PROTOTYPE(objectPrototype);
+struct Q_QML_EXPORT VariantAssociationPrototype : public QV4::Object
+{
+ V4_PROTOTYPE(objectPrototype);
- static ReturnedValue fromQVariantMap(
- ExecutionEngine *engine,
- const QVariantMap& variantMap,
- QV4::Heap::Object* container,
+ static ReturnedValue fromQVariantMap(
+ ExecutionEngine *engine, const QVariantMap &variantMap, QV4::Heap::Object *container,
int property, Heap::ReferenceObject::Flags flags);
- static ReturnedValue fromQVariantHash(
- ExecutionEngine *engine,
- const QVariantHash& variantHash,
- QV4::Heap::Object* container,
+ static ReturnedValue fromQVariantHash(
+ ExecutionEngine *engine, const QVariantHash &variantHash, QV4::Heap::Object *container,
int property, Heap::ReferenceObject::Flags flags);
- };
+};
- namespace Heap {
+namespace Heap {
- #define VariantAssociationObjectMembers(class, Member) \
- Member(class, Pointer, ArrayObject *, propertyIndexMapping);
+#define VariantAssociationObjectMembers(class, Member) \
+ Member(class, Pointer, ArrayObject *, propertyIndexMapping);
- DECLARE_HEAP_OBJECT(VariantAssociationObject, ReferenceObject)
- {
- DECLARE_MARKOBJECTS(VariantAssociationObject)
+DECLARE_HEAP_OBJECT(VariantAssociationObject, ReferenceObject)
+{
+ DECLARE_MARKOBJECTS(VariantAssociationObject)
- enum class AssociationType: quint8 {
- VariantMap,
- VariantHash
- };
+ enum class AssociationType: quint8
+ {
+ VariantMap,
+ VariantHash
+ };
- void init(
- const QVariantMap& variantMap,
- QV4::Heap::Object* container,
- int property, Heap::ReferenceObject::Flags flags);
+ void init(
+ const QVariantMap &variantMap, QV4::Heap::Object *container, int property,
+ Heap::ReferenceObject::Flags flags);
- void init(
- const QVariantHash& variantHash,
- QV4::Heap::Object* container,
- int property, Heap::ReferenceObject::Flags flags);
+ void init(
+ const QVariantHash &variantHash, QV4::Heap::Object *container, int property,
+ Heap::ReferenceObject::Flags flags);
- void destroy();
+ void destroy();
- void *storagePointer() { return &m_variantAssociation; }
+ void *storagePointer() { return &m_variantAssociation; }
- QVariant toVariant() const;
- bool setVariant(const QVariant &variant);
+ QVariant toVariant() const;
+ bool setVariant(const QVariant &variant);
- VariantAssociationObject *detached() const;
+ VariantAssociationObject *detached() const;
- // The alignment calculation needs to be out of the
- // `alignas` due to a GCC 8.3 bug (that at the time of
- // writing is used on the QNX 7.1 platform).
- // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94929
- static constexpr auto alignment =
- std::max(alignof(QVariantMap), alignof(QVariantHash));
- alignas(alignment)
- std::byte m_variantAssociation[std::max(sizeof(QVariantMap), sizeof(QVariantHash))];
+ // The alignment calculation needs to be out of the
+ // `alignas` due to a GCC 8.3 bug (that at the time of
+ // writing is used on the QNX 7.1 platform).
+ // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94929
+ static constexpr auto alignment = std::max(alignof(QVariantMap), alignof(QVariantHash));
+ alignas(alignment)
+ std::byte m_variantAssociation[std::max(sizeof(QVariantMap), sizeof(QVariantHash))];
- AssociationType m_type;
- };
+ AssociationType m_type;
+};
- } // namespace Heap
+} // namespace Heap
- struct Q_QML_EXPORT VariantAssociationObject : public QV4::ReferenceObject
- {
- V4_OBJECT2(VariantAssociationObject, QV4::ReferenceObject);
- V4_PROTOTYPE(variantAssociationPrototype);
- V4_NEEDS_DESTROY
+struct Q_QML_EXPORT VariantAssociationObject : public QV4::ReferenceObject
+{
+ V4_OBJECT2(VariantAssociationObject, QV4::ReferenceObject);
+ V4_PROTOTYPE(variantAssociationPrototype);
+ V4_NEEDS_DESTROY
- static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value,
- Value *receiver);
- static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id,
- const Value *receiver, bool *hasProperty);
+ static bool virtualPut(
+ Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver);
+ static QV4::ReturnedValue virtualGet(
+ const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty);
- static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id);
+ static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id);
- static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
+ static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
- static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id,
- Property *p);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
- static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a);
+ static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a);
- QV4::ReturnedValue getElement(const QString& id, bool *hasProperty = nullptr) const;
- bool putElement(const QString& key, const Value& value);
- bool deleteElement(const QString& key);
+ QV4::ReturnedValue getElement(const QString &id, bool *hasProperty = nullptr) const;
+ bool putElement(const QString &key, const Value &value);
+ bool deleteElement(const QString &key);
- QStringList keys() const;
- };
+ QStringList keys() const;
+};
} // namespace QV4
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 7a081adbdf..ff051363c8 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -2190,7 +2190,7 @@ bool AOTCompiledContext::callQmlContextPropertyLookup(uint index, void **args, i
return false;
}
-enum MatchScore { NoMatch, VariantMatch, ExactMatch, };
+enum MatchScore { NoMatch, ExactMatch, };
static MatchScore resolveQObjectMethodOverload(
QV4::QObjectMethod *method, QV4::Lookup *lookup, int relativeMethodIndex)
@@ -2374,6 +2374,20 @@ void AOTCompiledContext::initCallObjectPropertyLookupAsVariant(uint index, QObje
QV4::Lookup *lookup = compilationUnit->runtimeLookups + index;
QV4::Scope scope(engine->handle());
+
+ const auto throwInvalidObjectError = [&]() {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [object Object] is not a function")
+ .arg(compilationUnit->runtimeStrings[lookup->nameIndex]->toQString()));
+ };
+
+ const auto *ddata = QQmlData::get(object, false);
+ if (ddata && ddata->hasVMEMetaObject && ddata->jsWrapper.isNullOrUndefined()) {
+ // We cannot lookup functions on an object with VME metaobject but no QObjectWrapper
+ throwInvalidObjectError();
+ return;
+ }
+
QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
QV4::ScopedFunctionObject function(scope, lookup->getter(scope.engine, thisObject));
if (auto *method = function->as<QV4::QObjectMethod>()) {
@@ -2388,9 +2402,7 @@ void AOTCompiledContext::initCallObjectPropertyLookupAsVariant(uint index, QObje
return;
}
- scope.engine->throwTypeError(
- QStringLiteral("Property '%1' of object [object Object] is not a function")
- .arg(compilationUnit->runtimeStrings[lookup->nameIndex]->toQString()));
+ throwInvalidObjectError();
}
void AOTCompiledContext::initCallObjectPropertyLookup(
@@ -2403,6 +2415,20 @@ void AOTCompiledContext::initCallObjectPropertyLookup(
QV4::Lookup *lookup = compilationUnit->runtimeLookups + index;
QV4::Scope scope(engine->handle());
+
+ const auto throwInvalidObjectError = [&]() {
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [object Object] is not a function")
+ .arg(compilationUnit->runtimeStrings[lookup->nameIndex]->toQString()));
+ };
+
+ const auto *ddata = QQmlData::get(object, false);
+ if (ddata && ddata->hasVMEMetaObject && ddata->jsWrapper.isNullOrUndefined()) {
+ // We cannot lookup functions on an object with VME metaobject but no QObjectWrapper
+ throwInvalidObjectError();
+ return;
+ }
+
QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
QV4::ScopedFunctionObject function(scope, lookup->getter(scope.engine, thisObject));
if (auto *method = function->as<QV4::QObjectMethod>()) {
@@ -2418,9 +2444,7 @@ void AOTCompiledContext::initCallObjectPropertyLookup(
return;
}
- scope.engine->throwTypeError(
- QStringLiteral("Property '%1' of object [object Object] is not a function")
- .arg(compilationUnit->runtimeStrings[lookup->nameIndex]->toQString()));
+ throwInvalidObjectError();
}
bool AOTCompiledContext::loadGlobalLookup(uint index, void *target) const
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index a1f4aaca18..10de6f85af 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -543,6 +543,13 @@ QMetaType QQmlValueTypeWrapper::type() const
bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
{
bool destructGadgetOnExit = false;
+ auto cleanup = qScopeGuard([&]() {
+ if (destructGadgetOnExit) {
+ d()->metaType().destruct(d()->gadgetPtr());
+ d()->setGadgetPtr(nullptr);
+ }
+ });
+
Q_ALLOCA_DECLARE(void, gadget);
if (d()->isReference()) {
if (!d()->gadgetPtr()) {
@@ -559,11 +566,6 @@ bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const
int status = -1;
void *a[] = { d()->gadgetPtr(), nullptr, &status, &flags };
QMetaObject::metacall(target, QMetaObject::WriteProperty, propertyIndex, a);
-
- if (destructGadgetOnExit) {
- d()->metaType().destruct(d()->gadgetPtr());
- d()->setGadgetPtr(nullptr);
- }
return true;
}
diff --git a/src/qmlcompiler/qcoloroutput.cpp b/src/qmlcompiler/qcoloroutput.cpp
index f3f5b86d7a..6a99a8007e 100644
--- a/src/qmlcompiler/qcoloroutput.cpp
+++ b/src/qmlcompiler/qcoloroutput.cpp
@@ -6,7 +6,9 @@
#include <QtCore/qfile.h>
#include <QtCore/qhash.h>
-#ifndef Q_OS_WIN
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#else
#include <unistd.h>
#endif
@@ -75,17 +77,25 @@ private:
*/
inline bool isColoringPossible() const
{
+ static std::optional<bool> canColor;
+ if (canColor.has_value())
+ return canColor.value();
+
#if defined(Q_OS_WIN)
- /* Windows doesn't at all support ANSI escape codes, unless
- * the user install a "device driver". See the Wikipedia links in the
- * class documentation for details. */
- return false;
+ HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE);
+ DWORD mode = 0;
+
+ if (GetConsoleMode(hErr, &mode))
+ canColor = SetConsoleMode(hErr, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+ else
+ canColor = false;
#else
/* We use QFile::handle() to get the file descriptor. It's a bit unsure
* whether it's 2 on all platforms and in all cases, so hopefully this layer
* of abstraction helps handle such cases. */
- return isatty(fileno(stderr));
+ canColor = isatty(fileno(stderr));
#endif
+ return canColor.value();
}
};
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 2edbe61307..91c88081c3 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -2652,6 +2652,7 @@ bool QQmlJSImportVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
{
QQmlJSMetaEnum qmlEnum(uied->name.toString());
qmlEnum.setIsQml(true);
+ qmlEnum.setLineNumber(uied->enumToken.startLine);
for (const auto *member = uied->members; member; member = member->next) {
qmlEnum.addKey(member->member.toString());
qmlEnum.addValue(int(member->value));
diff --git a/src/qmlcompiler/qqmljslinter.cpp b/src/qmlcompiler/qqmljslinter.cpp
index 3031d82bf7..c177968220 100644
--- a/src/qmlcompiler/qqmljslinter.cpp
+++ b/src/qmlcompiler/qqmljslinter.cpp
@@ -471,31 +471,18 @@ static void addJsonWarning(QJsonArray &warnings, const QQmlJS::DiagnosticMessage
QJsonObject jsonFix {
{ "message"_L1, suggestion->fixDescription() },
{ "replacement"_L1, suggestion->replacement() },
- { "isHint"_L1, !suggestion->isAutoApplicable() },
+ { "isAutoApplicable"_L1, suggestion->isAutoApplicable() },
+ { "hint"_L1, suggestion->hint() },
};
convertLocation(suggestion->location(), &jsonFix);
const QString filename = suggestion->filename();
if (!filename.isEmpty())
jsonFix.insert("fileName"_L1, filename);
suggestions << jsonFix;
-
- const QString hint = suggestion->hint();
- if (!hint.isEmpty()) {
- // We need to keep compatibility with the JSON format.
- // Therefore the overly verbose encoding of the hint.
- QJsonObject jsonHint {
- { "message"_L1, hint },
- { "replacement"_L1, QString() },
- { "isHint"_L1, true }
- };
- convertLocation(QQmlJS::SourceLocation(), &jsonHint);
- suggestions << jsonHint;
- }
}
jsonMessage[u"suggestions"] = suggestions;
warnings << jsonMessage;
-
}
void QQmlJSLinter::processMessages(QJsonArray &warnings)
diff --git a/src/qmlcompiler/qqmljslogger.cpp b/src/qmlcompiler/qqmljslogger.cpp
index 8410032f48..6d42e4f545 100644
--- a/src/qmlcompiler/qqmljslogger.cpp
+++ b/src/qmlcompiler/qqmljslogger.cpp
@@ -477,13 +477,15 @@ void QQmlJSLogger::printFix(const QQmlJSFixSuggestion &fixItem)
int tabCount = issueLocationWithContext.beforeText().count(u'\t');
// Do not draw location indicator for multiline replacement strings
- if (replacementString.contains(u'\n'))
- return;
+ if (!replacementString.contains(u'\n')) {
+ m_output.write(u" "_s.repeated(
+ issueLocationWithContext.beforeText().size() - tabCount)
+ + u"\t"_s.repeated(tabCount)
+ + u"^"_s.repeated(replacement.size()) + u'\n');
+ }
- m_output.write(u" "_s.repeated(
- issueLocationWithContext.beforeText().size() - tabCount)
- + u"\t"_s.repeated(tabCount)
- + u"^"_s.repeated(replacement.size()) + u'\n');
+ if (!fixItem.hint().isEmpty())
+ m_output.write(" "_L1 + fixItem.hint());
}
QQmlJSFixSuggestion::QQmlJSFixSuggestion(const QString &fixDescription,
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index 829ad6eda0..3466745ae5 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -59,6 +59,7 @@ class QQmlJSMetaEnum
QString m_alias;
QString m_typeName;
QSharedPointer<const QQmlJSScope> m_type;
+ int m_lineNumber = 0;
bool m_isFlag = false;
bool m_isScoped = false;
bool m_isQml = false;
@@ -100,6 +101,9 @@ public:
QSharedPointer<const QQmlJSScope> type() const { return m_type; }
void setType(const QSharedPointer<const QQmlJSScope> &type) { m_type = type; }
+ int lineNumber() const { return m_lineNumber; }
+ void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; }
+
friend bool operator==(const QQmlJSMetaEnum &a, const QQmlJSMetaEnum &b)
{
return a.m_keys == b.m_keys
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 1a266024d7..12286af628 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -254,12 +254,13 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
scope->setIsJavaScriptBuiltin(true);
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name, prototype, defaultProperty, attachedType, "
+ tr("Expected only lineNumber, name, prototype, defaultProperty, "
+ "attachedType, "
"valueType, exports, interfaces, isSingleton, isCreatable, "
"isStructured, isComposite, hasCustomParser, enforcesScopedEnums, "
"aliases, exportMetaObjectRevisions, deferredNames, and "
"immediateNames in script bindings, not \"%1\".")
- .arg(name));
+ .arg(name));
}
} else {
addWarning(member->firstSourceLocation(),
@@ -303,6 +304,9 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(
QString name = toString(script->qualifiedId);
if (name == QLatin1String("name")) {
metaMethod.setMethodName(readStringBinding(script));
+ } else if (name == QLatin1String("lineNumber")) {
+ metaMethod.setSourceLocation(
+ SourceLocation::fromQSizeType(0, 0, readIntBinding(script), 1));
} else if (name == QLatin1String("type")) {
metaMethod.setReturnTypeName(readStringBinding(script));
} else if (name == QLatin1String("revision")) {
@@ -343,7 +347,8 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(
metaMethod.setIsConst(readBoolBinding(script));
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name, type, revision, isPointer, isTypeConstant, "
+ tr("Expected only name, lineNumber, type, revision, isPointer, "
+ "isTypeConstant, "
"isList, isCloned, isConstructor, isMethodConstant, and "
"isJavaScriptFunction in script bindings."));
}
@@ -388,6 +393,9 @@ void QQmlJSTypeDescriptionReader::readProperty(UiObjectDefinition *ast, const QQ
QString id = toString(script->qualifiedId);
if (id == QLatin1String("name")) {
property.setPropertyName(readStringBinding(script));
+ } else if (id == QLatin1String("lineNumber")) {
+ property.setSourceLocation(
+ SourceLocation::fromQSizeType(0, 0, readIntBinding(script), 1));
} else if (id == QLatin1String("type")) {
property.setTypeName(readStringBinding(script));
} else if (id == QLatin1String("isPointer")) {
@@ -425,8 +433,10 @@ void QQmlJSTypeDescriptionReader::readProperty(UiObjectDefinition *ast, const QQ
property.setPrivateClass(readStringBinding(script));
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only type, name, revision, isPointer, isTypeConstant, isReadonly, isRequired, "
- "isFinal, isList, bindable, read, write, isPropertyConstant, reset, notify, index, and "
+ tr("Expected only type, name, lineNumber, revision, isPointer, "
+ "isTypeConstant, isReadonly, isRequired, "
+ "isFinal, isList, bindable, read, write, isPropertyConstant, reset, "
+ "notify, index, and "
"privateClass and script bindings."));
}
}
@@ -467,9 +477,12 @@ void QQmlJSTypeDescriptionReader::readEnum(UiObjectDefinition *ast, const QQmlJS
metaEnum.setIsScoped(readBoolBinding(script));
} else if (name == QLatin1String("type")) {
metaEnum.setTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("lineNumber")) {
+ metaEnum.setLineNumber(readIntBinding(script));
} else {
addWarning(script->firstSourceLocation(),
- tr("Expected only name, alias, isFlag, values, isScoped, or type."));
+ tr("Expected only name, alias, isFlag, values, isScoped, type, or "
+ "lineNumber."));
}
}
diff --git a/src/qmlformat/qqmlformatoptions.cpp b/src/qmlformat/qqmlformatoptions.cpp
index 76b919fee4..d4b95819c3 100644
--- a/src/qmlformat/qqmlformatoptions.cpp
+++ b/src/qmlformat/qqmlformatoptions.cpp
@@ -188,6 +188,20 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
"rule"_L1, "always"_L1);
parser.addOption(semicolonRuleOption);
+ QCommandLineOption dryrunOption(
+ QStringList() << "dry-run"_L1,
+ QStringLiteral("Prints the settings file that would be used for this instance."
+ "This is useful to see what settings would be used "
+ "without actually performing anything."));
+ parser.addOption(dryrunOption);
+
+ QCommandLineOption settingsOption(
+ { "s"_L1, "settings"_L1 },
+ QStringLiteral("Use the specified .qmlformat.ini file as the only configuration source."
+ "Overrides any per-directory configuration lookup."),
+ "file"_L1);
+ parser.addOption(settingsOption);
+
parser.addPositionalArgument("filenames"_L1, "files to be processed by qmlformat"_L1);
parser.process(args);
@@ -250,6 +264,7 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
}
}
+ options.setDryRun(parser.isSet(dryrunOption));
options.setIsVerbose(parser.isSet("verbose"_L1));
options.setIsInplace(parser.isSet("inplace"_L1));
options.setForceEnabled(parser.isSet("force"_L1));
@@ -285,6 +300,20 @@ QQmlFormatOptions QQmlFormatOptions::buildCommandLineOptions(const QStringList &
options.setNewline(QQmlFormatOptions::parseEndings(parser.value("newline"_L1)));
}
+ if (parser.isSet(settingsOption)) {
+ options.mark(Settings::SettingsFile);
+ const auto value = parser.value(settingsOption);
+ if (value.isEmpty()) {
+ options.addError("Error: No settings file specified for option -s."_L1);
+ return options;
+ }
+ if (!QFile::exists(value)) {
+ options.addError("Error: Could not find file \""_L1 + value + "\"."_L1);
+ return options;
+ }
+ options.setSettingsFile(value);
+ }
+
if (parser.isSet(semicolonRuleOption)) {
options.mark(Settings::SemicolonRule);
const auto value = parser.value(semicolonRuleOption);
@@ -322,7 +351,8 @@ QQmlFormatOptions QQmlFormatOptions::optionsForFile(const QString &fileName,
if (hasFiles)
perFileOptions.setIsInplace(true);
- if (!ignoreSettingsEnabled() && settings->search(fileName).isValid())
+ if (!ignoreSettingsEnabled()
+ && settings->search(fileName, { m_settingsFile, m_verbose }).isValid())
perFileOptions.applySettings(*settings);
return perFileOptions;
diff --git a/src/qmlformat/qqmlformatoptions_p.h b/src/qmlformat/qqmlformatoptions_p.h
index 011ad4f9fa..a9f9e98ea3 100644
--- a/src/qmlformat/qqmlformatoptions_p.h
+++ b/src/qmlformat/qqmlformatoptions_p.h
@@ -126,6 +126,10 @@ public:
bool indentWidthSet() const { return m_indentWidthSet; }
void setIndentWidthSet(bool newIndentWidthSet) { m_indentWidthSet = newIndentWidthSet; }
+ bool dryRun() const { return m_dryRun; }
+ void setDryRun(bool newDryRun) { m_dryRun = newDryRun; }
+ QString settingsFile() const { return m_settingsFile; }
+ void setSettingsFile(const QString &newSettingsFile) { m_settingsFile = newSettingsFile; }
QStringList errors() const { return m_errors; }
void addError(const QString &newError) { m_errors.append(newError); };
@@ -145,6 +149,7 @@ public:
FunctionsSpacing,
SortImports,
SemicolonRule,
+ SettingsFile,
SettingsCount
};
@@ -171,6 +176,8 @@ private:
bool m_writeDefaultSettings = false;
bool m_indentWidthSet = false;
std::bitset<SettingsCount> m_settingBits;
+ bool m_dryRun = false;
+ QString m_settingsFile;
};
QT_END_NAMESPACE
diff --git a/src/qmlls/documentSymbolSupport/qqmldocumentsymbolsupport.cpp b/src/qmlls/documentSymbolSupport/qqmldocumentsymbolsupport.cpp
index 68cc8ff973..57e474f652 100644
--- a/src/qmlls/documentSymbolSupport/qqmldocumentsymbolsupport.cpp
+++ b/src/qmlls/documentSymbolSupport/qqmldocumentsymbolsupport.cpp
@@ -37,7 +37,7 @@ void QQmlDocumentSymbolSupport::process(QQmlDocumentSymbolSupport::RequestPointe
const auto qmlFileItem = doc.snapshot.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely);
QList<QLspSpecification::DocumentSymbol> results;
ResponseScopeGuard guard(results, request->m_response);
- if (!qmlFileItem)
+ if (qmlFileItem.internalKind() != QQmlJS::Dom::DomType::QmlFile)
return;
results = DocumentSymbolUtils::assembleSymbolsForQmlFile(qmlFileItem);
DocumentSymbolUtils::reorganizeForOutlineView(results);
diff --git a/src/qmlls/qmllsmain.cpp b/src/qmlls/qmllsmain.cpp
index bb6d86b075..9cf5ebc9fa 100644
--- a/src/qmlls/qmllsmain.cpp
+++ b/src/qmlls/qmllsmain.cpp
@@ -301,6 +301,7 @@ int qmllsMain(int argv, char *argc[])
if (parser.isSet(writeDefaultsOption)) {
return settings.writeDefaults() ? 0 : 1;
}
+
if (parser.isSet(logFileOption)) {
QString fileName = parser.value(logFileOption);
qInfo() << "will log to" << fileName;
@@ -319,8 +320,6 @@ int qmllsMain(int argv, char *argc[])
logFile->flush();
});
}
- if (parser.isSet(verboseOption))
- QLoggingCategory::setFilterRules("qt.languageserver*.debug=true\n"_L1);
if (parser.isSet(waitOption)) {
int waitSeconds = parser.value(waitOption).toInt();
if (waitSeconds > 0)
@@ -337,6 +336,10 @@ int qmllsMain(int argv, char *argc[])
},
(parser.isSet(ignoreSettings) ? nullptr : &settings));
+ if (parser.isSet(verboseOption)) {
+ QLoggingCategory::setFilterRules("qt.languageserver*.debug=true\n"_L1);
+ qmlServer.codeModelManager()->setVerbose(true);
+ }
if (parser.isSet(docDir))
qmlServer.codeModelManager()->setDocumentationRootPath(
QString::fromUtf8(parser.value(docDir).toUtf8()));
diff --git a/src/qmlls/qqmlcodemodel.cpp b/src/qmlls/qqmlcodemodel.cpp
index 76b7aead36..1720719eea 100644
--- a/src/qmlls/qqmlcodemodel.cpp
+++ b/src/qmlls/qqmlcodemodel.cpp
@@ -277,7 +277,7 @@ void QQmlCodeModel::initializeCMakeStatus(const QString &pathForSettings)
{
if (m_settings) {
const QString cmakeCalls = u"no-cmake-calls"_s;
- m_settings->search(pathForSettings);
+ m_settings->search(pathForSettings, { QString(), m_verbose });
if (m_settings->isSet(cmakeCalls) && m_settings->value(cmakeCalls).toBool()) {
qWarning() << "Disabling CMake calls via .qmlls.ini setting.";
m_cmakeStatus = DoesNotHaveCMake;
@@ -553,7 +553,8 @@ QStringList QQmlCodeModel::importPathsForUrl(const QByteArray &url)
QStringList result = importPaths();
const QString importPaths = u"importPaths"_s;
- if (m_settings && m_settings->search(fileName).isValid() && m_settings->isSet(importPaths)) {
+ if (m_settings && m_settings->search(fileName, { QString(), m_verbose }).isValid()
+ && m_settings->isSet(importPaths)) {
result.append(m_settings->value(importPaths).toString().split(QDir::listSeparator()));
}
@@ -628,7 +629,7 @@ QStringList QQmlCodeModel::buildPathsForFileUrl(const QByteArray &url)
// look in the settings.
// This is the one that is passed via the .qmlls.ini file.
if (buildPaths.isEmpty() && m_settings) {
- m_settings->search(path);
+ m_settings->search(path, { QString(), m_verbose });
QString buildDir = QStringLiteral(u"buildDir");
if (m_settings->isSet(buildDir))
buildPaths += m_settings->value(buildDir).toString().split(QDir::listSeparator(),
diff --git a/src/qmlls/qqmlcodemodel_p.h b/src/qmlls/qqmlcodemodel_p.h
index 1df8201f16..3f05286d69 100644
--- a/src/qmlls/qqmlcodemodel_p.h
+++ b/src/qmlls/qqmlcodemodel_p.h
@@ -135,6 +135,9 @@ public:
QSet<QString> ignoreForWatching() const { return m_ignoreForWatching; }
HelpManager *helpManager() { return &m_helpManager; }
+ void setVerbose(bool verbose) { m_verbose = verbose; }
+ bool verbose() const { return m_verbose; }
+
Q_SIGNALS:
void updatedSnapshot(const QByteArray &url);
void documentationRootPathChanged(const QString &path);
@@ -174,6 +177,7 @@ private:
QString m_documentationRootPath;
QSet<QString> m_ignoreForWatching;
HelpManager m_helpManager;
+ bool m_verbose = false;
private slots:
void onCppFileChanged(const QString &);
};
diff --git a/src/qmlls/qqmlcodemodelmanager.cpp b/src/qmlls/qqmlcodemodelmanager.cpp
index 23a7d49d68..0e48ffd9eb 100644
--- a/src/qmlls/qqmlcodemodelmanager.cpp
+++ b/src/qmlls/qqmlcodemodelmanager.cpp
@@ -242,6 +242,13 @@ void QQmlCodeModelManager::setDocumentationRootPath(const QString &path)
ws.codeModel->setDocumentationRootPath(path);
}
+void QQmlCodeModelManager::setVerbose(bool verbose)
+{
+ m_verbose = verbose;
+ for (const auto &ws : m_workspaces)
+ ws.codeModel->setVerbose(verbose);
+}
+
void QQmlCodeModelManager::setBuildPathsForRootUrl(const QByteArray &url, const QStringList &paths)
{
m_buildInformation.loadSettingsFrom(paths);
diff --git a/src/qmlls/qqmlcodemodelmanager_p.h b/src/qmlls/qqmlcodemodelmanager_p.h
index 3ad14ae233..06ac1f7a4e 100644
--- a/src/qmlls/qqmlcodemodelmanager_p.h
+++ b/src/qmlls/qqmlcodemodelmanager_p.h
@@ -71,6 +71,8 @@ public:
void setDocumentationRootPath(const QString &path);
HelpManager *helpManagerForUrl(const QByteArray &);
+ void setVerbose(bool verbose);
+
protected:
using Workspaces = std::vector<QQmlWorkspace>;
using WorkspaceIterator = Workspaces::const_iterator;
@@ -96,6 +98,7 @@ protected:
QStringList m_defaultImportPaths;
bool m_defaultDisableCMakeCalls = false;
QString m_defaultDocumentationRootPath;
+ bool m_verbose = false;
Q_SIGNALS:
void updatedSnapshot(const QByteArray &url);
diff --git a/src/qmlls/qqmllshelputils.cpp b/src/qmlls/qqmllshelputils.cpp
index 54469e9b71..d6fcf17673 100644
--- a/src/qmlls/qqmllshelputils.cpp
+++ b/src/qmlls/qqmllshelputils.cpp
@@ -111,6 +111,7 @@ HelpManager::extractDocumentationForIdentifiers(const DomItem &item,
}
case QQmlLSUtils::SingletonIdentifier:
case QQmlLSUtils::AttachedTypeIdentifier:
+ case QQmlLSUtils::AttachedTypeIdentifierInBindingTarget:
case QQmlLSUtils::QmlComponentIdentifier: {
const auto &keyword = item.field(Fields::identifier).value().toString();
// The keyword is a qmlobject. Keyword search should be sufficient.
diff --git a/src/qmlls/qqmllsutils.cpp b/src/qmlls/qqmllsutils.cpp
index b5f21367de..3f39074034 100644
--- a/src/qmlls/qqmllsutils.cpp
+++ b/src/qmlls/qqmllsutils.cpp
@@ -1184,7 +1184,7 @@ propertyBindingFromReferrerScope(const QQmlJSScope::ConstPtr &referrerScope, con
}
const auto typeIdentifier =
- bindingIsAttached ? AttachedTypeIdentifier : GroupedPropertyIdentifier;
+ bindingIsAttached ? AttachedTypeIdentifierInBindingTarget : GroupedPropertyIdentifier;
const auto getScope = [bindingIsAttached, binding]() -> QQmlJSScope::ConstPtr {
if (bindingIsAttached)
@@ -1309,8 +1309,11 @@ resolveTypeName(const std::shared_ptr<QQmlJSTypeResolver> &resolver, const QStri
if (fieldMemberAccessName.isEmpty() || !fieldMemberAccessName.front().isLower())
return ExpressionType{ name, scope, QmlComponentIdentifier };
- return ExpressionType{ name, options == ResolveOwnerType ? scope : scope->attachedType(),
- IdentifierType::AttachedTypeIdentifier };
+ if (scope->attachedType()) {
+ return ExpressionType{ name, options == ResolveOwnerType ? scope : scope->attachedType(),
+ IdentifierType::AttachedTypeIdentifier };
+ }
+ return {};
}
static std::optional<ExpressionType> resolveFieldMemberExpressionType(const DomItem &item,
@@ -1358,9 +1361,11 @@ static std::optional<ExpressionType> resolveFieldMemberExpressionType(const DomI
// Enumerations should live under the root element scope of the file that defines the enum,
// therefore use the DomItem to find the root element of the qml file instead of directly
// using owner->semanticScope.
- if (const auto scope = item.goToFile(owner->semanticScope->filePath())
- .rootQmlObject(GoTo::MostLikely)
- .semanticScope()) {
+ if (const auto scope = owner->semanticScope->isComposite()
+ ? item.goToFile(owner->semanticScope->filePath())
+ .rootQmlObject(GoTo::MostLikely)
+ .semanticScope()
+ : owner->semanticScope) {
if (scope->hasEnumerationKey(name)) {
return ExpressionType{ name, scope, EnumeratorValueIdentifier };
}
@@ -1906,6 +1911,43 @@ DomItem sourceLocationToDomItem(const DomItem &file, const QQmlJS::SourceLocatio
}
static std::optional<Location>
+findEnumDefinitionOf(const DomItem &file, QQmlJS::SourceLocation location, const QString &name)
+{
+ const DomItem enumeration = [&file, &location, &name]() -> DomItem {
+ const DomItem enumerations = QQmlLSUtils::sourceLocationToDomItem(file, location)
+ .qmlObject()
+ .component()
+ .field(Fields::enumerations);
+ const QSet<QString> enumerationNames = enumerations.keys();
+ for (const QString &enumName : enumerationNames) {
+ const DomItem currentKey = enumerations.key(enumName).index(0);
+ if (enumName == name)
+ return currentKey;
+ const DomItem values = currentKey.field(Fields::values);
+ for (int i = 0, end = values.size(); i < end; ++i) {
+ const DomItem currentValue = values.index(i);
+ if (currentValue.field(Fields::name).value().toStringView() == name)
+ return currentValue;
+ }
+ }
+ return {};
+ }();
+
+ auto fileLocation = FileLocations::treeOf(enumeration);
+
+ if (!fileLocation)
+ return {};
+
+ auto regions = fileLocation->info().regions;
+
+ if (auto it = regions.constFind(IdentifierRegion); it != regions.constEnd()) {
+ return Location::tryFrom(enumeration.canonicalFilePath(), *it, file);
+ }
+
+ return {};
+}
+
+static std::optional<Location>
findMethodDefinitionOf(const DomItem &file, QQmlJS::SourceLocation location, const QString &name)
{
DomItem owner = QQmlLSUtils::sourceLocationToDomItem(file, location).qmlObject();
@@ -1943,23 +1985,36 @@ findPropertyDefinitionOf(const DomItem &file, QQmlJS::SourceLocation propertyDef
return {};
}
-static std::optional<Location> fallbackLocationForCppType(const ExpressionType &type,
- const QStringList &headerLocations)
+static QQmlJS::SourceLocation sourceLocationOrDefault(const QQmlJS::SourceLocation &location)
{
- // fallback: construct location from the line number in the qmltypes file for C++ defined
- // elements:
- const QString filePath =
- findFilePathFromFileName(headerLocations, type.semanticScope->filePath());
- if (filePath.isEmpty())
+ return location.startLine == 0 ? QQmlJS::SourceLocation{ 0, 0, 1, 1 } : location;
+}
+
+static std::optional<Location> createCppTypeLocation(const QQmlJSScope::ConstPtr &type,
+ const QStringList &headerLocations,
+ const QQmlJS::SourceLocation &location)
+{
+ const QString filePath = findFilePathFromFileName(headerLocations, type->filePath());
+ if (filePath.isEmpty()) {
+ qCWarning(QQmlLSUtilsLog) << "Couldn't find the C++ file '%1'."_L1.arg(type->filePath());
return {};
+ }
- // note: Select the first line of the file if lineNumber is not set.
- const quint32 lineNumber = type.semanticScope->lineNumber();
- const QQmlJS::SourceLocation startLocation = lineNumber == 0
- ? QQmlJS::SourceLocation{ 0, 0, 1, 1 }
- : type.semanticScope->sourceLocation();
- const TextPosition endPosition{ static_cast<int>(startLocation.startLine) + 1, 1 };
- return Location{ filePath, startLocation, endPosition };
+ const TextPosition endPosition{ static_cast<int>(location.startLine) + 1, 1 };
+ return Location{ filePath, location, endPosition };
+}
+
+static std::optional<Location> findDefinitionOfType(const QQmlJSScope::ConstPtr &scope,
+ const DomItem &item,
+ const QStringList &headerDirectories)
+{
+ if (!scope)
+ return {};
+ if (scope->isComposite())
+ if (const auto result = Location::tryFrom(scope->filePath(), scope->sourceLocation(), item))
+ return result;
+ return createCppTypeLocation(scope, headerDirectories,
+ sourceLocationOrDefault(scope->sourceLocation()));
}
std::optional<Location> findDefinitionOf(const DomItem &item, const QStringList &headerDirectories)
@@ -1984,7 +2039,14 @@ std::optional<Location> findDefinitionOf(const DomItem &item, const QStringList
jsIdentifier->location, item);
}
+ case GroupedPropertyIdentifier:
case PropertyIdentifier: {
+ if (!resolvedExpression->semanticScope->isComposite()) {
+ return createCppTypeLocation(
+ resolvedExpression->semanticScope, headerDirectories,
+ resolvedExpression->semanticScope->property(*resolvedExpression->name)
+ .sourceLocation());
+ }
const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
const QQmlJS::SourceLocation ownerLocation =
resolvedExpression->semanticScope->sourceLocation();
@@ -1995,6 +2057,14 @@ std::optional<Location> findDefinitionOf(const DomItem &item, const QStringList
case SignalIdentifier:
case SignalHandlerIdentifier:
case MethodIdentifier: {
+ if (!resolvedExpression->semanticScope->isComposite()) {
+ const auto methods =
+ resolvedExpression->semanticScope->methods(*resolvedExpression->name);
+ if (methods.isEmpty())
+ return {};
+ return createCppTypeLocation(resolvedExpression->semanticScope, headerDirectories,
+ methods.front().sourceLocation());
+ }
const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
const QQmlJS::SourceLocation ownerLocation =
resolvedExpression->semanticScope->sourceLocation();
@@ -2019,14 +2089,14 @@ std::optional<Location> findDefinitionOf(const DomItem &item, const QStringList
FileLocations::treeOf(domId)->info().fullRegion, domId);
}
case AttachedTypeIdentifier:
- case QmlComponentIdentifier: {
- if (const auto result =
- Location::tryFrom(resolvedExpression->semanticScope->filePath(),
- resolvedExpression->semanticScope->sourceLocation(), item)) {
- return result;
- }
- return fallbackLocationForCppType(*resolvedExpression, headerDirectories);
- }
+ return findDefinitionOfType(resolvedExpression->semanticScope->attachedType(), item,
+ headerDirectories);
+ case AttachedTypeIdentifierInBindingTarget:
+ return findDefinitionOfType(resolvedExpression->semanticScope->baseType(), item,
+ headerDirectories);
+ case QmlComponentIdentifier:
+ case SingletonIdentifier:
+ return findDefinitionOfType(resolvedExpression->semanticScope, item, headerDirectories);
case QualifiedModuleIdentifier: {
const DomItem imports = item.fileObject().field(Fields::imports);
for (int i = 0; i < imports.indexes(); ++i) {
@@ -2040,17 +2110,27 @@ std::optional<Location> findDefinitionOf(const DomItem &item, const QStringList
}
return {};
}
- case SingletonIdentifier: {
- const QString filePath = resolvedExpression->semanticScope->filePath();
- const QQmlJS::SourceLocation location = resolvedExpression->semanticScope->sourceLocation();
- if (const auto result = Location::tryFrom(filePath, location, item))
- return result;
-
- return fallbackLocationForCppType(*resolvedExpression, headerDirectories);
- }
case EnumeratorIdentifier:
- case EnumeratorValueIdentifier:
- case GroupedPropertyIdentifier:
+ case EnumeratorValueIdentifier: {
+ if (!resolvedExpression->semanticScope->isComposite()) {
+ const auto enumerations = resolvedExpression->semanticScope->enumerations();
+ for (const auto &enumeration : enumerations) {
+ if (enumeration.hasKey(*resolvedExpression->name)
+ || enumeration.name() == *resolvedExpression->name) {
+ return createCppTypeLocation(
+ resolvedExpression->semanticScope, headerDirectories,
+ sourceLocationOrDefault(QQmlJS::SourceLocation::fromQSizeType(
+ 0, 0, enumeration.lineNumber(), 1)));
+ }
+ }
+ return {};
+ }
+ const DomItem ownerFile = item.goToFile(resolvedExpression->semanticScope->filePath());
+ const QQmlJS::SourceLocation ownerLocation =
+ resolvedExpression->semanticScope->sourceLocation();
+ return findEnumDefinitionOf(ownerFile, ownerLocation, *resolvedExpression->name);
+ }
+
case LambdaMethodIdentifier:
case NotAnIdentifier:
qCDebug(QQmlLSUtilsLog) << "QQmlLSUtils::findDefinitionOf was not implemented for type"
@@ -2126,6 +2206,7 @@ static QQmlJSScope::ConstPtr expressionTypeWithDefinition(const ExpressionType &
case EnumeratorIdentifier:
case EnumeratorValueIdentifier:
case AttachedTypeIdentifier:
+ case AttachedTypeIdentifierInBindingTarget:
case GroupedPropertyIdentifier:
case QmlComponentIdentifier:
case LambdaMethodIdentifier:
diff --git a/src/qmlls/qqmllsutils_p.h b/src/qmlls/qqmllsutils_p.h
index 47b0990709..e9c2da68f0 100644
--- a/src/qmlls/qqmllsutils_p.h
+++ b/src/qmlls/qqmllsutils_p.h
@@ -58,6 +58,9 @@ enum IdentifierType : quint8 {
EnumeratorIdentifier,
EnumeratorValueIdentifier,
AttachedTypeIdentifier,
+ // qqmljsimportvisitor creates extra attached type scopes for `Type.property: ...` compared to
+ // other usages of attached types, like for example `p: Type.property`
+ AttachedTypeIdentifierInBindingTarget,
GroupedPropertyIdentifier,
QmlComponentIdentifier,
QualifiedModuleIdentifier,
diff --git a/src/qmlls/qqmlsemantictokens.cpp b/src/qmlls/qqmlsemantictokens.cpp
index 5d9d868126..b076790418 100644
--- a/src/qmlls/qqmlsemantictokens.cpp
+++ b/src/qmlls/qqmlsemantictokens.cpp
@@ -712,6 +712,7 @@ void HighlightingVisitor::highlightBySemanticAnalysis(const DomItem &item, QQmlJ
m_highlights.addHighlight(loc, QmlHighlightKind::QmlEnumMember);
return;
case QQmlLSUtils::AttachedTypeIdentifier:
+ case QQmlLSUtils::AttachedTypeIdentifierInBindingTarget:
m_highlights.addHighlight(loc, QmlHighlightKind::QmlType);
return;
case QQmlLSUtils::GroupedPropertyIdentifier:
diff --git a/src/qmlmodels/sfpm/filters/qqmlfilterbase_p.h b/src/qmlmodels/sfpm/filters/qqmlfilterbase_p.h
index 8768097f4a..92acdd2ca8 100644
--- a/src/qmlmodels/sfpm/filters/qqmlfilterbase_p.h
+++ b/src/qmlmodels/sfpm/filters/qqmlfilterbase_p.h
@@ -33,6 +33,7 @@ class Q_QMLMODELS_EXPORT QQmlFilterBase: public QObject
Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged FINAL)
QML_NAMED_ELEMENT(FilterBase)
QML_UNCREATABLE("")
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlFilterBase(QQmlFilterBasePrivate *privObj, QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/filters/qqmlfiltercompositor_p.h b/src/qmlmodels/sfpm/filters/qqmlfiltercompositor_p.h
index b07fdc99b9..b3628b4152 100644
--- a/src/qmlmodels/sfpm/filters/qqmlfiltercompositor_p.h
+++ b/src/qmlmodels/sfpm/filters/qqmlfiltercompositor_p.h
@@ -25,8 +25,8 @@ class QQmlFilterCompositorPrivate;
class QQmlFilterCompositor: public QQmlFilterBase
{
Q_OBJECT
- QML_ELEMENT
- QML_UNCREATABLE("")
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlFilterCompositor(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/filters/qqmlfunctionfilter_p.h b/src/qmlmodels/sfpm/filters/qqmlfunctionfilter_p.h
index dd174e6ceb..044ffbdd4c 100644
--- a/src/qmlmodels/sfpm/filters/qqmlfunctionfilter_p.h
+++ b/src/qmlmodels/sfpm/filters/qqmlfunctionfilter_p.h
@@ -29,6 +29,7 @@ class Q_QMLMODELS_EXPORT QQmlFunctionFilter : public QQmlFilterBase, public QQml
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
QML_NAMED_ELEMENT(FunctionFilter)
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlFunctionFilter(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/filters/qqmlrolefilter_p.h b/src/qmlmodels/sfpm/filters/qqmlrolefilter_p.h
index e2778ba941..e88c62c17b 100644
--- a/src/qmlmodels/sfpm/filters/qqmlrolefilter_p.h
+++ b/src/qmlmodels/sfpm/filters/qqmlrolefilter_p.h
@@ -28,6 +28,7 @@ class Q_QMLMODELS_EXPORT QQmlRoleFilter : public QQmlFilterBase
Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged)
QML_NAMED_ELEMENT(RoleFilter)
QML_UNCREATABLE("")
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlRoleFilter(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/filters/qqmlvaluefilter_p.h b/src/qmlmodels/sfpm/filters/qqmlvaluefilter_p.h
index ccffe1928b..7862ad6bec 100644
--- a/src/qmlmodels/sfpm/filters/qqmlvaluefilter_p.h
+++ b/src/qmlmodels/sfpm/filters/qqmlvaluefilter_p.h
@@ -27,6 +27,7 @@ class Q_QMLMODELS_EXPORT QQmlValueFilter : public QQmlRoleFilter
Q_OBJECT
Q_PROPERTY(QVariant value READ value WRITE setValue RESET resetValue NOTIFY valueChanged)
QML_NAMED_ELEMENT(ValueFilter)
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlValueFilter(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/sorters/qqmlfunctionsorter_p.h b/src/qmlmodels/sfpm/sorters/qqmlfunctionsorter_p.h
index a473093600..1b02a00d4b 100644
--- a/src/qmlmodels/sfpm/sorters/qqmlfunctionsorter_p.h
+++ b/src/qmlmodels/sfpm/sorters/qqmlfunctionsorter_p.h
@@ -27,6 +27,7 @@ class Q_QMLMODELS_EXPORT QQmlFunctionSorter : public QQmlSorterBase, public QQml
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
QML_NAMED_ELEMENT(FunctionSorter)
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlFunctionSorter(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/sorters/qqmlrolesorter_p.h b/src/qmlmodels/sfpm/sorters/qqmlrolesorter_p.h
index 0d2c0d16ff..1c22a720b1 100644
--- a/src/qmlmodels/sfpm/sorters/qqmlrolesorter_p.h
+++ b/src/qmlmodels/sfpm/sorters/qqmlrolesorter_p.h
@@ -27,6 +27,7 @@ class Q_QMLMODELS_EXPORT QQmlRoleSorter : public QQmlSorterBase
Q_OBJECT
Q_PROPERTY(QString roleName READ roleName WRITE setRoleName NOTIFY roleNameChanged)
QML_NAMED_ELEMENT(RoleSorter)
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlRoleSorter(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/sorters/qqmlsorterbase_p.h b/src/qmlmodels/sfpm/sorters/qqmlsorterbase_p.h
index 52a95a26c1..64d2bcb38c 100644
--- a/src/qmlmodels/sfpm/sorters/qqmlsorterbase_p.h
+++ b/src/qmlmodels/sfpm/sorters/qqmlsorterbase_p.h
@@ -33,8 +33,9 @@ class Q_QMLMODELS_EXPORT QQmlSorterBase : public QObject
Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged FINAL)
Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged FINAL)
Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged FINAL)
- QML_ELEMENT
+ QML_NAMED_ELEMENT(SorterBase)
QML_UNCREATABLE("")
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlSorterBase(QQmlSorterBasePrivate *privObj, QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/sorters/qqmlsortercompositor_p.h b/src/qmlmodels/sfpm/sorters/qqmlsortercompositor_p.h
index f99b06dd0f..a8eeb2472d 100644
--- a/src/qmlmodels/sfpm/sorters/qqmlsortercompositor_p.h
+++ b/src/qmlmodels/sfpm/sorters/qqmlsortercompositor_p.h
@@ -25,8 +25,8 @@ class QQmlSorterCompositorPrivate;
class QQmlSorterCompositor: public QQmlSorterBase
{
Q_OBJECT
- QML_ELEMENT
- QML_UNCREATABLE("")
+ QML_ANONYMOUS
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlSorterCompositor(QObject *parent = nullptr);
diff --git a/src/qmlmodels/sfpm/sorters/qqmlstringsorter_p.h b/src/qmlmodels/sfpm/sorters/qqmlstringsorter_p.h
index c4dd3ec0d9..77bc00eebc 100644
--- a/src/qmlmodels/sfpm/sorters/qqmlstringsorter_p.h
+++ b/src/qmlmodels/sfpm/sorters/qqmlstringsorter_p.h
@@ -31,6 +31,7 @@ class Q_QMLMODELS_EXPORT QQmlStringSorter : public QQmlRoleSorter
Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged)
Q_PROPERTY(bool numericMode READ numericMode WRITE setNumericMode NOTIFY numericModeChanged)
QML_NAMED_ELEMENT(StringSorter)
+ QML_ADDED_IN_VERSION(6, 10)
public:
explicit QQmlStringSorter(QObject *parent = nullptr);
diff --git a/src/qmltoolingsettings/qqmltoolingsettings.cpp b/src/qmltoolingsettings/qqmltoolingsettings.cpp
index 9d2f33d1f1..d953aacc6d 100644
--- a/src/qmltoolingsettings/qqmltoolingsettings.cpp
+++ b/src/qmltoolingsettings/qqmltoolingsettings.cpp
@@ -7,6 +7,7 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qset.h>
+#include <QtCore/qtextstream.h>
#if QT_CONFIG(settings)
#include <QtCore/qsettings.h>
#endif
@@ -14,7 +15,7 @@
using namespace Qt::StringLiterals;
-void QQmlToolingSettings::addOption(const QString &name, QVariant defaultValue)
+void QQmlToolingSettings::addOption(const QString &name, const QVariant &defaultValue)
{
if (defaultValue.isValid()) {
m_values[name] = defaultValue;
@@ -109,8 +110,12 @@ QQmlToolingSettings::Searcher::searchDefaultLocation(const QSet<QString> *visite
}
QQmlToolingSettings::SearchResult
-QQmlToolingSettings::Searcher::searchDirectoryHierarchy(QSet<QString> *visitedDirs, QDir dir)
+QQmlToolingSettings::Searcher::searchDirectoryHierarchy(
+ QSet<QString> *visitedDirs, const QString &path)
{
+ const QFileInfo fileInfo(path);
+ QDir dir(fileInfo.isDir() ? path : fileInfo.dir());
+
while (dir.exists() && dir.isReadable()) {
const QString dirPath = dir.absolutePath();
@@ -143,12 +148,10 @@ QQmlToolingSettings::Searcher::searchDirectoryHierarchy(QSet<QString> *visitedDi
QQmlToolingSettings::SearchResult QQmlToolingSettings::Searcher::search(const QString &path)
{
#if QT_CONFIG(settings)
- QFileInfo fileInfo(path);
- QDir dir(fileInfo.isDir() ? path : fileInfo.dir());
QSet<QString> visitedDirs;
// Try to find settings in directory hierarchy
- if (const SearchResult result = searchDirectoryHierarchy(&visitedDirs, dir); result.isValid())
+ if (const SearchResult result = searchDirectoryHierarchy(&visitedDirs, path); result.isValid())
return result;
// If we didn't find the settings file in the current directory or any parent directories,
@@ -161,8 +164,20 @@ QQmlToolingSettings::SearchResult QQmlToolingSettings::Searcher::search(const QS
return SearchResult();
}
-QQmlToolingSettings::SearchResult QQmlToolingSettings::search(const QString &path)
+QQmlToolingSettings::SearchResult QQmlToolingSettings::search(
+ const QString &path, const SearchOptions &options)
{
+ const auto maybeReport = qScopeGuard([&]() {
+ if (options.verbose)
+ reportConfigForFiles({ path });
+ });
+
+ // If a specific settings file is provided, read it directly
+ if (!options.settingsFileName.isEmpty()) {
+ QFileInfo fileInfo(options.settingsFileName);
+ return fileInfo.exists() ? read(fileInfo.absoluteFilePath()) : SearchResult();
+ }
+
if (const SearchResult result = m_searcher.search(path); result.isValid())
return read(result.iniFilePath);
@@ -184,3 +199,48 @@ bool QQmlToolingSettings::isSet(const QString &name) const
// Unset is encoded as an empty string
return !(variant.canConvert(QMetaType(QMetaType::QString)) && variant.toString().isEmpty());
}
+
+bool QQmlToolingSettings::reportConfigForFiles(const QStringList &files)
+{
+ constexpr int maxAllowedFileLength = 255;
+ constexpr int minAllowedFileLength = 40;
+ bool headerPrinted = false;
+ auto lengthForFile = [maxAllowedFileLength](const QString &file) {
+ return std::min(int(file.length()), maxAllowedFileLength);
+ };
+
+ int maxFileLength =
+ std::accumulate(files.begin(), files.end(), 0, [&](int acc, const QString &file) {
+ return std::max(acc, lengthForFile(file));
+ });
+
+ if (maxFileLength < minAllowedFileLength)
+ maxFileLength = minAllowedFileLength;
+
+ for (const auto &file : files) {
+ if (file.isEmpty()) {
+ qWarning().noquote() << "Error: Could not find file" << file;
+ return false;
+ }
+
+ QString displayFile = file;
+ if (displayFile.length() > maxAllowedFileLength) {
+ displayFile = "..." + displayFile.right(maxAllowedFileLength - 3);
+ }
+
+ const auto result = search(file);
+
+ if (!headerPrinted) {
+ QString header =
+ QStringLiteral("%1 | %2").arg("File", -maxFileLength).arg("Settings File");
+ qWarning().noquote() << header;
+ qWarning().noquote() << QString(header.length(), u'-');
+ headerPrinted = true;
+ }
+ QString line =
+ QStringLiteral("%1 | %2").arg(displayFile, -maxFileLength).arg(result.iniFilePath);
+ qWarning().noquote() << line;
+ }
+
+ return true;
+}
diff --git a/src/qmltoolingsettings/qqmltoolingsettings_p.h b/src/qmltoolingsettings/qqmltoolingsettings_p.h
index c2142e1790..be3c2ad570 100644
--- a/src/qmltoolingsettings/qqmltoolingsettings_p.h
+++ b/src/qmltoolingsettings/qqmltoolingsettings_p.h
@@ -27,6 +27,12 @@ QT_BEGIN_NAMESPACE
class QQmlToolingSettings
{
public:
+ QQmlToolingSettings(const QString &toolName);
+ struct SearchOptions
+ {
+ QString settingsFileName;
+ bool verbose;
+ };
struct SearchResult
{
enum class ResultType { Found, NotFound };
@@ -49,23 +55,22 @@ public:
private:
SearchResult searchDefaultLocation(const QSet<QString> *visitedDirs);
SearchResult searchCurrentDirInCache(const QString &dirPath);
- SearchResult searchDirectoryHierarchy(QSet<QString> *visitedDir, QDir dir);
+ SearchResult searchDirectoryHierarchy(QSet<QString> *visitedDir, const QString &path);
const QString m_localSettingsFile;
const QString m_globalSettingsFile;
QHash<QString, QString> m_seenDirectories;
};
- QQmlToolingSettings(const QString &toolName);
-
- void addOption(const QString &name, const QVariant defaultValue = QVariant());
-
+ void addOption(const QString &name, const QVariant &defaultValue = QVariant());
+ SearchResult search(const QString &path, const SearchOptions &options = {});
bool writeDefaults() const;
- SearchResult search(const QString &path);
QVariant value(const QString &name) const;
bool isSet(const QString &name) const;
+ bool reportConfigForFiles(const QStringList &files);
+
private:
QString m_currentSettingsPath;
QVariantHash m_values;
@@ -79,7 +84,7 @@ class QQmlToolingSharedSettings : private QQmlToolingSettings
public:
QQmlToolingSharedSettings(const QString &toolName) : QQmlToolingSettings(toolName) { }
- void addOption(const QString &name, const QVariant defaultValue = QVariant())
+ void addOption(const QString &name, const QVariant &defaultValue = QVariant())
{
QMutexLocker lock(&m_mutex);
QQmlToolingSettings::addOption(name, defaultValue);
@@ -91,10 +96,10 @@ public:
return QQmlToolingSettings::writeDefaults();
}
- SearchResult search(const QString &path)
+ SearchResult search(const QString &path, const SearchOptions &options = {})
{
QMutexLocker lock(&m_mutex);
- return QQmlToolingSettings::search(path);
+ return QQmlToolingSettings::search(path, options);
}
QVariant value(const QString &name) const
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
index 967646a5d5..36f7ed22ce 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor.cpp
@@ -756,6 +756,7 @@ Property::Property(const QCborMap &cbor)
, bindable(cbor[S_BINDABLE].toStringView())
, privateClass(cbor[S_PRIVATE_CLASS].toStringView())
, index(cbor[S_INDEX].toInteger(-1))
+ , lineNumber(cbor[S_LINENUMBER].toInteger(0))
, revision(getRevision(cbor))
, isFinal(cbor[S_FINAL].toBool())
, isConstant(cbor[S_CONSTANT].toBool())
@@ -773,6 +774,7 @@ Method::Method(const QCborMap &cbor, bool isConstructor)
: name(cbor[S_NAME].toStringView())
, returnType(cbor[S_RETURN_TYPE].toStringView())
, index(cbor[S_INDEX].toInteger(InvalidIndex))
+ , lineNumber(cbor[S_LINENUMBER].toInteger(0))
, revision(getRevision(cbor))
, access(getAccess(cbor))
, isCloned(cbor[S_IS_CLONED].toBool())
@@ -797,6 +799,7 @@ Enum::Enum(const QCborMap &cbor)
: name(cbor[S_NAME].toStringView())
, alias(cbor[S_ALIAS].toStringView())
, type(cbor[S_TYPE].toStringView())
+ , lineNumber(cbor[S_LINENUMBER].toInteger(0))
, isFlag(cbor[S_IS_FLAG].toBool())
, isClass(cbor[S_IS_CLASS].toBool())
{
diff --git a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
index 827e4c1e60..2bab7d522b 100644
--- a/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
+++ b/src/qmltyperegistrar/qmetatypesjsonprocessor_p.h
@@ -84,6 +84,7 @@ struct Property
QAnyStringView privateClass;
int index = -1;
+ int lineNumber = 0;
QTypeRevision revision;
@@ -117,6 +118,7 @@ struct Method
QAnyStringView returnType;
int index = InvalidIndex;
+ int lineNumber = 0;
QTypeRevision revision;
@@ -141,6 +143,7 @@ struct Enum
QList<QAnyStringView> values;
+ int lineNumber = 0;
bool isFlag = false;
bool isClass = false;
};
diff --git a/src/qmltyperegistrar/qqmltypescreator.cpp b/src/qmltyperegistrar/qqmltypescreator.cpp
index 93be96d69e..14d6e2c1a3 100644
--- a/src/qmltyperegistrar/qqmltypescreator.cpp
+++ b/src/qmltyperegistrar/qqmltypescreator.cpp
@@ -216,6 +216,10 @@ void QmlTypesCreator::writeProperties(const Property::Container &properties)
if (index != -1) {
m_qml.writeNumberBinding(S_INDEX, index);
}
+ const auto lineNumber = obj.lineNumber;
+ if (lineNumber != 0)
+ m_qml.writeNumberBinding(S_LINE_NUMBER, obj.lineNumber);
+
const auto privateClass = obj.privateClass;
if (!privateClass.isEmpty()) {
m_qml.writeStringBinding(
@@ -260,6 +264,9 @@ void QmlTypesCreator::writeMethods(const Method::Container &methods, QLatin1Stri
m_qml.writeBooleanBinding(S_IS_JAVASCRIPT_FUNCTION, true);
if (obj.isConst)
m_qml.writeBooleanBinding(S_IS_METHOD_CONSTANT, true);
+ const auto lineNumber = obj.lineNumber;
+ if (lineNumber != 0)
+ m_qml.writeNumberBinding(S_LINE_NUMBER, obj.lineNumber);
const Argument::Container &arguments = obj.arguments;
for (qsizetype i = 0, end = arguments.size(); i != end; ++i) {
@@ -287,6 +294,9 @@ void QmlTypesCreator::writeEnums(const Enum::Container &enums)
if (obj.isClass)
m_qml.writeBooleanBinding(S_IS_SCOPED, true);
writeType(obj.type);
+ const auto lineNumber = obj.lineNumber;
+ if (lineNumber != 0)
+ m_qml.writeNumberBinding(S_LINE_NUMBER, obj.lineNumber);
m_qml.writeStringListBinding(S_VALUES, obj.values);
m_qml.writeEndObject();
}
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 30089cc05e..8a46ad3f91 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -210,6 +210,12 @@ QAccessibleQuickItem::QAccessibleQuickItem(QQuickItem *item)
{
}
+bool QAccessibleQuickItem::isValid() const
+{
+ return item() && !QQuickItemPrivate::get(item())->inDestructor;
+}
+
+
QWindow *QAccessibleQuickItem::window() const
{
QQuickWindow *window = item()->window();
@@ -717,7 +723,8 @@ void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t)
if (t == QAccessible::TextInterface) {
if (r == QAccessible::EditableText ||
- r == QAccessible::StaticText)
+ r == QAccessible::StaticText ||
+ r == QAccessible::Heading)
return static_cast<QAccessibleTextInterface*>(this);
}
diff --git a/src/quick/accessible/qaccessiblequickitem_p.h b/src/quick/accessible/qaccessiblequickitem_p.h
index b64251640e..9499bd63f9 100644
--- a/src/quick/accessible/qaccessiblequickitem_p.h
+++ b/src/quick/accessible/qaccessiblequickitem_p.h
@@ -31,6 +31,7 @@ class Q_QUICK_EXPORT QAccessibleQuickItem : public QAccessibleObject, public QAc
public:
QAccessibleQuickItem(QQuickItem *item);
+ bool isValid() const override;
QWindow *window() const override;
QRect rect() const override;
diff --git a/src/quick/doc/src/cmake/qt_target_qml_from_svg.qdoc b/src/quick/doc/src/cmake/qt_target_qml_from_svg.qdoc
new file mode 100644
index 0000000000..d9d5e1b80f
--- /dev/null
+++ b/src/quick/doc/src/cmake/qt_target_qml_from_svg.qdoc
@@ -0,0 +1,65 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qt_target_qml_from_svg.html
+\ingroup cmake-commands-qtquick
+
+\title qt_target_qml_from_svg
+\keyword qt6_target_qml_from_svg
+
+\summary {Generates QML code based on an SVG file.}
+
+The command is defined in the \c QuickTools component of the \c Qt6 package, which can
+be loaded like this:
+
+\badcode
+find_package(Qt6 REQUIRED COMPONENTS QuickTools)
+\endcode
+
+\cmakecommandsince 6.11
+
+\section1 Synopsis
+
+\badcode
+qt_target_qml_from_svg(target
+ [CURVE_RENDERER]
+ [OPTIMIZE_PATHS]
+ [OUTLINE_STROKE_MODE]
+ [TYPE_NAME "MyShapeName"]
+ [COPYRIGHT_STATEMENT "Copyright © Company1"]
+ FILES file1.svg [file2.svg ...]
+ OUTPUTS File1.qml [File2.qml ...]
+ )
+\endcode
+
+\versionlessCMakeCommandsNote qt6_target_qml_from_svg()
+
+\section1 Description
+
+qt_target_qml_from_svg() creates the build steps to run \l{svgtoqml} on the list of SVG images in
+\c FILES. The names of the generated files should be specified in \c OUTPUTS. The length of this
+list must be the same as \c FILES, and the first element corresponds to the first element in
+\c FILES and so forth. The names provided in \c OUTPUTS will be the names of the QML types as they
+appear in the module. These can in turn be instantiated in the application code.
+
+The generated QML files will be added to the QML module of \c{target}. You must use
+\l{qt_add_qml_module()} to define a module for the \c{target} first.
+
+Optionally, a \c COPYRIGHT_STATEMENT argument can be provided to insert copyright information
+into the generated files.
+
+\note Certain characters must be escaped for command line use or you may see build errors on
+some platforms.
+
+\c TYPE_NAME is also optional, and can be used to replace all instances of the \l{Shape} type in
+the generated file with a custom type. This can be useful to make general customizations to all
+the shapes in the provided SVG files. The \c{TYPE_NAME} should refer to a QML type which is
+available in the \c{target} QML module.
+
+The options \c CURVE_RENDERER, \c OPTIMIZE_PATHS and \c OUTLINE_STROKE_MODE correspond to the
+\c{--curve-renderer}, \c{--optimize-paths} and \c{--outline-stroke-mode} in \l{svgtoqml}
+respectively.
+*/
+
+
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index fa0f94ccd4..ca5067c59a 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -49,6 +49,10 @@
# include <QtGui/qcursor.h>
#endif
+#if QT_CONFIG(accessibility)
+# include <private/qaccessiblecache_p.h>
+#endif
+
#include <QtCore/qpointer.h>
#include <algorithm>
@@ -2383,6 +2387,11 @@ QQuickItem::~QQuickItem()
Q_D(QQuickItem);
d->inDestructor = true;
+#if QT_CONFIG(accessibility)
+ if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing && QAccessible::isActive())
+ QAccessibleCache::instance()->sendObjectDestroyedEvent(this);
+#endif
+
if (d->windowRefCount > 1)
d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow().
if (d->parentItem)
@@ -5794,6 +5803,10 @@ bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const
HoverHandlers if the event is a QMouseEvent or QWheelEvent (they are visited
in QQuickDeliveryAgentPrivate::deliverHoverEventToItem()), and skip handlers
that are in QQuickPointerHandlerPrivate::deviceDeliveryTargets().
+ However if the event is a QTabletEvent, we do NOT skip delivery here:
+ this is the means by which HoverHandler can change the cursor when the
+ tablet stylus hovers over its parent item.
+
If \a avoidGrabbers is true, also skip delivery to any handler that
is exclusively or passively grabbing any point within \a event
(because delivery to grabbers is handled separately).
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index e7b1c5b954..8d1bd28da1 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -3599,20 +3599,16 @@ void Renderer::renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthP
}
}
-void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e, bool depthPostPass)
+void Renderer::setViewportAndScissors(QRhiCommandBuffer *cb, const Batch *batch)
{
- cb->setGraphicsPipeline(depthPostPass ? e->depthPostPassPs : e->ps);
-
if (!m_pstate.viewportSet) {
m_pstate.viewportSet = true;
cb->setViewport(m_pstate.viewport);
}
if (batch->clipState.type & ClipState::ScissorClip) {
- Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
m_pstate.scissorSet = true;
cb->setScissor(batch->clipState.scissor);
} else {
- Q_ASSERT(!e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesScissor));
// Regardless of the ps not using scissor, the scissor may need to be
// reset, depending on the backend. So set the viewport again, which in
// turn also sets the scissor on backends where a scissor rect is
@@ -3622,6 +3618,15 @@ void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, El
cb->setViewport(m_pstate.viewport);
}
}
+}
+
+
+void Renderer::setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e, bool depthPostPass)
+{
+ cb->setGraphicsPipeline(depthPostPass ? e->depthPostPassPs : e->ps);
+
+ setViewportAndScissors(cb, batch);
+
if (batch->clipState.type & ClipState::StencilClip) {
Q_ASSERT(e->ps->flags().testFlag(QRhiGraphicsPipeline::UsesStencilRef));
cb->setStencilRef(batch->clipState.stencilRef);
@@ -4168,6 +4173,7 @@ void Renderer::renderRhiRenderNode(const Batch *batch)
const QSGRenderNode::StateFlags changes = e->renderNode->changedStates();
QRhiCommandBuffer *cb = renderTarget().cb;
+ setViewportAndScissors(cb, batch);
const bool needsExternal = !e->renderNode->flags().testFlag(QSGRenderNode::NoExternalRendering);
if (needsExternal)
cb->beginExternal();
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index b51a2d523a..15cff4caec 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -827,6 +827,7 @@ private:
void renderMergedBatch(PreparedRenderBatch *renderBatch, bool depthPostPass = false);
bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
void renderUnmergedBatch(PreparedRenderBatch *renderBatch, bool depthPostPass = false);
+ void setViewportAndScissors(QRhiCommandBuffer *cb, const Batch *batch);
void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e, bool depthPostPass = false);
ClipState::ClipType updateStencilClip(const QSGClipNode *clip);
void updateClip(const QSGClipNode *clipList, const Batch *batch);
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 6a0286dbce..bc27593f2f 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -1661,8 +1661,14 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
Q_TRACE(QSG_polishItems_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync,
QQuickProfiler::SceneGraphPolishAndSyncPolish);
- Q_TRACE(QSG_wait_entry);
+ w = windowFor(window);
+ if (!w || !w->thread || !w->thread->window) {
+ qCDebug(QSG_LOG_RENDERLOOP, "- removed after polishing, abort");
+ return;
+ }
+
+ Q_TRACE(QSG_wait_entry);
w->updateDuringSync = false;
emit window->afterAnimating();
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
index db2b214a55..9f156602b8 100644
--- a/src/quick/util/qquickdeliveryagent.cpp
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -2042,7 +2042,26 @@ void QQuickDeliveryAgentPrivate::deliverPointerEvent(QPointerEvent *event)
if (!deliverPressOrReleaseEvent(event))
event->setAccepted(false);
}
- if (!allUpdatedPointsAccepted(event))
+
+ auto isHoveringMoveEvent = [](QPointerEvent *event) -> bool {
+ if (event->type() == QEvent::MouseMove) {
+ const auto *spe = static_cast<const QSinglePointEvent *>(event);
+ if (spe->button() == Qt::NoButton && spe->buttons() == Qt::NoButton)
+ return true;
+ }
+ return false;
+ };
+
+ /*
+ If some QEventPoints were not yet handled, deliver to existing grabbers,
+ and then non-grabbing pointer handlers.
+ But don't deliver stray mouse moves in which no buttons are pressed:
+ stray mouse moves risk deactivating handlers that don't expect them;
+ for mouse hover tracking, we rather use deliverHoverEvent().
+ But do deliver TabletMove events, in case there is a HoverHandler that
+ changes its cursorShape depending on stylus type.
+ */
+ if (!allUpdatedPointsAccepted(event) && !isHoveringMoveEvent(event))
deliverUpdatedPoints(event);
if (event->isEndEvent())
deliverPressOrReleaseEvent(event, true);
diff --git a/src/quick/util/qquickdeliveryagent_p_p.h b/src/quick/util/qquickdeliveryagent_p_p.h
index d77fc44bee..887504c458 100644
--- a/src/quick/util/qquickdeliveryagent_p_p.h
+++ b/src/quick/util/qquickdeliveryagent_p_p.h
@@ -149,6 +149,7 @@ public:
static bool isMouseEvent(const QPointerEvent *ev);
static bool isMouseOrWheelEvent(const QPointerEvent *ev);
static bool isHoverEvent(const QPointerEvent *ev);
+ static bool isHoveringMoveEvent(const QPointerEvent *ev);
static bool isTouchEvent(const QPointerEvent *ev);
static bool isTabletEvent(const QPointerEvent *ev);
static bool isEventFromMouseOrTouchpad(const QPointerEvent *ev);
diff --git a/src/quickcontrols/doc/src/includes/varying-delegate-heights-section.qdocinc b/src/quickcontrols/doc/src/includes/varying-delegate-heights-section.qdocinc
new file mode 100644
index 0000000000..a2ca1e6513
--- /dev/null
+++ b/src/quickcontrols/doc/src/includes/varying-delegate-heights-section.qdocinc
@@ -0,0 +1,8 @@
+//! [file]
+\section\1 Varying Delegate Sizes
+
+Variable delegate sizes can lead \2 to "jump around" as new delegates are
+loaded into the view. It is recommended to have equally-sized delegates for
+this reason. See \l {Variable Delegate Size and Section Labels} for more
+information.
+//! [file]
diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
index aeef892e6f..f644881dfc 100644
--- a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
+++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext.cpp
@@ -59,10 +59,7 @@ void QQuickMaterialPlaceholderText::setControlHasActiveFocus(bool controlHasActi
return;
m_controlHasActiveFocus = controlHasActiveFocus;
- if (m_controlHasActiveFocus)
- controlGotActiveFocus();
- else
- controlLostActiveFocus();
+ controlActiveFocusChanged();
emit controlHasActiveFocusChanged();
}
@@ -77,7 +74,7 @@ void QQuickMaterialPlaceholderText::setControlHasText(bool controlHasText)
return;
m_controlHasText = controlHasText;
- maybeSetFocusAnimationProgress();
+ updateFocusAnimation();
emit controlHasTextChanged();
}
@@ -185,7 +182,7 @@ void QQuickMaterialPlaceholderText::setControlImplicitBackgroundHeight(qreal con
return;
m_controlImplicitBackgroundHeight = controlImplicitBackgroundHeight;
- updateY();
+ updateFocusAnimation();
emit controlImplicitBackgroundHeightChanged();
}
@@ -210,7 +207,7 @@ void QQuickMaterialPlaceholderText::setControlHeight(qreal controlHeight)
return;
m_controlHeight = controlHeight;
- updateY();
+ updateFocusAnimation();
}
qreal QQuickMaterialPlaceholderText::verticalPadding() const
@@ -224,6 +221,7 @@ void QQuickMaterialPlaceholderText::setVerticalPadding(qreal verticalPadding)
return;
m_verticalPadding = verticalPadding;
+ updateFocusAnimation();
emit verticalPaddingChanged();
}
@@ -233,7 +231,7 @@ void QQuickMaterialPlaceholderText::setLeftPadding(int leftPadding)
return;
m_leftPadding = leftPadding;
- updateX();
+ updateFocusAnimation();
}
void QQuickMaterialPlaceholderText::setFloatingLeftPadding(int floatingLeftPadding)
@@ -242,7 +240,7 @@ void QQuickMaterialPlaceholderText::setFloatingLeftPadding(int floatingLeftPaddi
return;
m_floatingLeftPadding = floatingLeftPadding;
- updateX();
+ updateFocusAnimation();
}
void QQuickMaterialPlaceholderText::adjustTransformOrigin()
@@ -262,95 +260,64 @@ void QQuickMaterialPlaceholderText::adjustTransformOrigin()
}
}
-void QQuickMaterialPlaceholderText::controlGotActiveFocus()
+void QQuickMaterialPlaceholderText::controlActiveFocusChanged()
{
- if (m_focusOutAnimation) {
+ if (m_focusAnimation) {
// Focus changes can happen before the animations finish.
// In that case, stop the animation, which will eventually delete it.
// Until it's deleted, we clear the pointer so that our asserts don't fail
// for the wrong reason.
- m_focusOutAnimation->stop();
- m_focusOutAnimation.clear();
- }
-
- Q_ASSERT(!m_focusInAnimation);
- if (shouldAnimate()) {
- m_focusInAnimation = new QParallelAnimationGroup(this);
-
- QPropertyAnimation *yAnimation = new QPropertyAnimation(this, "y", this);
- yAnimation->setDuration(300);
- yAnimation->setStartValue(y());
- yAnimation->setEndValue(floatingTargetY());
- yAnimation->setEasingCurve(*animationEasingCurve);
- m_focusInAnimation->addAnimation(yAnimation);
-
- QPropertyAnimation *xAnimation = new QPropertyAnimation(this, "x", this);
- xAnimation->setDuration(300);
- xAnimation->setStartValue(x());
- xAnimation->setEndValue(floatingTargetX());
- xAnimation->setEasingCurve(*animationEasingCurve);
- m_focusInAnimation->addAnimation(xAnimation);
-
- auto *scaleAnimation = new QPropertyAnimation(this, "scale", this);
- scaleAnimation->setDuration(300);
- scaleAnimation->setStartValue(1);
- scaleAnimation->setEndValue(floatingScale);
- yAnimation->setEasingCurve(*animationEasingCurve);
- m_focusInAnimation->addAnimation(scaleAnimation);
-
- m_focusInAnimation->start(QAbstractAnimation::DeleteWhenStopped);
- } else {
- updateY();
- updateX();
+ m_focusAnimation->stop();
+ m_focusAnimation.clear();
}
+ updateFocusAnimation(true);
}
-void QQuickMaterialPlaceholderText::controlLostActiveFocus()
+void QQuickMaterialPlaceholderText::updateFocusAnimation(bool createIfNeeded)
{
- if (m_focusInAnimation) {
- m_focusInAnimation->stop();
- m_focusInAnimation.clear();
- }
+ if (shouldAnimate() && (m_focusAnimation || createIfNeeded)) {
+ int duration = 300;
+ if (m_focusAnimation) {
+ duration = m_focusAnimation->totalDuration() - m_focusAnimation->currentTime();
+ m_focusAnimation->stop();
+ m_focusAnimation.clear();
+ }
- Q_ASSERT(!m_focusOutAnimation);
- if (shouldAnimate()) {
- m_focusOutAnimation = new QParallelAnimationGroup(this);
+ m_focusAnimation = new QParallelAnimationGroup(this);
auto *yAnimation = new QPropertyAnimation(this, "y", this);
- yAnimation->setDuration(300);
+ yAnimation->setDuration(duration);
yAnimation->setStartValue(y());
- yAnimation->setEndValue(normalTargetY());
+ yAnimation->setEndValue(shouldFloat() ? floatingTargetY() : normalTargetY());
yAnimation->setEasingCurve(*animationEasingCurve);
- m_focusOutAnimation->addAnimation(yAnimation);
+ m_focusAnimation->addAnimation(yAnimation);
QPropertyAnimation *xAnimation = new QPropertyAnimation(this, "x", this);
- xAnimation->setDuration(300);
+ xAnimation->setDuration(duration);
xAnimation->setStartValue(x());
- xAnimation->setEndValue(normalTargetX());
+ xAnimation->setEndValue(shouldFloat() ? floatingTargetX() : normalTargetX());
xAnimation->setEasingCurve(*animationEasingCurve);
- m_focusOutAnimation->addAnimation(xAnimation);
+ m_focusAnimation->addAnimation(xAnimation);
auto *scaleAnimation = new QPropertyAnimation(this, "scale", this);
- scaleAnimation->setDuration(300);
- scaleAnimation->setStartValue(floatingScale);
- scaleAnimation->setEndValue(1);
+ scaleAnimation->setDuration(duration);
+ scaleAnimation->setStartValue(scale());
+ scaleAnimation->setEndValue(shouldFloat() ? floatingScale : 1.0);
yAnimation->setEasingCurve(*animationEasingCurve);
- m_focusOutAnimation->addAnimation(scaleAnimation);
+ m_focusAnimation->addAnimation(scaleAnimation);
- m_focusOutAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ m_focusAnimation->start(QAbstractAnimation::DeleteWhenStopped);
} else {
+ if (m_focusAnimation) {
+ m_focusAnimation->stop();
+ m_focusAnimation.clear();
+ }
updateY();
updateX();
+ setScale(shouldFloat() ? floatingScale : 1.0);
}
}
-void QQuickMaterialPlaceholderText::maybeSetFocusAnimationProgress()
-{
- updateY();
- updateX();
- setScale(shouldFloat() ? floatingScale : 1.0);
-}
-
void QQuickMaterialPlaceholderText::componentComplete()
{
QQuickPlaceholderText::componentComplete();
@@ -365,7 +332,7 @@ void QQuickMaterialPlaceholderText::componentComplete()
<< "to be greater than 0 by component completion!";
}
- maybeSetFocusAnimationProgress();
+ updateFocusAnimation();
}
QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
index 263c656d02..d47714bb28 100644
--- a/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
+++ b/src/quickcontrols/material/impl/qquickmaterialplaceholdertext_p.h
@@ -89,10 +89,9 @@ private:
qreal normalTargetX() const;
qreal floatingTargetX() const;
- void controlGotActiveFocus();
- void controlLostActiveFocus();
+ void controlActiveFocusChanged();
- void maybeSetFocusAnimationProgress();
+ void updateFocusAnimation(bool createIfNeeded = false);
void componentComplete() override;
@@ -105,8 +104,7 @@ private:
qreal m_controlHeight = 0;
int m_leftPadding = 0;
int m_floatingLeftPadding = 0;
- QPointer<QParallelAnimationGroup> m_focusInAnimation;
- QPointer<QParallelAnimationGroup> m_focusOutAnimation;
+ QPointer<QParallelAnimationGroup> m_focusAnimation;
};
QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
index a0ad0967fb..20c4609cb5 100644
--- a/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer.cpp
@@ -163,7 +163,7 @@ void QQuickMaterialTextContainer::setControlHasText(bool controlHasText)
m_controlHasText = controlHasText;
// TextArea's text length is updated after component completion,
// so account for that here and in setPlaceholderHasText().
- maybeSetFocusAnimationProgress();
+ updateFocusAnimation();
update();
emit controlHasTextChanged();
}
@@ -179,7 +179,7 @@ void QQuickMaterialTextContainer::setPlaceholderHasText(bool placeholderHasText)
return;
m_placeholderHasText = placeholderHasText;
- maybeSetFocusAnimationProgress();
+ updateFocusAnimation();
update();
emit placeholderHasTextChanged();
}
@@ -349,6 +349,10 @@ QQuickItem *QQuickMaterialTextContainer::textControl() const
void QQuickMaterialTextContainer::controlGotActiveFocus()
{
+ if (m_focusAnimation) {
+ m_focusAnimation->stop();
+ m_focusAnimation.clear();
+ }
const bool shouldAnimate = m_filled ? !m_controlHasText : shouldAnimateOutline();
if (!shouldAnimate) {
// It does have focus, but sometimes we don't need to animate anything, just change colors.
@@ -361,11 +365,15 @@ void QQuickMaterialTextContainer::controlGotActiveFocus()
return;
}
- startFocusAnimation();
+ updateFocusAnimation(true);
}
void QQuickMaterialTextContainer::controlLostActiveFocus()
{
+ if (m_focusAnimation) {
+ m_focusAnimation->stop();
+ m_focusAnimation.clear();
+ }
// We don't want to animate the active indicator line (at the bottom) of filled containers
// when the control loses focus, only when it gets it.
if (m_filled || !shouldAnimateOutline()) {
@@ -377,35 +385,43 @@ void QQuickMaterialTextContainer::controlLostActiveFocus()
return;
}
- QPropertyAnimation *animation = new QPropertyAnimation(this, "focusAnimationProgress", this);
- animation->setDuration(300);
- animation->setStartValue(1);
- animation->setEndValue(0);
- animation->start(QAbstractAnimation::DeleteWhenStopped);
+ updateFocusAnimation(true);
}
-void QQuickMaterialTextContainer::startFocusAnimation()
+void QQuickMaterialTextContainer::updateFocusAnimation(bool createIfNeeded)
{
- // Each time setFocusAnimationProgress is called by the animation, it'll call update(),
- // which will cause us to be re-rendered.
- QPropertyAnimation *animation = new QPropertyAnimation(this, "focusAnimationProgress", this);
- animation->setDuration(300);
- animation->setStartValue(0);
- animation->setEndValue(1);
- animation->start(QAbstractAnimation::DeleteWhenStopped);
-}
-
-void QQuickMaterialTextContainer::maybeSetFocusAnimationProgress()
-{
- if (m_filled)
+ if (m_filled) {
+ if (m_focusAnimation) {
+ m_focusAnimation->stop();
+ m_focusAnimation.clear();
+ }
return;
+ }
+ int focusAnimationProgressValue = 0;
if (m_controlHasText && m_placeholderHasText) {
// Show the interrupted outline when there is text.
- setFocusAnimationProgress(1);
- } else if (!m_controlHasText && !m_controlHasActiveFocus) {
- // If the text was cleared while it didn't have focus, don't animate, just close the gap.
- setFocusAnimationProgress(0);
+ focusAnimationProgressValue = 1;
+ } else if (!m_controlHasText) {
+ if (m_controlHasActiveFocus && m_placeholderHasText)
+ focusAnimationProgressValue = 1;
+ else
+ focusAnimationProgressValue = 0;
+ }
+
+ if (m_focusAnimation || createIfNeeded) {
+ int duration = 300;
+ if (m_focusAnimation) {
+ duration = m_focusAnimation->totalDuration() - m_focusAnimation->currentTime();
+ m_focusAnimation->stop();
+ }
+ m_focusAnimation = new QPropertyAnimation(this, "focusAnimationProgress", this);
+ m_focusAnimation->setDuration(duration);
+ m_focusAnimation->setStartValue(focusAnimationProgress());
+ m_focusAnimation->setEndValue(focusAnimationProgressValue);
+ m_focusAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ } else {
+ setFocusAnimationProgress(focusAnimationProgressValue);
}
}
@@ -416,7 +432,7 @@ void QQuickMaterialTextContainer::componentComplete()
if (!parentItem())
qmlWarning(this) << "Expected parent item by component completion!";
- maybeSetFocusAnimationProgress();
+ updateFocusAnimation();
}
QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
index 648e83521f..f056fddf87 100644
--- a/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
+++ b/src/quickcontrols/material/impl/qquickmaterialtextcontainer_p.h
@@ -15,6 +15,8 @@
// We mean it.
//
+#include <QtCore/qpointer.h>
+#include <QtCore/qpropertyanimation.h>
#include <QtCore/private/qglobal_p.h>
#include <QtGui/qcolor.h>
#include <QtQuick/qquickpainteditem.h>
@@ -98,9 +100,8 @@ private:
QQuickItem *textControl() const;
void controlGotActiveFocus();
void controlLostActiveFocus();
- void startFocusAnimation();
- void maybeSetFocusAnimationProgress();
+ void updateFocusAnimation(bool createIfNeeded = false);
void componentComplete() override;
@@ -115,6 +116,7 @@ private:
bool m_placeholderHasText = false;
int m_horizontalPadding = 0;
PlaceHolderHAlignment m_placeholderTextHAlign;
+ QPointer<QPropertyAnimation> m_focusAnimation;
};
QT_END_NAMESPACE
diff --git a/src/quickcontrols/windows/CMakeLists.txt b/src/quickcontrols/windows/CMakeLists.txt
index 8b8569952c..87928b95f0 100644
--- a/src/quickcontrols/windows/CMakeLists.txt
+++ b/src/quickcontrols/windows/CMakeLists.txt
@@ -27,6 +27,7 @@ set(qml_files
"RadioDelegate.qml"
"RangeSlider.qml"
"ScrollIndicator.qml"
+ "SearchField.qml"
"SelectionRectangle.qml"
"Slider.qml"
"SpinBox.qml"
@@ -59,6 +60,12 @@ qt_internal_add_qml_module(qtquickcontrols2windowsstyleplugin
images/menuarrow.png
images/menuarrow@2x.png
images/menuarrow@3x.png
+ images/close_big.png
+ images/close_big@2x.png
+ images/close_big@3x.png
+ images/search-magnifier.png
+ images/search-magnifier@2x.png
+ images/search-magnifier@3x.png
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
diff --git a/src/quickcontrols/windows/SearchField.qml b/src/quickcontrols/windows/SearchField.qml
new file mode 100644
index 0000000000..c3d87d9e1a
--- /dev/null
+++ b/src/quickcontrols/windows/SearchField.qml
@@ -0,0 +1,109 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+import QtQuick.NativeStyle as NativeStyle
+
+NativeStyle.DefaultSearchField {
+ id: control
+
+ readonly property bool __nativeSearchIndicator: searchIndicator.indicator.hasOwnProperty("_qt_default")
+ readonly property bool __nativeClearIndicator: clearIndicator.indicator.hasOwnProperty("_qt_default")
+
+ readonly property bool __notCustomizable: true
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding,
+ 90 /* minimum */ )
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding,
+ searchIndicator.implicitIndicatorHeight + topPadding + bottomPadding,
+ clearIndicator.implicitIndicatorHeight + topPadding + bottomPadding)
+
+ contentItem: T.TextField {
+ text: control.text
+
+ color: control.palette.text
+ selectionColor: control.palette.highlight
+ selectedTextColor: control.palette.highlightedText
+ verticalAlignment: Text.AlignVCenter
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+
+ NativeStyle.SearchField {
+ id: search
+ visible: control.__nativeSearchIndicator
+ control: control
+ subControl: NativeStyle.SearchField.Search
+ x: searchIndicator.indicator.x
+ y: searchIndicator.indicator.y
+ useNinePatchImage: false
+ }
+
+ searchIndicator.indicator: Item {
+ x: 3
+ y: control.topPadding + (control.availableHeight - height) / 2
+ implicitWidth: search.width
+ implicitHeight: search.height
+
+ property bool _qt_default
+ readonly property bool __ignoreNotCustomizable: true
+
+ ColorImage {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: 12
+ height: 12
+
+ source: Qt.resolvedUrl("images/search-magnifier")
+ color: control.palette.buttonText
+ opacity: control.searchIndicator.pressed ? 0.7 : 1
+ }
+ }
+
+ NativeStyle.SearchField {
+ id: clear
+ visible: control.__nativeClearIndicator && control.text.length > 0
+ control: control
+ subControl: NativeStyle.SearchField.Clear
+ x: clearIndicator.indicator.x
+ y: clearIndicator.indicator.y
+ useNinePatchImage: false
+ }
+
+ clearIndicator.indicator: Item {
+ x: control.width - width - 3
+ y: control.topPadding + (control.availableHeight - height) / 2
+ implicitWidth: clear.width
+ implicitHeight: clear.height
+
+ property bool _qt_default
+ readonly property bool __ignoreNotCustomizable: true
+
+ ColorImage {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: 12
+ height: 12
+
+ source: Qt.resolvedUrl("images/close_big")
+ visible: control.text.length > 0
+ color: control.palette.buttonText
+ opacity: control.clearIndicator.pressed ? 0.7 : 1
+ }
+ }
+
+ background: NativeStyle.SearchField {
+ control: control
+ subControl: NativeStyle.SearchField.Frame
+ contentWidth: contentItem.implicitWidth
+ contentHeight: contentItem.implicitHeight
+
+ readonly property bool __ignoreNotCustomizable: true
+ }
+}
diff --git a/src/quickcontrols/windows/images/close_big.png b/src/quickcontrols/windows/images/close_big.png
new file mode 100644
index 0000000000..ac082847e7
--- /dev/null
+++ b/src/quickcontrols/windows/images/close_big.png
Binary files differ
diff --git a/src/quickcontrols/windows/images/close_big@2x.png b/src/quickcontrols/windows/images/close_big@2x.png
new file mode 100644
index 0000000000..868efe2b46
--- /dev/null
+++ b/src/quickcontrols/windows/images/close_big@2x.png
Binary files differ
diff --git a/src/quickcontrols/windows/images/close_big@3x.png b/src/quickcontrols/windows/images/close_big@3x.png
new file mode 100644
index 0000000000..84ede53230
--- /dev/null
+++ b/src/quickcontrols/windows/images/close_big@3x.png
Binary files differ
diff --git a/src/quickcontrols/windows/images/search-magnifier.png b/src/quickcontrols/windows/images/search-magnifier.png
new file mode 100644
index 0000000000..4d967c4e79
--- /dev/null
+++ b/src/quickcontrols/windows/images/search-magnifier.png
Binary files differ
diff --git a/src/quickcontrols/windows/images/search-magnifier@2x.png b/src/quickcontrols/windows/images/search-magnifier@2x.png
new file mode 100644
index 0000000000..4d1a04cd92
--- /dev/null
+++ b/src/quickcontrols/windows/images/search-magnifier@2x.png
Binary files differ
diff --git a/src/quickcontrols/windows/images/search-magnifier@3x.png b/src/quickcontrols/windows/images/search-magnifier@3x.png
new file mode 100644
index 0000000000..8b0515bc72
--- /dev/null
+++ b/src/quickcontrols/windows/images/search-magnifier@3x.png
Binary files differ
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
index bee06c47ae..34c758f702 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/FileDialog.qml
@@ -122,21 +122,21 @@ FileDialogImpl {
}
}
- contentItem: RowLayout {
+ contentItem: SplitView {
id: contentLayout
+ contentHeight: sideBar.implicitHeight
DialogsImpl.SideBar {
id: sideBar
dialog: control
- Layout.fillHeight: true
- implicitWidth: 150
+ SplitView.minimumWidth: 50
+ SplitView.maximumWidth: contentLayout.width / 2
}
Frame {
padding: 0
verticalPadding: 1
- Layout.fillWidth: true
- Layout.fillHeight: true
+ SplitView.fillWidth: true
ListView {
id: fileDialogListView
@@ -166,6 +166,10 @@ FileDialogImpl {
KeyNavigation.tab: fileNameTextField.visible ? fileNameTextField : nameFiltersComboBox
}
}
+
+ background: Rectangle {
+ color: control.palette.base
+ }
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/SideBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/SideBar.qml
index 237be7847c..c9dcf17f9d 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/SideBar.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Fusion/SideBar.qml
@@ -14,11 +14,25 @@ DialogsQuickImpl.SideBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
+ contentWidth: (contentItem as ListView)?.contentWidth
+
+ background: Rectangle {
+ color: control.palette.window
+ x: 1
+ y: 1
+ width: parent.width - 2
+ height: parent.height - 2
+ radius: 2
+ }
+
contentItem: ListView {
id: listView
currentIndex: control.currentIndex
model: control.contentModel
clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
Rectangle {
anchors.fill: parent
@@ -50,8 +64,8 @@ DialogsQuickImpl.SideBar {
}
separatorDelegate: Item {
- width: control.width
- height: 9
+ implicitWidth: control.width
+ implicitHeight: 9
Rectangle {
id: separatorDelegate
color: Qt.lighter(Fusion.darkShade, 1.06)
@@ -81,6 +95,5 @@ DialogsQuickImpl.SideBar {
required property string labelText
required property bool dragHovering
- required icon
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
index ec73dda344..ad56459ffd 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/FileDialog.qml
@@ -120,21 +120,21 @@ FileDialogImpl {
}
}
- contentItem: RowLayout {
+ contentItem: SplitView {
id: contentLayout
+ contentHeight: sideBar.implicitHeight
DialogsImpl.SideBar {
id: sideBar
dialog: control
- Layout.fillHeight: true
- implicitWidth: 150
+ SplitView.minimumWidth: 50
+ SplitView.maximumWidth: contentLayout.width / 2
}
ListView {
id: fileDialogListView
objectName: "fileDialogListView"
- Layout.fillWidth: true
- Layout.fillHeight: true
+ SplitView.fillWidth: true
clip: true
boundsBehavior: Flickable.StopAtBounds
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/SideBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/SideBar.qml
index f0fd1b5610..eeaaf7e80d 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/SideBar.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Imagine/SideBar.qml
@@ -44,8 +44,8 @@ DialogsQuickImpl.SideBar {
}
separatorDelegate: Item {
- width: control.width
- height: 9
+ implicitWidth: control.width
+ implicitHeight: 9
Rectangle {
id: separatorDelegate
color: Qt.lighter(Imagine.darkShade, 1.06)
@@ -75,6 +75,5 @@ DialogsQuickImpl.SideBar {
required property string labelText
required property bool dragHovering
- required icon
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
index 02ad9f03de..77da872cb1 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/FileDialog.qml
@@ -102,23 +102,23 @@ FileDialogImpl {
}
}
- contentItem: RowLayout {
+ contentItem: SplitView {
id: contentLayout
+ contentHeight: sideBar.implicitHeight
DialogsImpl.SideBar {
id: sideBar
dialog: control
- Layout.fillHeight: true
- implicitWidth: 150
+ SplitView.minimumWidth: 50
+ SplitView.maximumWidth: contentLayout.width / 2
}
ListView {
id: fileDialogListView
objectName: "fileDialogListView"
- Layout.fillWidth: true
- Layout.fillHeight: true
+ SplitView.fillWidth: true
clip: true
-
+ boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar {}
model: FolderListModel {
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/SideBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/SideBar.qml
index 8b25c975da..c6fdbd06ab 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Material/SideBar.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Material/SideBar.qml
@@ -14,6 +14,8 @@ DialogsQuickImpl.SideBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
+ contentWidth: (contentItem as ListView)?.contentWidth
+
background: Rectangle {
color: control.Material.backgroundColor
}
@@ -22,6 +24,7 @@ DialogsQuickImpl.SideBar {
currentIndex: control.currentIndex
model: control.contentModel
clip: true
+ boundsBehavior: Flickable.StopAtBounds
}
buttonDelegate: Button {
@@ -46,8 +49,8 @@ DialogsQuickImpl.SideBar {
}
separatorDelegate: Item {
- width: control.width
- height: 9
+ implicitWidth: control.width
+ implicitHeight: 9
Rectangle {
id: separatorDelegate
color: Qt.lighter(Material.darkShade, 1.06)
@@ -77,6 +80,5 @@ DialogsQuickImpl.SideBar {
required property string labelText
required property bool dragHovering
- required icon
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
index aedf3e1852..0c621240cc 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/FileDialog.qml
@@ -106,21 +106,21 @@ FileDialogImpl {
}
}
- contentItem: RowLayout {
+ contentItem: SplitView {
id: contentLayout
+ contentHeight: sideBar.implicitHeight
DialogsImpl.SideBar {
id: sideBar
dialog: control
- Layout.fillHeight: true
- implicitWidth: 150
+ SplitView.minimumWidth: 50
+ SplitView.maximumWidth: contentLayout.width / 2
}
ListView {
id: fileDialogListView
objectName: "fileDialogListView"
- Layout.fillWidth: true
- Layout.fillHeight: true
+ SplitView.fillWidth: true
clip: true
boundsBehavior: Flickable.StopAtBounds
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/SideBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/SideBar.qml
index e620ee994b..5c2ba0aabb 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/SideBar.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/+Universal/SideBar.qml
@@ -14,6 +14,8 @@ DialogsQuickImpl.SideBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
+ contentWidth: (contentItem as ListView)?.contentWidth
+
background: Rectangle {
color: control.Universal.background
}
@@ -23,6 +25,7 @@ DialogsQuickImpl.SideBar {
currentIndex: control.currentIndex
model: control.contentModel
clip: true
+ boundsBehavior: Flickable.StopAtBounds
}
buttonDelegate: Button {
@@ -47,8 +50,8 @@ DialogsQuickImpl.SideBar {
}
separatorDelegate: Item {
- width: control.width
- height: 9
+ implicitWidth: control.width
+ implicitHeight: 9
Rectangle {
id: separatorDelegate
color: Qt.lighter(Universal.darkShade, 1.06)
@@ -78,6 +81,5 @@ DialogsQuickImpl.SideBar {
required property string labelText
required property bool dragHovering
- required icon
}
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
index 5490d2cdfa..db81faa045 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/FileDialog.qml
@@ -112,21 +112,21 @@ FileDialogImpl {
}
}
- contentItem: RowLayout {
+ contentItem: SplitView {
id: contentLayout
+ contentHeight: sideBar.implicitHeight
DialogsImpl.SideBar {
id: sideBar
dialog: control
- Layout.fillHeight: true
- implicitWidth: 150
+ SplitView.minimumWidth: 50
+ SplitView.maximumWidth: contentLayout.width / 2
}
ListView {
id: fileDialogListView
objectName: "fileDialogListView"
- Layout.fillWidth: true
- Layout.fillHeight: true
+ SplitView.fillWidth: true
clip: true
focus: true
boundsBehavior: Flickable.StopAtBounds
diff --git a/src/quickdialogs/quickdialogsquickimpl/qml/SideBar.qml b/src/quickdialogs/quickdialogsquickimpl/qml/SideBar.qml
index 8c49991509..231d2da0cb 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qml/SideBar.qml
+++ b/src/quickdialogs/quickdialogsquickimpl/qml/SideBar.qml
@@ -14,6 +14,8 @@ DialogsQuickImpl.SideBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding)
+ contentWidth: (contentItem as ListView)?.contentWidth
+
background: Rectangle {
color: control.palette.window
}
@@ -23,6 +25,9 @@ DialogsQuickImpl.SideBar {
currentIndex: control.currentIndex
model: control.contentModel
clip: true
+ boundsBehavior: Flickable.StopAtBounds
+
+ ScrollBar.vertical: ScrollBar {}
}
buttonDelegate: Button {
@@ -49,8 +54,8 @@ DialogsQuickImpl.SideBar {
}
separatorDelegate: Item {
- width: control.width
- height: 9
+ implicitWidth: control.width
+ implicitHeight: 9
Rectangle {
id: separatorDelegate
color: Qt.lighter(control.palette.dark, 1.06)
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
index 340695fe11..ffc27daaf0 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickdialogimplfactory.cpp
@@ -29,25 +29,35 @@ std::unique_ptr<QPlatformDialogHelper> QQuickDialogImplFactory::createPlatformDi
std::unique_ptr<QPlatformDialogHelper> dialogHelper;
switch (type) {
case QQuickDialogType::ColorDialog: {
- dialogHelper.reset(new QQuickPlatformColorDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformColorDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
#if QT_CONFIG(quick_listview) && QT_CONFIG(quick_draganddrop)
case QQuickDialogType::FileDialog: {
- dialogHelper.reset(new QQuickPlatformFileDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformFileDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
case QQuickDialogType::FolderDialog: {
- dialogHelper.reset(new QQuickPlatformFolderDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformFolderDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
case QQuickDialogType::FontDialog: {
- dialogHelper.reset(new QQuickPlatformFontDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformFontDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
#endif
case QQuickDialogType::MessageDialog: {
- dialogHelper.reset(new QQuickPlatformMessageDialog(parent));
+ auto *quickPlatformDialog = new QQuickPlatformMessageDialog(parent);
+ if (quickPlatformDialog->isValid())
+ dialogHelper.reset(quickPlatformDialog);
break;
}
default:
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
index 9df7f681df..32488e83e2 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquickfiledialogimpl.cpp
@@ -369,11 +369,15 @@ void QQuickFileDialogImpl::setInitialCurrentFolderAndSelectedFile(const QUrl &fi
d->updateFileNameTextEdit();
d->setCurrentIndexToInitiallySelectedFile = true;
+ bool isListViewCurrentIndexNegative = false;
+ if (const auto *attached = d->attachedOrWarn())
+ isListViewCurrentIndexNegative = attached->fileDialogListView()->currentIndex() < 0;
+
// If the currentFolder didn't change, the FolderListModel won't change and
// neither will the ListView. This means that setFileDialogListViewCurrentIndex
// will never get called and the currentIndex will not reflect selectedFile.
// We need to account for that here.
- if (!currentFolderChanged) {
+ if (!currentFolderChanged || isListViewCurrentIndexNegative) {
const QFileInfo newSelectedFileInfo(d->selectedFile.toLocalFile());
const int indexOfSelectedFileInFileDialogListView = d->cachedFileList.indexOf(newSelectedFileInfo);
d->tryUpdateFileDialogListViewCurrentIndex(indexOfSelectedFileInFileDialogListView);
@@ -744,10 +748,15 @@ void QQuickFileDialogImplAttached::setFileDialogListView(QQuickListView *fileDia
if (fileDialogListView == d->fileDialogListView)
return;
+ if (d->fileDialogListView)
+ QObjectPrivate::disconnect(d->fileDialogListView, &QQuickListView::currentIndexChanged,
+ d, &QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged);
+
d->fileDialogListView = fileDialogListView;
- QObjectPrivate::connect(d->fileDialogListView, &QQuickListView::currentIndexChanged,
- d, &QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged);
+ if (d->fileDialogListView)
+ QObjectPrivate::connect(d->fileDialogListView, &QQuickListView::currentIndexChanged,
+ d, &QQuickFileDialogImplAttachedPrivate::fileDialogListViewCurrentIndexChanged);
emit fileDialogListViewChanged();
}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquicksidebar.cpp b/src/quickdialogs/quickdialogsquickimpl/qquicksidebar.cpp
index a6310a3a37..773ea5b674 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquicksidebar.cpp
+++ b/src/quickdialogs/quickdialogsquickimpl/qquicksidebar.cpp
@@ -275,21 +275,33 @@ void QQuickSideBarPrivate::repopulate()
for (auto &folder : folders)
createButtonDelegate(insertIndex++, QStandardPaths::displayName(folder), folderIconSource(folder));
- if (showSeparator)
- if (QQuickItem *separatorItem = createDelegateItem(separatorDelegate, {}))
+
+ if (QQuickItem *separatorItem = createDelegateItem(separatorDelegate, {{"visible"_L1, false}})) {
+ separatorImplicitSize = separatorItem->implicitHeight();
+ if (showSeparator) {
+ separatorItem->setVisible(true);
insertItem(insertIndex++, separatorItem);
+ } else {
+ separatorItem->deleteLater();
+ }
+ }
- if (showAddFavoriteDelegate()) {
- // the variant needs to be QString, not a QLatin1StringView
- const QString labelText = QCoreApplication::translate("FileDialog", "Add Favorite");
- QVariantMap initialProperties = {
- { "labelText"_L1, QVariant::fromValue(labelText) },
- { "dragHovering"_L1, QVariant::fromValue(addFavoriteDelegateHovered()) },
- };
- if (auto *addFavoriteDelegateItem = createDelegateItem(addFavoriteDelegate, initialProperties)) {
+ // The variant needs to be QString, not a QLatin1StringView
+ const QString labelText = QCoreApplication::translate("FileDialog", "Add Favorite");
+ const QVariantMap initialProperties = {
+ { "labelText"_L1, QVariant::fromValue(labelText) },
+ { "dragHovering"_L1, QVariant::fromValue(addFavoriteDelegateHovered()) },
+ { "visible"_L1, false}
+ };
+ if (auto *addFavoriteDelegateItem = createDelegateItem(addFavoriteDelegate, initialProperties)) {
+ addFavoriteButtonImplicitSize = addFavoriteDelegateItem->implicitHeight();
+ if (showAddFavoriteDelegate()) {
+ addFavoriteDelegateItem->setVisible(true);
if (QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(addFavoriteDelegateItem))
updateIconSourceAndSize(button, addFavoriteIconUrl());
insertItem(insertIndex++, addFavoriteDelegateItem);
+ } else {
+ addFavoriteDelegateItem->deleteLater();
}
}
@@ -526,3 +538,74 @@ void QQuickSideBarPrivate::handleRemoveAction()
removeFavorite(urlToBeRemoved);
urlToBeRemoved.clear();
}
+
+qreal QQuickSideBarPrivate::getContentWidth() const
+{
+ Q_Q(const QQuickSideBar);
+ if (!contentModel)
+ return 0;
+
+ const int count = contentModel->count();
+ qreal maxWidth = 0;
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item)
+ maxWidth = qMax(maxWidth, item->implicitWidth());
+ }
+ return maxWidth;
+}
+
+qreal QQuickSideBarPrivate::getContentHeight() const
+{
+ Q_Q(const QQuickSideBar);
+ if (!contentModel)
+ return 0;
+ // All StandardPaths buttons + spacing + separator + AddFavoriteButton
+ const int modelCount = contentModel->count();
+ const int folderPathCount = q->effectiveFolderPaths().count();
+ qreal spacing = 0;
+ if (contentItem) {
+ QQuickListView *listView = contentItem->findChild<QQuickListView*>();
+ if (listView)
+ spacing = listView->spacing();
+ }
+ qreal totalHeight = 0;
+ int i = 0;
+ for (; i < qMin(modelCount, folderPathCount); ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item) {
+ totalHeight += item->implicitHeight();
+ }
+ }
+ // Add spacing
+ if (i)
+ totalHeight += (i - 1) * spacing;
+
+ if (!qFuzzyIsNull(separatorImplicitSize))
+ totalHeight += separatorImplicitSize + spacing;
+ if (!qFuzzyIsNull(addFavoriteButtonImplicitSize))
+ totalHeight += addFavoriteButtonImplicitSize + spacing;
+
+ return totalHeight;
+}
+
+void QQuickSideBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
+{
+ QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
+ if (change.sizeChange())
+ updateImplicitContentSize();
+}
+
+void QQuickSideBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
+{
+ QQuickContainerPrivate::itemImplicitWidthChanged(item);
+ if (item != contentItem)
+ updateImplicitContentWidth();
+}
+
+void QQuickSideBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
+{
+ QQuickContainerPrivate::itemImplicitHeightChanged(item);
+ if (item != contentItem)
+ updateImplicitContentHeight();
+}
diff --git a/src/quickdialogs/quickdialogsquickimpl/qquicksidebar_p_p.h b/src/quickdialogs/quickdialogsquickimpl/qquicksidebar_p_p.h
index 047425a988..1ad1972390 100644
--- a/src/quickdialogs/quickdialogsquickimpl/qquicksidebar_p_p.h
+++ b/src/quickdialogs/quickdialogsquickimpl/qquicksidebar_p_p.h
@@ -60,6 +60,14 @@ public:
void handleContextMenuRequested(QPointF pos);
void handleRemoveAction();
+protected:
+ qreal getContentWidth() const override;
+ qreal getContentHeight() const override;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
+ void itemImplicitWidthChanged(QQuickItem *item) override;
+ void itemImplicitHeightChanged(QQuickItem *item) override;
+
+
private:
QQuickDialog *dialog = nullptr;
QQmlComponent *buttonDelegate = nullptr;
@@ -72,6 +80,8 @@ private:
QList<QStandardPaths::StandardLocation> folderPaths;
QList<QUrl> favoritePaths;
QUrl currentButtonClickedUrl;
+ qreal addFavoriteButtonImplicitSize = 0;
+ qreal separatorImplicitSize = 0;
bool folderPathsValid = false;
bool favoritePathsValid = false;
bool repopulating = false;
diff --git a/src/quicklayouts/qquickflexboxlayout.cpp b/src/quicklayouts/qquickflexboxlayout.cpp
index 87cbf2cc64..2dad441652 100644
--- a/src/quicklayouts/qquickflexboxlayout.cpp
+++ b/src/quicklayouts/qquickflexboxlayout.cpp
@@ -8,7 +8,7 @@
/*!
\qmltype FlexboxLayout
//! \nativetype QQuickFlexboxLayout
- \inherits Layout
+ \inherits Item
\inqmlmodule QtQuick.Layouts
\ingroup layouts
\since 6.10
@@ -47,6 +47,11 @@
\li \l{Layout::maximumHeight}{Layout.maximumHeight}
\li \l{Layout::fillWidth}{Layout.fillWidth}
\li \l{Layout::fillHeight}{Layout.fillHeight}
+ \li \l{Layout::margins}{Layout.margins}
+ \li \l{Layout::leftMargin}{Layout.leftMargin}
+ \li \l{Layout::rightMargin}{Layout.rightMargin}
+ \li \l{Layout::topMargin}{Layout.topMargin}
+ \li \l{Layout::bottomMargin}{Layout.bottomMargin}
\endlist
Read more about attached properties \l{QML Object Attributes}{here}.
diff --git a/src/quicklayouts/qquickflexboxlayout_p.h b/src/quicklayouts/qquickflexboxlayout_p.h
index 60e036a3c5..751f2c7c51 100644
--- a/src/quicklayouts/qquickflexboxlayout_p.h
+++ b/src/quicklayouts/qquickflexboxlayout_p.h
@@ -38,7 +38,7 @@ class Q_QUICKLAYOUTS_EXPORT QQuickFlexboxLayout : public QQuickLayout
Q_PROPERTY(qreal columnGap READ columnGap WRITE setColumnGap NOTIFY columnGapChanged RESET resetColumnGap FINAL)
QML_NAMED_ELEMENT(FlexboxLayout)
- QML_ADDED_IN_VERSION(2, 0)
+ QML_ADDED_IN_VERSION(6, 10)
QML_ATTACHED(QQuickFlexboxLayoutAttached)
public:
diff --git a/src/quicknativestyle/qstyle/qquickcommonstyle.cpp b/src/quicknativestyle/qstyle/qquickcommonstyle.cpp
index 138f2022e5..c1b5c76f3d 100644
--- a/src/quicknativestyle/qstyle/qquickcommonstyle.cpp
+++ b/src/quicknativestyle/qstyle/qquickcommonstyle.cpp
@@ -3946,6 +3946,36 @@ QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex
ret = visualRect(cb->direction, cb->rect, ret);
}
break;
+ case CC_SearchField:
+ if (const QStyleOptionSearchField *sf = qstyleoption_cast<const QStyleOptionSearchField *>(opt)) {
+ const qreal dpi = QStyleHelper::dpi(opt);
+ const int x = sf->rect.x(), y = sf->rect.y(), wi = sf->rect.width(), he = sf->rect.height();
+ const int margin = qRound(QStyleHelper::dpiScaled(3, dpi));
+ const int buttonSize = qRound(QStyleHelper::dpiScaled(16, dpi));
+ const int spacing = qRound(QStyleHelper::dpiScaled(2, dpi));
+
+ switch (sc) {
+ case SC_SearchFieldFrame:
+ ret = sf->rect;
+ break;
+ case SC_SearchFieldSearch:
+ ret.setRect(x + margin, y + (he - buttonSize) / 2, buttonSize, buttonSize);
+ break;
+ case SC_SearchFieldClear:
+ ret.setRect(x + wi - margin - buttonSize, y + (he - buttonSize) / 2, buttonSize, buttonSize);
+ break;
+ case SC_SearchFieldEditField:
+ ret.setRect(x + margin + buttonSize + spacing, y + margin, wi - (margin + buttonSize + spacing) * 2, he - 2 * margin);
+ break;
+ case SC_SearchFieldPopup:
+ ret = sf->rect;
+ break;
+ default:
+ break;
+ }
+ ret = visualRect(sf->direction, sf->rect, ret);
+ }
+ break;
case CC_TitleBar:
if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
const int controlMargin = 2;
@@ -4224,6 +4254,7 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt) const
ret = 2;
break;
+ case PM_SearchFieldFrameWidth:
case PM_ComboBoxFrameWidth:
case PM_SpinBoxFrameWidth:
case PM_MenuPanelWidth:
@@ -4658,6 +4689,27 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, c
}
}
break;
+ case CT_SearchField:
+ if (const QStyleOptionSearchField *vopt = qstyleoption_cast<const QStyleOptionSearchField *>(opt)) {
+ const QSize clearButton = proxy()->subControlRect(CC_SearchField, vopt, SC_SearchFieldClear).size();
+ const QSize searchButton = proxy()->subControlRect(CC_SearchField, vopt, SC_SearchFieldSearch).size();
+
+ const qreal dpi = QStyleHelper::dpi(opt);
+ const int margin = qRound(QStyleHelper::dpiScaled(3, dpi));
+ const int spacing = qRound(QStyleHelper::dpiScaled(2, dpi));
+ // Add buttons + frame widths
+ if (vopt->subControls == SC_SearchFieldFrame) {
+ const int totalIconsSize = clearButton.width() + searchButton.width() + (margin + spacing) * 2;
+
+ const int fw = vopt->frame ? proxy()->pixelMetric(PM_SearchFieldFrameWidth, vopt) : 0;
+ sz += QSize(totalIconsSize + 2*fw, 1 + 2*fw);
+ } else if (vopt->subControls == SC_SearchFieldClear) {
+ sz = clearButton;
+ } else if (vopt->subControls == SC_SearchFieldSearch) {
+ sz = searchButton;
+ }
+ }
+ break;
case CT_Slider:
if (const QStyleOptionSlider *option = qstyleoption_cast<const QStyleOptionSlider *>(opt))
sz = subControlRect(QStyle::CC_Slider, option, QStyle::SC_SliderHandle).size();
diff --git a/src/quicknativestyle/qstyle/qquickstyle.h b/src/quicknativestyle/qstyle/qquickstyle.h
index 7689e50ee4..7d15baa7df 100644
--- a/src/quicknativestyle/qstyle/qquickstyle.h
+++ b/src/quicknativestyle/qstyle/qquickstyle.h
@@ -384,6 +384,7 @@ public:
PM_DefaultFrameWidth,
PM_SpinBoxFrameWidth,
PM_ComboBoxFrameWidth,
+ PM_SearchFieldFrameWidth,
PM_MaximumDragDistance,
diff --git a/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp b/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp
index d0262d4c47..6a3cf64412 100644
--- a/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp
+++ b/src/quicknativestyle/qstyle/windows/qquickwindowsxpstyle.cpp
@@ -2401,6 +2401,30 @@ void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCo
}
break;
//#endif // QT_CONFIG(spinbox)
+//#if QT_CONFIG(searchfield)
+ case CC_SearchField:
+ if (const QStyleOptionSearchField *sf = qstyleoption_cast<const QStyleOptionSearchField *>(option))
+ {
+ if (sf->frame && (sub & SC_SearchFieldFrame)) {
+ partId = EP_EDITBORDER_NOSCROLL;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData theme(option->window, p,
+ QWindowsXPStylePrivate::EditTheme,
+ partId, stateId, r);
+
+ d->drawBackground(theme);
+ }
+ }
+ break;
+//#endif QT_CONFIG(searchfield)
//#if QT_CONFIG(combobox)
case CC_ComboBox:
if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
@@ -3063,6 +3087,7 @@ int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option) con
break;
case PM_MenuPanelWidth:
case PM_SpinBoxFrameWidth:
+ case PM_SearchFieldFrameWidth:
res = 1;
break;
@@ -3310,7 +3335,6 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl
}
}
break;
-
case CC_ComboBox:
if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
diff --git a/src/quicktemplates/qquickscrollbar.cpp b/src/quicktemplates/qquickscrollbar.cpp
index fe41252db5..54f593cc81 100644
--- a/src/quicktemplates/qquickscrollbar.cpp
+++ b/src/quicktemplates/qquickscrollbar.cpp
@@ -119,6 +119,8 @@ QT_BEGIN_NAMESPACE
visible.
\endlist
+ \include varying-delegate-heights-section.qdocinc {file} {1} {ScrollBar}
+
\sa ScrollIndicator, ScrollView, {Customizing ScrollBar}, {Indicator Controls}
*/
diff --git a/src/quicktemplates/qquickscrollindicator.cpp b/src/quicktemplates/qquickscrollindicator.cpp
index 2c94181471..0e6d855198 100644
--- a/src/quicktemplates/qquickscrollindicator.cpp
+++ b/src/quicktemplates/qquickscrollindicator.cpp
@@ -90,6 +90,8 @@ QT_BEGIN_NAMESPACE
\image qtquickcontrols-scrollindicator-non-attached.png
+ \include varying-delegate-heights-section.qdocinc {file} {1} {ScrollIndicator}
+
\sa ScrollBar, {Customizing ScrollIndicator}, {Indicator Controls}
*/
diff --git a/src/quicktemplates/qquickscrollview.cpp b/src/quicktemplates/qquickscrollview.cpp
index e46c7ea4b6..0eb5aa4200 100644
--- a/src/quicktemplates/qquickscrollview.cpp
+++ b/src/quicktemplates/qquickscrollview.cpp
@@ -86,6 +86,8 @@ QT_BEGIN_NAMESPACE
\snippet qtquickcontrols-scrollview-interactive.qml file
+ \include varying-delegate-heights-section.qdocinc {file} {2} {ScrollBar}
+
\sa ScrollBar, ScrollIndicator, {Customizing ScrollView}, {Container Controls},
{Focus Management in Qt Quick Controls}
*/
diff --git a/src/quickvectorimage/generator/qquickanimatedproperty_p.h b/src/quickvectorimage/generator/qquickanimatedproperty_p.h
index 5dc503d61a..a56358e7b3 100644
--- a/src/quickvectorimage/generator/qquickanimatedproperty_p.h
+++ b/src/quickvectorimage/generator/qquickanimatedproperty_p.h
@@ -31,13 +31,20 @@ public:
ReplacePreviousAnimations = 2,
};
- int subtype = 0;
QMap<int, QVariant> frames;
+ QMap<int, QBezier> easingPerFrame;
+ int subtype = 0;
int repeatCount = 1;
int startOffset = 0;
quint8 flags = NoFlags;
- QMap<int, QBezier> easingPerFrame;
+ bool isConstant() const {
+ for (const auto& frame : frames) {
+ if (frame != frames.first())
+ return false;
+ }
+ return true;
+ }
};
QQuickAnimatedProperty(const QVariant &defaultValue)
diff --git a/src/quickvectorimage/generator/qquickqmlgenerator.cpp b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
index bd445322de..b6e1d23b3b 100644
--- a/src/quickvectorimage/generator/qquickqmlgenerator.cpp
+++ b/src/quickvectorimage/generator/qquickqmlgenerator.cpp
@@ -116,20 +116,48 @@ QString QQuickQmlGenerator::generateNodeBase(const NodeInfo &info)
: info.transform.animationCount();
for (int i = nextAnimationStart - 1; i >= animationStart; --i) {
- const auto &animation = info.transform.animation(i);
+ const QQuickAnimatedProperty::PropertyAnimation &animation = info.transform.animation(i);
+ if (animation.frames.isEmpty())
+ continue;
+ const QVariantList &parameters = animation.frames.first().value<QVariantList>();
switch (animation.subtype) {
case QTransform::TxTranslate:
- stream() << "Translate { id: " << idString << "_transform_" << groupIndex << "_" << i << " }";
+ if (animation.isConstant()) {
+ const QPointF translation = parameters.value(0).value<QPointF>();
+ if (!translation.isNull())
+ stream() << "Translate { x: " << translation.x() << "; y: " << translation.y() << " }";
+ } else {
+ stream() << "Translate { id: " << idString << "_transform_" << groupIndex << "_" << i << " }";
+ }
break;
case QTransform::TxScale:
- stream() << "Scale { id: " << idString << "_transform_" << groupIndex << "_" << i << "}";
+ if (animation.isConstant()) {
+ const QPointF scale = parameters.value(0).value<QPointF>();
+ if (scale != QPointF(1, 1))
+ stream() << "Scale { xScale: " << scale.x() << "; yScale: " << scale.y() << " }";
+ } else {
+ stream() << "Scale { id: " << idString << "_transform_" << groupIndex << "_" << i << "}";
+ }
break;
case QTransform::TxRotate:
- stream() << "Rotation { id: " << idString << "_transform_" << groupIndex << "_" << i << "; origin.x: " << idString << ".width / 2.0; origin.y: " << idString << ".height / 2.0 }";
+ if (animation.isConstant()) {
+ const QPointF center = parameters.value(0).value<QPointF>();
+ const qreal angle = parameters.value(1).toReal();
+ if (!qFuzzyIsNull(angle))
+ stream() << "Rotation { angle: " << angle << "; origin.x: " << center.x() << "; origin.y: " << center.y() << " }"; //### center relative to what?
+ } else {
+ stream() << "Rotation { id: " << idString << "_transform_" << groupIndex << "_" << i << " }";
+ }
break;
case QTransform::TxShear:
- stream() << "Shear { id: " << idString << "_transform_" << groupIndex << "_" << i << " }";
+ if (animation.isConstant()) {
+ const QPointF skew = parameters.value(0).value<QPointF>();
+ if (!skew.isNull())
+ stream() << "Shear { xAngle: " << skew.x() << "; yAngle: " << skew.y() << " }";
+ } else {
+ stream() << "Shear { id: " << idString << "_transform_" << groupIndex << "_" << i << " }";
+ }
break;
default:
Q_UNREACHABLE();
@@ -838,6 +866,18 @@ void QQuickQmlGenerator::generateAnimateTransform(const QString &targetName, con
for (int i = animationStart; i < nextAnimationStart; ++i) {
const QQuickAnimatedProperty::PropertyAnimation &animation = info.transform.animation(i);
+ if (animation.isConstant())
+ continue;
+ bool hasRotationCenter = false;
+ if (animation.subtype == QTransform::TxRotate) {
+ for (auto it = animation.frames.constBegin(); it != animation.frames.constEnd(); ++it) {
+ const QPointF center = it->value<QVariantList>().value(0).value<QPointF>();
+ if (!center.isNull()) {
+ hasRotationCenter = true;
+ break;
+ }
+ }
+ }
stream() << "SequentialAnimation {";
m_indentLevel++;
@@ -903,15 +943,16 @@ void QQuickQmlGenerator::generateAnimateTransform(const QString &targetName, con
case QTransform::TxRotate:
{
Q_ASSERT(parameters.size() == 2);
- const QPointF center = parameters.value(0).value<QPointF>();
const qreal angle = parameters.value(1).toReal();
-
- generateAnimatedPropertySetter(propertyTargetName,
- QStringLiteral("origin"),
- QVector3D(center.x(), center.y(), 0.0),
- animation,
- frameTime,
- time);
+ if (hasRotationCenter) {
+ const QPointF center = parameters.value(0).value<QPointF>();
+ generateAnimatedPropertySetter(propertyTargetName,
+ QStringLiteral("origin"),
+ QVector3D(center.x(), center.y(), 0.0),
+ animation,
+ frameTime,
+ time);
+ }
generateAnimatedPropertySetter(propertyTargetName,
QStringLiteral("angle"),
angle,