2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2017 The Qt Company Ltd.
|
2024-02-22 14:51:16 +00:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
|
|
|
#include <QtTest/qtest.h>
|
|
|
|
#include <QtCore/private/qhooks_p.h>
|
|
|
|
#include <QtCore/qregularexpression.h>
|
|
|
|
#include <QtQml/qqmlengine.h>
|
|
|
|
#include <QtQml/qqmlcomponent.h>
|
2017-12-13 12:35:52 +00:00
|
|
|
#include <QtQuick/qquickitem.h>
|
|
|
|
#include <QtQuick/qquickwindow.h>
|
2021-08-06 10:27:35 +00:00
|
|
|
#include <QtQuickTestUtils/private/qmlutils_p.h>
|
|
|
|
#include <QtQuickControlsTestUtils/private/controlstestutils_p.h>
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
#include <QtQuickControls2/qquickstyle.h>
|
Use qmlRegisterModuleImport() to register styles
This patch completes the cumulative work done in previous patches.
- Uses qmlRegisterModuleImport() to register styles. This has some
added requirements:
- Each style must now be a QML module -- that is, it must have a
qmldir file.
- As a result of the above, the module must be available within the
QML import path in order to be found.
- The various forms of accepted style names have been reduced down to
one ("Material", "MyStyle", etc). See below for an explanation of
why.
- The following API in QQuickStyle is removed:
addStylePath(), availableStyles(), path(), stylePathList(). These
no longer make sense now that we reuse the existing QML import
system.
- Adds the tst_qquickstyleselector auto test back as "styleimports".
qmlRegisterModuleImport() vs resolvedUrl()
Previously we would use QQuickStyleSelector to select individual
QML files based on which style was set. We'd do this once when
QtQuick.Controls was first imported.
With Qt 6, and the requirement that each style be a proper QML
module, qmlRegisterModuleImport() was introduced. This allows us
to "link" one import with another. For an example of what this
looks like in practice, suppose the style was set to "MyStyle",
and the fallback to "Material". The "QtQuick.Controls" import
will be linked to "MyStyle", "MyStyle" to
"QtQuick.Controls.Material", and as a final fallback (for controls
like Action which only the Default style implements),
"QtQuick.Controls.Material" to "QtQuick.Controls.Default".
This is the same behavior as in Qt 5 (see qquickstyleselector.cpp):
// 1) requested style (e.g. "MyStyle", included in d->selectors)
// 2) fallback style (e.g. "Material", included in d->selectors)
// 3) default style (empty selector, not in d->selectors)
This is a necessary step to enable compilation of QML to C++.
Reducing the set of accepted style names
The problem
In QtQuickControls2Plugin() we need to call
QQuickStylePrivate::init(baseUrl()) in order to detect if the style
is a custom style in QQuickStyleSpec::resolve() (by checking if the
style path starts with the base URL). In Qt 5, init() is called in
QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too
late, because we need to call qmlRegisterModuleImport() in the
constructor. qmlRegisterModuleImport() itself requires the style to
have already been set in order to create the correct import URI
("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for
custom styles).
The solution
By reducing the valid forms for style names down to one:
./myapp -style MyStyle
we solve the problem of needing baseUrl() to determine if the
style is a custom style or not, but needing to call it too early
(since we now call qmlRegisterModuleImport() in
QtQuickControls2Plugin(), which itself requires the style to have
already been set). baseUrl() can't have been set before the
constructor is finished.
All of the various forms for _setting_ a style are still valid;
environment variables, qtquickcontrols2.conf, etc.
[ChangeLog][Important Behavior Changes] Custom styles must now have
a qmldir that lists the files that the style implements. For example,
for a style that only implements Button:
---
module MyStyle
Button 1.0 Button.qml
---
In addition, there is now only one valid, case-sensitive form for style
names: "Material", "MyStyle", etc.
These changes are done to help enable the compilation of QML code to
C++, as well as improve tooling capabilities.
[ChangeLog][Important Behavior Changes] The following API was removed:
- QQuickStyle::addStylePath()
- QQuickStyle::availableStyles()
- QQuickStyle::path()
- QQuickStyle::stylePathList()
- QT_QUICK_CONTROLS_STYLE_PATH
This API is no longer necessary and/or able to be provided now that
styles are treated as regular QML modules.
Task-number: QTBUG-82922
Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-04-17 12:32:55 +00:00
|
|
|
#include <QtQuickControls2/private/qquickstyle_p.h>
|
Don't delete items we didn't create
Up until this patch, we've always deleted "old" items when a new one is
assigned. For example, the style's implementation of contentItem will
be destroyed here as it is not accessible by the user and is no longer
used:
Button {
contentItem: Item { /* ... */ }
}
This was especially important before the introduction of deferred
execution, as the "default" items would always be created, regardless
of whether the user had overridden it with one of their own items.
By deleting the old items, we free unused resources that would
otherwise persist until application shutdown (calling gc() does not
result in the items being garbage-collected, from my testing).
Although this has largely worked without issues, deleting objects
that weren't created by us in C++ is not supported. User-assigned items
can be created in QML (with JavaScriptOwnership) or C++ (with
CppOwnership), and it is up to the user and/or the QML engine to
manage the lifetime of these items.
After the introduction of deferred execution, it became possible to
skip creation of the default items altogether, meaning that there was
nothing to delete when assigning a new, user-specified item. This
requires that no ids are used in these items, as doing so prevents
deferred execution. Assuming that users avoid using ids in their items,
there should be no unused items that live unnecessarily until
application shutdown. The remaining cases where items do not get
destroyed when they should result from the following:
- Imperative assignments (e.g. assigning an item to a Button's
contentItem in Component.onCompleted). We already encourage
declarative bindings rather than imperative assignments.
- Using ids in items.
Given that these are use cases that we will advise against in the
documentation, it's an acceptable compromise.
[ChangeLog][Important Behavior Changes] Old delegate items (background,
contentItem, etc.) are no longer destroyed, as they are technically
owned by user code. Instead, they are hidden, unparented from the
control (QQuickItem parent, not QObject), and Accessible.ignored is
set to true. This prevents them from being unintentionally visible and
interfering with the accessibility tree when a new delegate item is
set.
Change-Id: I56c39a73dfee989dbe8f8b8bb33aaa187750fdb7
Task-number: QTBUG-72085
Fixes: QTBUG-70144
Fixes: QTBUG-75605
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-11-08 14:53:18 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
using namespace QQuickControlsTestUtils;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
2017-12-19 11:53:44 +00:00
|
|
|
struct ControlInfo
|
|
|
|
{
|
|
|
|
QString type;
|
|
|
|
QStringList delegates;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const ControlInfo ControlInfos[] = {
|
|
|
|
{ "AbstractButton", QStringList() << "background" << "contentItem" << "indicator" },
|
|
|
|
{ "ApplicationWindow", QStringList() << "background" },
|
|
|
|
{ "BusyIndicator", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Button", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "CheckBox", QStringList() << "contentItem" << "indicator" },
|
|
|
|
{ "CheckDelegate", QStringList() << "background" << "contentItem" << "indicator" },
|
|
|
|
{ "ComboBox", QStringList() << "background" << "contentItem" << "indicator" }, // popup not created until needed
|
|
|
|
{ "Container", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Control", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "DelayButton", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Dial", QStringList() << "background" << "handle" },
|
|
|
|
{ "Dialog", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "DialogButtonBox", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Drawer", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Frame", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "GroupBox", QStringList() << "background" << "contentItem" << "label" },
|
|
|
|
{ "ItemDelegate", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Label", QStringList() << "background" },
|
|
|
|
{ "Menu", QStringList() << "background" << "contentItem" },
|
2018-01-08 11:50:28 +00:00
|
|
|
{ "MenuBar", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "MenuBarItem", QStringList() << "background" << "contentItem" },
|
2018-01-12 12:21:56 +00:00
|
|
|
{ "MenuItem", QStringList() << "arrow" << "background" << "contentItem" << "indicator" },
|
2017-12-19 11:53:44 +00:00
|
|
|
{ "MenuSeparator", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Page", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "PageIndicator", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Pane", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Popup", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ProgressBar", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "RadioButton", QStringList() << "contentItem" << "indicator" },
|
|
|
|
{ "RadioDelegate", QStringList() << "background" << "contentItem" << "indicator" },
|
|
|
|
{ "RangeSlider", QStringList() << "background" << "first.handle" << "second.handle" },
|
|
|
|
{ "RoundButton", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ScrollBar", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ScrollIndicator", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ScrollView", QStringList() << "background" },
|
|
|
|
{ "Slider", QStringList() << "background" << "handle" },
|
|
|
|
{ "SpinBox", QStringList() << "background" << "contentItem" << "up.indicator" << "down.indicator" },
|
|
|
|
{ "StackView", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "SwipeDelegate", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "SwipeView", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "Switch", QStringList() << "contentItem" << "indicator" },
|
|
|
|
{ "SwitchDelegate", QStringList() << "background" << "contentItem" << "indicator" },
|
|
|
|
{ "TabBar", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "TabButton", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "TextField", QStringList() << "background" },
|
|
|
|
{ "TextArea", QStringList() << "background" },
|
|
|
|
{ "ToolBar", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ToolButton", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ToolSeparator", QStringList() << "background" << "contentItem" },
|
|
|
|
{ "ToolTip", QStringList() << "background" << "contentItem" },
|
2018-01-16 14:22:40 +00:00
|
|
|
{ "Tumbler", QStringList() << "background" << "contentItem" }
|
2017-12-19 11:53:44 +00:00
|
|
|
};
|
|
|
|
|
2022-12-19 03:41:00 +00:00
|
|
|
static const QString nonCustomizableWarning = ".*The current style does not support customization of this control.*";
|
|
|
|
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
class tst_customization : public QQmlDataTest
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
public:
|
|
|
|
tst_customization();
|
|
|
|
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
private slots:
|
2021-03-17 08:06:59 +00:00
|
|
|
void initTestCase() override;
|
2017-12-21 15:55:36 +00:00
|
|
|
void cleanupTestCase();
|
|
|
|
|
2022-06-07 02:23:31 +00:00
|
|
|
void init() override;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
void cleanup();
|
|
|
|
|
|
|
|
void creation_data();
|
|
|
|
void creation();
|
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
void override_data();
|
|
|
|
void override();
|
|
|
|
|
2017-12-13 12:35:52 +00:00
|
|
|
void comboPopup();
|
|
|
|
|
2022-12-19 03:41:00 +00:00
|
|
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WINDOWS)
|
|
|
|
void noCustomizationWarningsForDefaultControls_data();
|
|
|
|
void noCustomizationWarningsForDefaultControls();
|
|
|
|
#endif
|
|
|
|
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
private:
|
|
|
|
void reset();
|
|
|
|
void addHooks();
|
|
|
|
void removeHooks();
|
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
QObject* createControl(const QString &type, const QString &qml, QString *error);
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
|
|
|
QQmlEngine *engine = nullptr;
|
|
|
|
};
|
|
|
|
|
2017-12-14 10:16:13 +00:00
|
|
|
typedef QHash<QObject *, QString> QObjectNameHash;
|
|
|
|
Q_GLOBAL_STATIC(QObjectNameHash, qt_objectNames)
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
Q_GLOBAL_STATIC(QStringList, qt_createdQObjects)
|
|
|
|
Q_GLOBAL_STATIC(QStringList, qt_destroyedQObjects)
|
2017-12-19 13:47:09 +00:00
|
|
|
Q_GLOBAL_STATIC(QStringList, qt_destroyedParentQObjects)
|
2017-12-21 15:55:36 +00:00
|
|
|
static int qt_unparentedItemCount = 0;
|
|
|
|
|
|
|
|
class ItemParentListener : public QQuickItem
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
ItemParentListener()
|
|
|
|
{
|
|
|
|
m_slotIndex = metaObject()->indexOfSlot("onParentChanged()");
|
|
|
|
m_signalIndex = QMetaObjectPrivate::signalIndex(QMetaMethod::fromSignal(&QQuickItem::parentChanged));
|
|
|
|
}
|
|
|
|
|
|
|
|
int signalIndex() const { return m_signalIndex; }
|
|
|
|
int slotIndex() const { return m_slotIndex; }
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
void onParentChanged()
|
|
|
|
{
|
|
|
|
const QQuickItem *item = qobject_cast<QQuickItem *>(sender());
|
|
|
|
if (!item)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!item->parentItem())
|
|
|
|
++qt_unparentedItemCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int m_slotIndex;
|
|
|
|
int m_signalIndex;
|
|
|
|
};
|
|
|
|
static ItemParentListener *qt_itemParentListener = nullptr;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
|
|
|
extern "C" Q_DECL_EXPORT void qt_addQObject(QObject *object)
|
|
|
|
{
|
|
|
|
// objectName is not set at construction time
|
|
|
|
QObject::connect(object, &QObject::objectNameChanged, [object](const QString &objectName) {
|
2017-12-14 10:16:13 +00:00
|
|
|
QString oldObjectName = qt_objectNames()->value(object);
|
|
|
|
if (!oldObjectName.isEmpty())
|
|
|
|
qt_createdQObjects()->removeOne(oldObjectName);
|
2018-03-23 09:39:20 +00:00
|
|
|
// Only track object names from our QML files,
|
|
|
|
// not e.g. contentItem object names (like "ApplicationWindow").
|
|
|
|
if (objectName.contains("-")) {
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
qt_createdQObjects()->append(objectName);
|
2017-12-14 10:16:13 +00:00
|
|
|
qt_objectNames()->insert(object, objectName);
|
|
|
|
}
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
});
|
2017-12-21 15:55:36 +00:00
|
|
|
|
|
|
|
if (qt_itemParentListener) {
|
|
|
|
static const int signalIndex = qt_itemParentListener->signalIndex();
|
|
|
|
static const int slotIndex = qt_itemParentListener->slotIndex();
|
|
|
|
QMetaObject::connect(object, signalIndex, qt_itemParentListener, slotIndex);
|
|
|
|
}
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" Q_DECL_EXPORT void qt_removeQObject(QObject *object)
|
|
|
|
{
|
|
|
|
QString objectName = object->objectName();
|
|
|
|
if (!objectName.isEmpty())
|
|
|
|
qt_destroyedQObjects()->append(objectName);
|
2017-12-14 10:16:13 +00:00
|
|
|
qt_objectNames()->remove(object);
|
2017-12-19 13:47:09 +00:00
|
|
|
|
|
|
|
QObject *parent = object->parent();
|
|
|
|
if (parent) {
|
|
|
|
QString parentName = parent->objectName();
|
|
|
|
if (!parentName.isEmpty())
|
|
|
|
qt_destroyedParentQObjects()->append(parentName);
|
|
|
|
}
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
2022-06-07 02:23:31 +00:00
|
|
|
// We don't want to fail on warnings until QTBUG-98964 is fixed,
|
|
|
|
// as we deliberately prevent deferred execution in some of the tests here,
|
|
|
|
// which causes warnings.
|
2021-08-06 10:27:35 +00:00
|
|
|
tst_customization::tst_customization()
|
2022-06-07 02:23:31 +00:00
|
|
|
: QQmlDataTest(QT_QMLTEST_DATADIR, FailOnWarningsPolicy::DoNotFailOnWarnings)
|
2021-08-06 10:27:35 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-12-21 15:55:36 +00:00
|
|
|
void tst_customization::initTestCase()
|
|
|
|
{
|
|
|
|
QQmlDataTest::initTestCase();
|
|
|
|
|
|
|
|
qt_itemParentListener = new ItemParentListener;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::cleanupTestCase()
|
|
|
|
{
|
|
|
|
delete qt_itemParentListener;
|
|
|
|
qt_itemParentListener = nullptr;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::init()
|
|
|
|
{
|
2022-06-07 02:23:31 +00:00
|
|
|
QQmlDataTest::init();
|
|
|
|
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
engine = new QQmlEngine(this);
|
Use qmlRegisterModuleImport() to register styles
This patch completes the cumulative work done in previous patches.
- Uses qmlRegisterModuleImport() to register styles. This has some
added requirements:
- Each style must now be a QML module -- that is, it must have a
qmldir file.
- As a result of the above, the module must be available within the
QML import path in order to be found.
- The various forms of accepted style names have been reduced down to
one ("Material", "MyStyle", etc). See below for an explanation of
why.
- The following API in QQuickStyle is removed:
addStylePath(), availableStyles(), path(), stylePathList(). These
no longer make sense now that we reuse the existing QML import
system.
- Adds the tst_qquickstyleselector auto test back as "styleimports".
qmlRegisterModuleImport() vs resolvedUrl()
Previously we would use QQuickStyleSelector to select individual
QML files based on which style was set. We'd do this once when
QtQuick.Controls was first imported.
With Qt 6, and the requirement that each style be a proper QML
module, qmlRegisterModuleImport() was introduced. This allows us
to "link" one import with another. For an example of what this
looks like in practice, suppose the style was set to "MyStyle",
and the fallback to "Material". The "QtQuick.Controls" import
will be linked to "MyStyle", "MyStyle" to
"QtQuick.Controls.Material", and as a final fallback (for controls
like Action which only the Default style implements),
"QtQuick.Controls.Material" to "QtQuick.Controls.Default".
This is the same behavior as in Qt 5 (see qquickstyleselector.cpp):
// 1) requested style (e.g. "MyStyle", included in d->selectors)
// 2) fallback style (e.g. "Material", included in d->selectors)
// 3) default style (empty selector, not in d->selectors)
This is a necessary step to enable compilation of QML to C++.
Reducing the set of accepted style names
The problem
In QtQuickControls2Plugin() we need to call
QQuickStylePrivate::init(baseUrl()) in order to detect if the style
is a custom style in QQuickStyleSpec::resolve() (by checking if the
style path starts with the base URL). In Qt 5, init() is called in
QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too
late, because we need to call qmlRegisterModuleImport() in the
constructor. qmlRegisterModuleImport() itself requires the style to
have already been set in order to create the correct import URI
("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for
custom styles).
The solution
By reducing the valid forms for style names down to one:
./myapp -style MyStyle
we solve the problem of needing baseUrl() to determine if the
style is a custom style or not, but needing to call it too early
(since we now call qmlRegisterModuleImport() in
QtQuickControls2Plugin(), which itself requires the style to have
already been set). baseUrl() can't have been set before the
constructor is finished.
All of the various forms for _setting_ a style are still valid;
environment variables, qtquickcontrols2.conf, etc.
[ChangeLog][Important Behavior Changes] Custom styles must now have
a qmldir that lists the files that the style implements. For example,
for a style that only implements Button:
---
module MyStyle
Button 1.0 Button.qml
---
In addition, there is now only one valid, case-sensitive form for style
names: "Material", "MyStyle", etc.
These changes are done to help enable the compilation of QML code to
C++, as well as improve tooling capabilities.
[ChangeLog][Important Behavior Changes] The following API was removed:
- QQuickStyle::addStylePath()
- QQuickStyle::availableStyles()
- QQuickStyle::path()
- QQuickStyle::stylePathList()
- QT_QUICK_CONTROLS_STYLE_PATH
This API is no longer necessary and/or able to be provided now that
styles are treated as regular QML modules.
Task-number: QTBUG-82922
Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-04-17 12:32:55 +00:00
|
|
|
engine->addImportPath(testFile("styles"));
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
|
|
|
qtHookData[QHooks::AddQObject] = reinterpret_cast<quintptr>(&qt_addQObject);
|
|
|
|
qtHookData[QHooks::RemoveQObject] = reinterpret_cast<quintptr>(&qt_removeQObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::cleanup()
|
|
|
|
{
|
|
|
|
qtHookData[QHooks::AddQObject] = 0;
|
|
|
|
qtHookData[QHooks::RemoveQObject] = 0;
|
|
|
|
|
|
|
|
delete engine;
|
|
|
|
engine = nullptr;
|
|
|
|
|
|
|
|
qmlClearTypeRegistrations();
|
|
|
|
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::reset()
|
|
|
|
{
|
2017-12-21 15:55:36 +00:00
|
|
|
qt_unparentedItemCount = 0;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
qt_createdQObjects()->clear();
|
|
|
|
qt_destroyedQObjects()->clear();
|
2017-12-19 13:47:09 +00:00
|
|
|
qt_destroyedParentQObjects()->clear();
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
QObject* tst_customization::createControl(const QString &name, const QString &qml, QString *error)
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
{
|
|
|
|
QQmlComponent component(engine);
|
2020-03-26 16:01:51 +00:00
|
|
|
component.setData("import QtQuick; import QtQuick.Window; import QtQuick.Controls; " + name.toUtf8() + " { " + qml.toUtf8() + " }", QUrl());
|
2017-12-11 13:54:12 +00:00
|
|
|
QObject *obj = component.create();
|
|
|
|
if (!obj)
|
2017-12-14 10:16:13 +00:00
|
|
|
*error = component.errorString();
|
2017-12-11 13:54:12 +00:00
|
|
|
return obj;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::creation_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("style");
|
|
|
|
QTest::addColumn<QString>("type");
|
|
|
|
QTest::addColumn<QStringList>("delegates");
|
|
|
|
|
|
|
|
// the "empty" style does not contain any delegates
|
2017-12-19 11:53:44 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos)
|
|
|
|
QTest::newRow(qPrintable("empty:" + control.type)) << "empty" << control.type << QStringList();
|
2017-12-14 14:12:00 +00:00
|
|
|
|
|
|
|
// the "incomplete" style is missing bindings to the delegates (must be created regardless)
|
2017-12-19 11:53:44 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos)
|
|
|
|
QTest::newRow(qPrintable("incomplete:" + control.type)) << "incomplete" << control.type << control.delegates;
|
2017-12-14 14:12:00 +00:00
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
// the "identified" style has IDs in the delegates (prevents deferred execution)
|
|
|
|
for (const ControlInfo &control : ControlInfos)
|
|
|
|
QTest::newRow(qPrintable("identified:" + control.type)) << "identified" << control.type << control.delegates;
|
2017-12-14 14:12:00 +00:00
|
|
|
|
|
|
|
// the "simple" style simulates a proper style and contains bindings to/in delegates
|
2017-12-19 11:53:44 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos)
|
|
|
|
QTest::newRow(qPrintable("simple:" + control.type)) << "simple" << control.type << control.delegates;
|
2017-12-14 14:12:00 +00:00
|
|
|
|
|
|
|
// the "override" style overrides all delegates in the "simple" style
|
2017-12-19 11:53:44 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos)
|
|
|
|
QTest::newRow(qPrintable("override:" + control.type)) << "override" << control.type << control.delegates;
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::creation()
|
|
|
|
{
|
|
|
|
QFETCH(QString, style);
|
|
|
|
QFETCH(QString, type);
|
|
|
|
QFETCH(QStringList, delegates);
|
|
|
|
|
Use qmlRegisterModuleImport() to register styles
This patch completes the cumulative work done in previous patches.
- Uses qmlRegisterModuleImport() to register styles. This has some
added requirements:
- Each style must now be a QML module -- that is, it must have a
qmldir file.
- As a result of the above, the module must be available within the
QML import path in order to be found.
- The various forms of accepted style names have been reduced down to
one ("Material", "MyStyle", etc). See below for an explanation of
why.
- The following API in QQuickStyle is removed:
addStylePath(), availableStyles(), path(), stylePathList(). These
no longer make sense now that we reuse the existing QML import
system.
- Adds the tst_qquickstyleselector auto test back as "styleimports".
qmlRegisterModuleImport() vs resolvedUrl()
Previously we would use QQuickStyleSelector to select individual
QML files based on which style was set. We'd do this once when
QtQuick.Controls was first imported.
With Qt 6, and the requirement that each style be a proper QML
module, qmlRegisterModuleImport() was introduced. This allows us
to "link" one import with another. For an example of what this
looks like in practice, suppose the style was set to "MyStyle",
and the fallback to "Material". The "QtQuick.Controls" import
will be linked to "MyStyle", "MyStyle" to
"QtQuick.Controls.Material", and as a final fallback (for controls
like Action which only the Default style implements),
"QtQuick.Controls.Material" to "QtQuick.Controls.Default".
This is the same behavior as in Qt 5 (see qquickstyleselector.cpp):
// 1) requested style (e.g. "MyStyle", included in d->selectors)
// 2) fallback style (e.g. "Material", included in d->selectors)
// 3) default style (empty selector, not in d->selectors)
This is a necessary step to enable compilation of QML to C++.
Reducing the set of accepted style names
The problem
In QtQuickControls2Plugin() we need to call
QQuickStylePrivate::init(baseUrl()) in order to detect if the style
is a custom style in QQuickStyleSpec::resolve() (by checking if the
style path starts with the base URL). In Qt 5, init() is called in
QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too
late, because we need to call qmlRegisterModuleImport() in the
constructor. qmlRegisterModuleImport() itself requires the style to
have already been set in order to create the correct import URI
("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for
custom styles).
The solution
By reducing the valid forms for style names down to one:
./myapp -style MyStyle
we solve the problem of needing baseUrl() to determine if the
style is a custom style or not, but needing to call it too early
(since we now call qmlRegisterModuleImport() in
QtQuickControls2Plugin(), which itself requires the style to have
already been set). baseUrl() can't have been set before the
constructor is finished.
All of the various forms for _setting_ a style are still valid;
environment variables, qtquickcontrols2.conf, etc.
[ChangeLog][Important Behavior Changes] Custom styles must now have
a qmldir that lists the files that the style implements. For example,
for a style that only implements Button:
---
module MyStyle
Button 1.0 Button.qml
---
In addition, there is now only one valid, case-sensitive form for style
names: "Material", "MyStyle", etc.
These changes are done to help enable the compilation of QML code to
C++, as well as improve tooling capabilities.
[ChangeLog][Important Behavior Changes] The following API was removed:
- QQuickStyle::addStylePath()
- QQuickStyle::availableStyles()
- QQuickStyle::path()
- QQuickStyle::stylePathList()
- QT_QUICK_CONTROLS_STYLE_PATH
This API is no longer necessary and/or able to be provided now that
styles are treated as regular QML modules.
Task-number: QTBUG-82922
Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-04-17 12:32:55 +00:00
|
|
|
QQuickStyle::setStyle(style);
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
2017-12-14 10:16:13 +00:00
|
|
|
QString error;
|
2017-12-19 12:38:40 +00:00
|
|
|
QScopedPointer<QObject> control(createControl(type, "", &error));
|
2017-12-14 10:16:13 +00:00
|
|
|
QVERIFY2(control, qPrintable(error));
|
|
|
|
|
|
|
|
QByteArray templateType = "QQuick" + type.toUtf8();
|
|
|
|
QVERIFY2(control->inherits(templateType), qPrintable(type + " does not inherit " + templateType + " (" + control->metaObject()->className() + ")"));
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
// <control>-<style>
|
2017-12-14 14:12:00 +00:00
|
|
|
QString controlName = type.toLower() + "-" + style;
|
2017-12-19 12:38:40 +00:00
|
|
|
QCOMPARE(control->objectName(), controlName);
|
2017-12-14 14:12:00 +00:00
|
|
|
QVERIFY2(qt_createdQObjects()->removeOne(controlName), qPrintable(controlName + " was not created as expected"));
|
|
|
|
|
2022-10-06 09:30:50 +00:00
|
|
|
for (QString delegate : std::as_const(delegates)) {
|
2020-02-25 14:36:49 +00:00
|
|
|
QStringList properties = delegate.split(".", Qt::SkipEmptyParts);
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
// <control>-<delegate>-<style>(-<override>)
|
|
|
|
delegate.append("-" + style);
|
2017-12-14 14:12:00 +00:00
|
|
|
delegate.prepend(type.toLower() + "-");
|
|
|
|
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
QVERIFY2(qt_createdQObjects()->removeOne(delegate), qPrintable(delegate + " was not created as expected"));
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
// verify that the delegate instance has the expected object name
|
|
|
|
// in case of grouped properties, we must query the properties step by step
|
|
|
|
QObject *instance = control.data();
|
|
|
|
while (!properties.isEmpty()) {
|
|
|
|
QString property = properties.takeFirst();
|
|
|
|
instance = instance->property(property.toUtf8()).value<QObject *>();
|
|
|
|
QVERIFY2(instance, qPrintable("property was null: " + property));
|
|
|
|
}
|
|
|
|
QCOMPARE(instance->objectName(), delegate);
|
2017-12-14 14:12:00 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
QEXPECT_FAIL("identified:ComboBox", "ComboBox::popup with an ID is created at construction time", Continue);
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
|
|
|
|
QVERIFY2(qt_createdQObjects()->isEmpty(), qPrintable("unexpectedly created: " + qt_createdQObjects->join(", ")));
|
|
|
|
QVERIFY2(qt_destroyedQObjects()->isEmpty(), qPrintable("unexpectedly destroyed: " + qt_destroyedQObjects->join(", ") + " were unexpectedly destroyed"));
|
2017-12-19 13:47:09 +00:00
|
|
|
|
|
|
|
QVERIFY2(qt_destroyedParentQObjects()->isEmpty(), qPrintable("delegates/children of: " + qt_destroyedParentQObjects->join(", ") + " were unexpectedly destroyed"));
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
void tst_customization::override_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("style");
|
|
|
|
QTest::addColumn<QString>("type");
|
|
|
|
QTest::addColumn<QStringList>("delegates");
|
|
|
|
QTest::addColumn<QString>("nonDeferred");
|
|
|
|
QTest::addColumn<bool>("identify");
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
QTest::addColumn<bool>("controlNotCustomizable");
|
|
|
|
|
|
|
|
static const bool controlCustomizable = false;
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
// NOTE: delegates with IDs prevent deferred execution
|
|
|
|
|
|
|
|
// default delegates with IDs, override with custom delegates with no IDs
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos) {
|
|
|
|
QTest::newRow(qPrintable("identified:" + control.type)) << "identified" << control.type << control.delegates
|
|
|
|
<< "identified" << false << controlCustomizable;
|
|
|
|
}
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
// default delegates with no IDs, override with custom delegates with IDs
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos) {
|
|
|
|
QTest::newRow(qPrintable("simple:" + control.type)) << "simple" << control.type << control.delegates
|
|
|
|
<< "" << true << controlCustomizable;
|
|
|
|
}
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
// default delegates with IDs, override with custom delegates with IDs
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos) {
|
|
|
|
QTest::newRow(qPrintable("overidentified:" + control.type)) << "identified" << control.type << control.delegates
|
|
|
|
<< "identified" << true << controlCustomizable;
|
|
|
|
}
|
2017-12-19 12:38:40 +00:00
|
|
|
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
// Some styles don't support customization. Their fallbacks may, though,
|
|
|
|
// so only warn about the controls that that style provides.
|
|
|
|
const QHash<QString, QStringList> nonCustomizableControls = {
|
|
|
|
{
|
|
|
|
"macOS",
|
|
|
|
{
|
2023-10-26 05:29:48 +00:00
|
|
|
"Button", "CheckBox", "CheckDelegate", "ComboBox", "DelayButton", "Dial", "Frame",
|
2024-04-04 02:49:03 +00:00
|
|
|
"GroupBox", "ProgressBar", "RadioButton", "RadioDelegate", "SelectionRectangle",
|
2023-10-26 07:31:14 +00:00
|
|
|
"RangeSlider", "Slider", "SpinBox", "TextArea", "TextField", "TreeViewDelegate"
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
// TODO: ScrollView, ScrollBar
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Windows",
|
|
|
|
{
|
2023-10-30 08:08:30 +00:00
|
|
|
"Button", "CheckBox", "CheckDelegate", "ComboBox", "DelayButton", "Frame", "GroupBox",
|
2024-04-04 02:49:03 +00:00
|
|
|
"ProgressBar", "RadioButton", "RadioDelegate", "RangeSlider", "SelectionRectangle",
|
2023-10-31 08:18:47 +00:00
|
|
|
"ScrollBar", "Slider", "SpinBox", "Switch", "SwitchDelegate", "TextArea", "TextField"
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2018-01-08 10:41:56 +00:00
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
// test that the built-in styles don't have undesired IDs in their delegates
|
Use qmlRegisterModuleImport() to register styles
This patch completes the cumulative work done in previous patches.
- Uses qmlRegisterModuleImport() to register styles. This has some
added requirements:
- Each style must now be a QML module -- that is, it must have a
qmldir file.
- As a result of the above, the module must be available within the
QML import path in order to be found.
- The various forms of accepted style names have been reduced down to
one ("Material", "MyStyle", etc). See below for an explanation of
why.
- The following API in QQuickStyle is removed:
addStylePath(), availableStyles(), path(), stylePathList(). These
no longer make sense now that we reuse the existing QML import
system.
- Adds the tst_qquickstyleselector auto test back as "styleimports".
qmlRegisterModuleImport() vs resolvedUrl()
Previously we would use QQuickStyleSelector to select individual
QML files based on which style was set. We'd do this once when
QtQuick.Controls was first imported.
With Qt 6, and the requirement that each style be a proper QML
module, qmlRegisterModuleImport() was introduced. This allows us
to "link" one import with another. For an example of what this
looks like in practice, suppose the style was set to "MyStyle",
and the fallback to "Material". The "QtQuick.Controls" import
will be linked to "MyStyle", "MyStyle" to
"QtQuick.Controls.Material", and as a final fallback (for controls
like Action which only the Default style implements),
"QtQuick.Controls.Material" to "QtQuick.Controls.Default".
This is the same behavior as in Qt 5 (see qquickstyleselector.cpp):
// 1) requested style (e.g. "MyStyle", included in d->selectors)
// 2) fallback style (e.g. "Material", included in d->selectors)
// 3) default style (empty selector, not in d->selectors)
This is a necessary step to enable compilation of QML to C++.
Reducing the set of accepted style names
The problem
In QtQuickControls2Plugin() we need to call
QQuickStylePrivate::init(baseUrl()) in order to detect if the style
is a custom style in QQuickStyleSpec::resolve() (by checking if the
style path starts with the base URL). In Qt 5, init() is called in
QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too
late, because we need to call qmlRegisterModuleImport() in the
constructor. qmlRegisterModuleImport() itself requires the style to
have already been set in order to create the correct import URI
("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for
custom styles).
The solution
By reducing the valid forms for style names down to one:
./myapp -style MyStyle
we solve the problem of needing baseUrl() to determine if the
style is a custom style or not, but needing to call it too early
(since we now call qmlRegisterModuleImport() in
QtQuickControls2Plugin(), which itself requires the style to have
already been set). baseUrl() can't have been set before the
constructor is finished.
All of the various forms for _setting_ a style are still valid;
environment variables, qtquickcontrols2.conf, etc.
[ChangeLog][Important Behavior Changes] Custom styles must now have
a qmldir that lists the files that the style implements. For example,
for a style that only implements Button:
---
module MyStyle
Button 1.0 Button.qml
---
In addition, there is now only one valid, case-sensitive form for style
names: "Material", "MyStyle", etc.
These changes are done to help enable the compilation of QML code to
C++, as well as improve tooling capabilities.
[ChangeLog][Important Behavior Changes] The following API was removed:
- QQuickStyle::addStylePath()
- QQuickStyle::availableStyles()
- QQuickStyle::path()
- QQuickStyle::stylePathList()
- QT_QUICK_CONTROLS_STYLE_PATH
This API is no longer necessary and/or able to be provided now that
styles are treated as regular QML modules.
Task-number: QTBUG-82922
Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-04-17 12:32:55 +00:00
|
|
|
const QStringList styles = QQuickStylePrivate::builtInStyles();
|
2017-12-19 12:38:40 +00:00
|
|
|
for (const QString &style : styles) {
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
for (const ControlInfo &control : ControlInfos) {
|
|
|
|
QTest::newRow(qPrintable(style + ":" + control.type)) << style << control.type << control.delegates
|
|
|
|
<< "" << false << nonCustomizableControls.value(style).contains(control.type);
|
|
|
|
}
|
2017-12-19 12:38:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::override()
|
|
|
|
{
|
|
|
|
QFETCH(QString, style);
|
|
|
|
QFETCH(QString, type);
|
|
|
|
QFETCH(QStringList, delegates);
|
|
|
|
QFETCH(QString, nonDeferred);
|
|
|
|
QFETCH(bool, identify);
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
QFETCH(bool, controlNotCustomizable);
|
2017-12-19 12:38:40 +00:00
|
|
|
|
Use qmlRegisterModuleImport() to register styles
This patch completes the cumulative work done in previous patches.
- Uses qmlRegisterModuleImport() to register styles. This has some
added requirements:
- Each style must now be a QML module -- that is, it must have a
qmldir file.
- As a result of the above, the module must be available within the
QML import path in order to be found.
- The various forms of accepted style names have been reduced down to
one ("Material", "MyStyle", etc). See below for an explanation of
why.
- The following API in QQuickStyle is removed:
addStylePath(), availableStyles(), path(), stylePathList(). These
no longer make sense now that we reuse the existing QML import
system.
- Adds the tst_qquickstyleselector auto test back as "styleimports".
qmlRegisterModuleImport() vs resolvedUrl()
Previously we would use QQuickStyleSelector to select individual
QML files based on which style was set. We'd do this once when
QtQuick.Controls was first imported.
With Qt 6, and the requirement that each style be a proper QML
module, qmlRegisterModuleImport() was introduced. This allows us
to "link" one import with another. For an example of what this
looks like in practice, suppose the style was set to "MyStyle",
and the fallback to "Material". The "QtQuick.Controls" import
will be linked to "MyStyle", "MyStyle" to
"QtQuick.Controls.Material", and as a final fallback (for controls
like Action which only the Default style implements),
"QtQuick.Controls.Material" to "QtQuick.Controls.Default".
This is the same behavior as in Qt 5 (see qquickstyleselector.cpp):
// 1) requested style (e.g. "MyStyle", included in d->selectors)
// 2) fallback style (e.g. "Material", included in d->selectors)
// 3) default style (empty selector, not in d->selectors)
This is a necessary step to enable compilation of QML to C++.
Reducing the set of accepted style names
The problem
In QtQuickControls2Plugin() we need to call
QQuickStylePrivate::init(baseUrl()) in order to detect if the style
is a custom style in QQuickStyleSpec::resolve() (by checking if the
style path starts with the base URL). In Qt 5, init() is called in
QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too
late, because we need to call qmlRegisterModuleImport() in the
constructor. qmlRegisterModuleImport() itself requires the style to
have already been set in order to create the correct import URI
("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for
custom styles).
The solution
By reducing the valid forms for style names down to one:
./myapp -style MyStyle
we solve the problem of needing baseUrl() to determine if the
style is a custom style or not, but needing to call it too early
(since we now call qmlRegisterModuleImport() in
QtQuickControls2Plugin(), which itself requires the style to have
already been set). baseUrl() can't have been set before the
constructor is finished.
All of the various forms for _setting_ a style are still valid;
environment variables, qtquickcontrols2.conf, etc.
[ChangeLog][Important Behavior Changes] Custom styles must now have
a qmldir that lists the files that the style implements. For example,
for a style that only implements Button:
---
module MyStyle
Button 1.0 Button.qml
---
In addition, there is now only one valid, case-sensitive form for style
names: "Material", "MyStyle", etc.
These changes are done to help enable the compilation of QML code to
C++, as well as improve tooling capabilities.
[ChangeLog][Important Behavior Changes] The following API was removed:
- QQuickStyle::addStylePath()
- QQuickStyle::availableStyles()
- QQuickStyle::path()
- QQuickStyle::stylePathList()
- QT_QUICK_CONTROLS_STYLE_PATH
This API is no longer necessary and/or able to be provided now that
styles are treated as regular QML modules.
Task-number: QTBUG-82922
Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-04-17 12:32:55 +00:00
|
|
|
QQuickStyle::setStyle(style);
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
QString qml;
|
|
|
|
qml += QString("objectName: '%1-%2-override'; ").arg(type.toLower()).arg(style);
|
|
|
|
for (const QString &delegate : delegates) {
|
|
|
|
QString id = identify ? QString("id: %1;").arg(delegate) : QString();
|
|
|
|
qml += QString("%1: Item { %2 objectName: '%3-%1-%4-override' } ").arg(delegate).arg(id.replace(".", "")).arg(type.toLower()).arg(style);
|
|
|
|
}
|
|
|
|
|
Warn users when they customize native styles
Since Qt 6, the default style is no longer the Basic style, but instead
depends on the platform the application is run on. In addition, the
introduction of the native styles (which are not designed to be
customized) means that users customizing controls can run into visual
issues and not understand why.
This patch partially addresses this issue by warning when a native
control is customized (i.e. a delegate is overridden):
"qrc:/main.qml:11:22: QML QQuickItem: The current style does not
support customization of this control (property: "contentItem" item:
QQuickItem(0x1637375d210, parent=0x0, geometry=0,0 0x0)).
Please customize a non-native style (such as Basic, Fusion, Material,
etc). For more information, see:
https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference"
Ideally we'd also have qmllint integration in Creator so that users get
warnings from the IDE as they code, but that's not there yet.
The patch also updates the documentation by removing the code snippet
from the note and referring users to the existing
"Using Styles in Qt Quick Controls" section, which covers the topic in
greater detail. The snippet itself is also not considered a part of the
note, so the (online) styling looked a bit off.
[ChangeLog][Important Behavior Changes] Customization of native styles
will now result in warnings. Non-native styles (such as Basic) should
be used for customization purposes, or a custom style. If you are aware
of the risks and still want to customize these controls, you can ignore
the warnings by setting QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS
to 1.
Fixes: QTBUG-108519
Task-number: QTBUG-96733
Change-Id: Ib6dff4639bad76b228e0f31285d20db4e3207224
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2022-11-21 09:51:53 +00:00
|
|
|
for (int i = 0; i < delegates.size(); ++i) {
|
|
|
|
if (controlNotCustomizable) {
|
|
|
|
// If the control can't be customized, ensure that we inform the user of that by checking that a warning is issued.
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(nonCustomizableWarning));
|
|
|
|
} else {
|
|
|
|
// By failing on warnings, we are notified when a control is made non-customizable without also updating this test.
|
|
|
|
QTest::failOnWarning(QRegularExpression(nonCustomizableWarning));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
QString error;
|
|
|
|
QScopedPointer<QObject> control(createControl(type, qml, &error));
|
|
|
|
QVERIFY2(control, qPrintable(error));
|
|
|
|
|
2017-12-21 15:55:36 +00:00
|
|
|
// If there are no intentional IDs in the default delegates nor in the overridden custom
|
|
|
|
// delegates, no item should get un-parented during the creation process. An item being
|
|
|
|
// unparented means that a delegate got destroyed, so there must be an internal ID in one
|
|
|
|
// of the delegates in the tested style.
|
2017-12-22 12:27:08 +00:00
|
|
|
if (!identify && nonDeferred.isEmpty()) {
|
|
|
|
QEXPECT_FAIL("Universal:ApplicationWindow", "ApplicationWindow.qml contains an intentionally unparented FocusRectangle", Continue);
|
2017-12-21 15:55:36 +00:00
|
|
|
QCOMPARE(qt_unparentedItemCount, 0);
|
2017-12-22 12:27:08 +00:00
|
|
|
}
|
2017-12-21 15:55:36 +00:00
|
|
|
|
2017-12-19 12:38:40 +00:00
|
|
|
// <control>-<style>-override
|
|
|
|
QString controlName = type.toLower() + "-" + style + "-override";
|
|
|
|
QCOMPARE(control->objectName(), controlName);
|
|
|
|
QVERIFY2(qt_createdQObjects()->removeOne(controlName), qPrintable(controlName + " was not created as expected"));
|
|
|
|
|
2022-10-06 09:30:50 +00:00
|
|
|
for (QString delegate : std::as_const(delegates)) {
|
2020-02-25 14:36:49 +00:00
|
|
|
QStringList properties = delegate.split(".", Qt::SkipEmptyParts);
|
2017-12-19 12:38:40 +00:00
|
|
|
|
|
|
|
// <control>-<delegate>-<style>(-override)
|
|
|
|
delegate.append("-" + style);
|
|
|
|
delegate.prepend(type.toLower() + "-");
|
|
|
|
|
|
|
|
if (!nonDeferred.isEmpty())
|
|
|
|
QVERIFY2(qt_createdQObjects()->removeOne(delegate), qPrintable(delegate + " was not created as expected"));
|
|
|
|
|
|
|
|
delegate.append("-override");
|
|
|
|
QVERIFY2(qt_createdQObjects()->removeOne(delegate), qPrintable(delegate + " was not created as expected"));
|
|
|
|
|
|
|
|
// verify that the delegate instance has the expected object name
|
|
|
|
// in case of grouped properties, we must query the properties step by step
|
|
|
|
QObject *instance = control.data();
|
|
|
|
while (!properties.isEmpty()) {
|
|
|
|
QString property = properties.takeFirst();
|
|
|
|
instance = instance->property(property.toUtf8()).value<QObject *>();
|
|
|
|
QVERIFY2(instance, qPrintable("property was null: " + property));
|
|
|
|
}
|
|
|
|
QCOMPARE(instance->objectName(), delegate);
|
|
|
|
}
|
|
|
|
|
|
|
|
QEXPECT_FAIL("identified:ComboBox", "ComboBox::popup with an ID is created at construction time", Continue);
|
|
|
|
QEXPECT_FAIL("overidentified:ComboBox", "ComboBox::popup with an ID is created at construction time", Continue);
|
|
|
|
QVERIFY2(qt_createdQObjects()->isEmpty(), qPrintable("unexpectedly created: " + qt_createdQObjects->join(", ")));
|
|
|
|
|
|
|
|
if (!nonDeferred.isEmpty()) {
|
Don't delete items we didn't create
Up until this patch, we've always deleted "old" items when a new one is
assigned. For example, the style's implementation of contentItem will
be destroyed here as it is not accessible by the user and is no longer
used:
Button {
contentItem: Item { /* ... */ }
}
This was especially important before the introduction of deferred
execution, as the "default" items would always be created, regardless
of whether the user had overridden it with one of their own items.
By deleting the old items, we free unused resources that would
otherwise persist until application shutdown (calling gc() does not
result in the items being garbage-collected, from my testing).
Although this has largely worked without issues, deleting objects
that weren't created by us in C++ is not supported. User-assigned items
can be created in QML (with JavaScriptOwnership) or C++ (with
CppOwnership), and it is up to the user and/or the QML engine to
manage the lifetime of these items.
After the introduction of deferred execution, it became possible to
skip creation of the default items altogether, meaning that there was
nothing to delete when assigning a new, user-specified item. This
requires that no ids are used in these items, as doing so prevents
deferred execution. Assuming that users avoid using ids in their items,
there should be no unused items that live unnecessarily until
application shutdown. The remaining cases where items do not get
destroyed when they should result from the following:
- Imperative assignments (e.g. assigning an item to a Button's
contentItem in Component.onCompleted). We already encourage
declarative bindings rather than imperative assignments.
- Using ids in items.
Given that these are use cases that we will advise against in the
documentation, it's an acceptable compromise.
[ChangeLog][Important Behavior Changes] Old delegate items (background,
contentItem, etc.) are no longer destroyed, as they are technically
owned by user code. Instead, they are hidden, unparented from the
control (QQuickItem parent, not QObject), and Accessible.ignored is
set to true. This prevents them from being unintentionally visible and
interfering with the accessibility tree when a new delegate item is
set.
Change-Id: I56c39a73dfee989dbe8f8b8bb33aaa187750fdb7
Task-number: QTBUG-72085
Fixes: QTBUG-70144
Fixes: QTBUG-75605
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-11-08 14:53:18 +00:00
|
|
|
// There were items for which deferred execution was not possible.
|
2022-10-06 09:30:50 +00:00
|
|
|
for (QString delegateName : std::as_const(delegates)) {
|
Don't delete items we didn't create
Up until this patch, we've always deleted "old" items when a new one is
assigned. For example, the style's implementation of contentItem will
be destroyed here as it is not accessible by the user and is no longer
used:
Button {
contentItem: Item { /* ... */ }
}
This was especially important before the introduction of deferred
execution, as the "default" items would always be created, regardless
of whether the user had overridden it with one of their own items.
By deleting the old items, we free unused resources that would
otherwise persist until application shutdown (calling gc() does not
result in the items being garbage-collected, from my testing).
Although this has largely worked without issues, deleting objects
that weren't created by us in C++ is not supported. User-assigned items
can be created in QML (with JavaScriptOwnership) or C++ (with
CppOwnership), and it is up to the user and/or the QML engine to
manage the lifetime of these items.
After the introduction of deferred execution, it became possible to
skip creation of the default items altogether, meaning that there was
nothing to delete when assigning a new, user-specified item. This
requires that no ids are used in these items, as doing so prevents
deferred execution. Assuming that users avoid using ids in their items,
there should be no unused items that live unnecessarily until
application shutdown. The remaining cases where items do not get
destroyed when they should result from the following:
- Imperative assignments (e.g. assigning an item to a Button's
contentItem in Component.onCompleted). We already encourage
declarative bindings rather than imperative assignments.
- Using ids in items.
Given that these are use cases that we will advise against in the
documentation, it's an acceptable compromise.
[ChangeLog][Important Behavior Changes] Old delegate items (background,
contentItem, etc.) are no longer destroyed, as they are technically
owned by user code. Instead, they are hidden, unparented from the
control (QQuickItem parent, not QObject), and Accessible.ignored is
set to true. This prevents them from being unintentionally visible and
interfering with the accessibility tree when a new delegate item is
set.
Change-Id: I56c39a73dfee989dbe8f8b8bb33aaa187750fdb7
Task-number: QTBUG-72085
Fixes: QTBUG-70144
Fixes: QTBUG-75605
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2019-11-08 14:53:18 +00:00
|
|
|
if (!delegateName.contains("-"))
|
|
|
|
delegateName.append("-" + nonDeferred);
|
|
|
|
delegateName.prepend(type.toLower() + "-");
|
|
|
|
|
|
|
|
const int delegateIndex = qt_destroyedQObjects()->indexOf(delegateName);
|
|
|
|
QVERIFY2(delegateIndex == -1, qPrintable(delegateName + " was unexpectedly destroyed"));
|
|
|
|
|
|
|
|
const auto controlChildren = control->children();
|
|
|
|
const auto childIt = std::find_if(controlChildren.constBegin(), controlChildren.constEnd(), [delegateName](const QObject *child) {
|
|
|
|
return child->objectName() == delegateName;
|
|
|
|
});
|
|
|
|
// We test other delegates (like the background) here, so make sure we don't end up with XPASSes by using the wrong delegate.
|
|
|
|
if (delegateName.contains(QLatin1String("handle"))) {
|
|
|
|
QEXPECT_FAIL("identified:RangeSlider", "For some reason, items that are belong to grouped properties fail here", Abort);
|
|
|
|
QEXPECT_FAIL("overidentified:RangeSlider", "For some reason, items that are belong to grouped properties fail here", Abort);
|
|
|
|
}
|
|
|
|
if (delegateName.contains(QLatin1String("indicator"))) {
|
|
|
|
QEXPECT_FAIL("identified:SpinBox", "For some reason, items that are belong to grouped properties fail here", Abort);
|
|
|
|
QEXPECT_FAIL("overidentified:SpinBox", "For some reason, items that are belong to grouped properties fail here", Abort);
|
|
|
|
}
|
|
|
|
QVERIFY2(childIt != controlChildren.constEnd(), qPrintable(QString::fromLatin1(
|
|
|
|
"Expected delegate \"%1\" to still be a QObject child of \"%2\"").arg(delegateName).arg(controlName)));
|
|
|
|
|
|
|
|
const auto *delegate = qobject_cast<QQuickItem*>(*childIt);
|
|
|
|
// Ensure that the item is hidden, etc.
|
|
|
|
QVERIFY(delegate);
|
|
|
|
QCOMPARE(delegate->isVisible(), false);
|
|
|
|
QCOMPARE(delegate->parentItem(), nullptr);
|
2017-12-19 12:38:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QVERIFY2(qt_destroyedQObjects()->isEmpty(), qPrintable("unexpectedly destroyed: " + qt_destroyedQObjects->join(", ")));
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
}
|
|
|
|
|
2017-12-13 12:35:52 +00:00
|
|
|
void tst_customization::comboPopup()
|
|
|
|
{
|
Use qmlRegisterModuleImport() to register styles
This patch completes the cumulative work done in previous patches.
- Uses qmlRegisterModuleImport() to register styles. This has some
added requirements:
- Each style must now be a QML module -- that is, it must have a
qmldir file.
- As a result of the above, the module must be available within the
QML import path in order to be found.
- The various forms of accepted style names have been reduced down to
one ("Material", "MyStyle", etc). See below for an explanation of
why.
- The following API in QQuickStyle is removed:
addStylePath(), availableStyles(), path(), stylePathList(). These
no longer make sense now that we reuse the existing QML import
system.
- Adds the tst_qquickstyleselector auto test back as "styleimports".
qmlRegisterModuleImport() vs resolvedUrl()
Previously we would use QQuickStyleSelector to select individual
QML files based on which style was set. We'd do this once when
QtQuick.Controls was first imported.
With Qt 6, and the requirement that each style be a proper QML
module, qmlRegisterModuleImport() was introduced. This allows us
to "link" one import with another. For an example of what this
looks like in practice, suppose the style was set to "MyStyle",
and the fallback to "Material". The "QtQuick.Controls" import
will be linked to "MyStyle", "MyStyle" to
"QtQuick.Controls.Material", and as a final fallback (for controls
like Action which only the Default style implements),
"QtQuick.Controls.Material" to "QtQuick.Controls.Default".
This is the same behavior as in Qt 5 (see qquickstyleselector.cpp):
// 1) requested style (e.g. "MyStyle", included in d->selectors)
// 2) fallback style (e.g. "Material", included in d->selectors)
// 3) default style (empty selector, not in d->selectors)
This is a necessary step to enable compilation of QML to C++.
Reducing the set of accepted style names
The problem
In QtQuickControls2Plugin() we need to call
QQuickStylePrivate::init(baseUrl()) in order to detect if the style
is a custom style in QQuickStyleSpec::resolve() (by checking if the
style path starts with the base URL). In Qt 5, init() is called in
QtQuickControls2Plugin::registerTypes(), but in Qt 6 that's too
late, because we need to call qmlRegisterModuleImport() in the
constructor. qmlRegisterModuleImport() itself requires the style to
have already been set in order to create the correct import URI
("QtQuick.Controls.X" for built-in styles, "MyCustomStyle" for
custom styles).
The solution
By reducing the valid forms for style names down to one:
./myapp -style MyStyle
we solve the problem of needing baseUrl() to determine if the
style is a custom style or not, but needing to call it too early
(since we now call qmlRegisterModuleImport() in
QtQuickControls2Plugin(), which itself requires the style to have
already been set). baseUrl() can't have been set before the
constructor is finished.
All of the various forms for _setting_ a style are still valid;
environment variables, qtquickcontrols2.conf, etc.
[ChangeLog][Important Behavior Changes] Custom styles must now have
a qmldir that lists the files that the style implements. For example,
for a style that only implements Button:
---
module MyStyle
Button 1.0 Button.qml
---
In addition, there is now only one valid, case-sensitive form for style
names: "Material", "MyStyle", etc.
These changes are done to help enable the compilation of QML code to
C++, as well as improve tooling capabilities.
[ChangeLog][Important Behavior Changes] The following API was removed:
- QQuickStyle::addStylePath()
- QQuickStyle::availableStyles()
- QQuickStyle::path()
- QQuickStyle::stylePathList()
- QT_QUICK_CONTROLS_STYLE_PATH
This API is no longer necessary and/or able to be provided now that
styles are treated as regular QML modules.
Task-number: QTBUG-82922
Change-Id: I3b281131903c7c3c1cf0616eb7486a872dccd730
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2020-04-17 12:32:55 +00:00
|
|
|
QQuickStyle::setStyle("simple");
|
2017-12-13 12:35:52 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// test that ComboBox::popup is created when accessed
|
|
|
|
QQmlComponent component(engine);
|
2020-03-26 16:01:51 +00:00
|
|
|
component.setData("import QtQuick.Controls; ComboBox { }", QUrl());
|
2017-12-13 12:35:52 +00:00
|
|
|
QScopedPointer<QQuickItem> comboBox(qobject_cast<QQuickItem *>(component.create()));
|
|
|
|
QVERIFY(comboBox);
|
|
|
|
|
|
|
|
QVERIFY(!qt_createdQObjects()->contains("combobox-popup-simple"));
|
|
|
|
|
|
|
|
QObject *popup = comboBox->property("popup").value<QObject *>();
|
|
|
|
QVERIFY(popup);
|
|
|
|
QVERIFY(qt_createdQObjects()->contains("combobox-popup-simple"));
|
|
|
|
}
|
|
|
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
{
|
|
|
|
// test that ComboBox::popup is created when it becomes visible
|
|
|
|
QQuickWindow window;
|
|
|
|
window.resize(300, 300);
|
|
|
|
window.show();
|
|
|
|
window.requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
|
|
|
|
|
|
|
QQmlComponent component(engine);
|
2020-03-26 16:01:51 +00:00
|
|
|
component.setData("import QtQuick.Controls; ComboBox { }", QUrl());
|
2017-12-13 12:35:52 +00:00
|
|
|
QScopedPointer<QQuickItem> comboBox(qobject_cast<QQuickItem *>(component.create()));
|
|
|
|
QVERIFY(comboBox);
|
|
|
|
|
|
|
|
comboBox->setParentItem(window.contentItem());
|
|
|
|
QVERIFY(!qt_createdQObjects()->contains("combobox-popup-simple"));
|
|
|
|
|
|
|
|
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
|
|
|
|
QVERIFY(qt_createdQObjects()->contains("combobox-popup-simple"));
|
|
|
|
}
|
QQuickComboBox: fix popup's deferred execution
Unlike other delegates, such as background and contentItem, popup is
not unconditionally executed upon component completion. For performance
reasons, its execution is delayed until the popup is needed, that is,
the popup is accessed or shown.
When the popup is accessed, we use the current completion status via
isComponentComplete() to determine whether its execution must be
completed immediately, or if we can wait until componentComplete().
However, if the popup was accessed while the combobox itself was being
completed (used in ComboBox's bindings), the popup was never completed
because isComponentComplete() was still returning false even though the
popup was actually being indirectly accessed from componentComplete().
A simple execution order change, to complete the combobox itself before
the popup, fixes the problem.
Task-number: QTBUG-65962
Change-Id: I4764eb7e273e7f6fa1dab1a65a02b87722ee7cba
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-01-24 12:44:00 +00:00
|
|
|
|
|
|
|
reset();
|
|
|
|
|
|
|
|
{
|
|
|
|
// test that ComboBox::popup is completed upon component completion (if appropriate)
|
|
|
|
QQmlComponent component(engine);
|
2020-03-26 16:01:51 +00:00
|
|
|
component.setData("import QtQuick; import QtQuick.Controls; ComboBox { id: control; contentItem: Item { visible: !control.popup.visible } popup: Popup { property bool wasCompleted: false; Component.onCompleted: wasCompleted = true } }", QUrl());
|
QQuickComboBox: fix popup's deferred execution
Unlike other delegates, such as background and contentItem, popup is
not unconditionally executed upon component completion. For performance
reasons, its execution is delayed until the popup is needed, that is,
the popup is accessed or shown.
When the popup is accessed, we use the current completion status via
isComponentComplete() to determine whether its execution must be
completed immediately, or if we can wait until componentComplete().
However, if the popup was accessed while the combobox itself was being
completed (used in ComboBox's bindings), the popup was never completed
because isComponentComplete() was still returning false even though the
popup was actually being indirectly accessed from componentComplete().
A simple execution order change, to complete the combobox itself before
the popup, fixes the problem.
Task-number: QTBUG-65962
Change-Id: I4764eb7e273e7f6fa1dab1a65a02b87722ee7cba
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2018-01-24 12:44:00 +00:00
|
|
|
QScopedPointer<QQuickItem> comboBox(qobject_cast<QQuickItem *>(component.create()));
|
|
|
|
QVERIFY(comboBox);
|
|
|
|
|
|
|
|
QObject *popup = comboBox->property("popup").value<QObject *>();
|
|
|
|
QVERIFY(popup);
|
|
|
|
QCOMPARE(popup->property("wasCompleted"), QVariant(true));
|
|
|
|
}
|
2017-12-13 12:35:52 +00:00
|
|
|
}
|
|
|
|
|
2022-12-19 03:41:00 +00:00
|
|
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WINDOWS)
|
|
|
|
void tst_customization::noCustomizationWarningsForDefaultControls_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("style");
|
|
|
|
|
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
QTest::addRow("macOS") << "macOS";
|
|
|
|
#elif defined(Q_OS_WINDOWS)
|
|
|
|
QTest::addRow("Windows") << "Windows";
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_customization::noCustomizationWarningsForDefaultControls()
|
|
|
|
{
|
|
|
|
QFETCH(QString, style);
|
|
|
|
|
|
|
|
QQuickStyle::setStyle(style);
|
|
|
|
|
|
|
|
QTest::failOnWarning(QRegularExpression(nonCustomizableWarning));
|
|
|
|
|
|
|
|
for (const ControlInfo &controlInfo : ControlInfos) {
|
|
|
|
QString errorMessage;
|
|
|
|
QScopedPointer<QObject> control(createControl(controlInfo.type, {}, &errorMessage));
|
|
|
|
QVERIFY2(control, qPrintable(errorMessage));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Buttons: defer the execution of the delegates
In practice, deferring the execution of the delegates (until
accessed) means that the default delegate item does not get created
at all, when a custom control replaces it at construction time.
Button {
background: Rectangle { ... }
}
Before, such custom Button would never be faster than the original
one, because the default delegate was first created and then thrown
away at construction time. Originally, this was not considered a huge
problem, because the plan was to keep the default delegates so light-
weight that it wouldn't matter. However, then came the fancy styles
with shadows and effects and thus, heavier default delegates. There's
also a growing demand for more features, and the default delegates
are slowly getting heavier...
Now, after this patch, if you replace a heavy default delegate with
a light and simple custom delegate, the result is a much faster
control. For example, replacing Material style Button's background,
which has a shadow effect, with a plain Rectangle gives a ~10x boost,
because the default background with its heavy shadow effect is not
executed at all.
At the same time, deferring the execution of the default delegates
avoids troubles with asynchronous incubation, because we don't need
to destroy an object in the middle of the incubation process.
Task-number: QTBUG-50992
Change-Id: I2274bff99b9ff126d3748278d58d859222910c98
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2017-09-06 10:48:55 +00:00
|
|
|
QTEST_MAIN(tst_customization)
|
|
|
|
|
|
|
|
#include "tst_customization.moc"
|