qmllint/Quick: Warn about customizing native QtQuick.Controls styles
Certain properties in native QtQuick.Controls styles may not be overridden. This patch adds a pass to the Quick qmllint plugin to warn when a user modifies them. The pass also uses the fact that these types (except Control) don't inherit from each other to skip over checking for additional types once a matching type is found. Fixes: QTBUG-96737 Task-number: QTBUG-102277 Change-Id: I02f696aae716de45014736651385b6607eed6d15 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
fc3f4e5834
commit
894b8a605e
|
@ -49,9 +49,103 @@ void LayoutChildrenValidatorPass::run(const QQmlSA::Element &element)
|
|||
element->sourceLocation());
|
||||
}
|
||||
|
||||
ControlsNativeValidatorPass::ControlsNativeValidatorPass(QQmlSA::PassManager *manager)
|
||||
: QQmlSA::ElementPass(manager)
|
||||
{
|
||||
m_elements = {
|
||||
ControlElement { "Control",
|
||||
QStringList { "background", "contentItem", "leftPadding", "rightPadding",
|
||||
"topPadding", "bottomPadding", "horizontalPadding",
|
||||
"verticalPadding", "padding" },
|
||||
false, true },
|
||||
ControlElement { "Button", QStringList { "indicator" } },
|
||||
ControlElement {
|
||||
"ApplicationWindow",
|
||||
QStringList { "background", "contentItem", "header", "footer", "menuBar" } },
|
||||
ControlElement { "ComboBox", QStringList { "indicator" } },
|
||||
ControlElement { "Dial", QStringList { "handle" } },
|
||||
ControlElement { "Dialog", QStringList { "header", "footer" } },
|
||||
ControlElement { "GroupBox", QStringList { "label" } },
|
||||
ControlElement { "$internal$.QQuickIndicatorButton", QStringList { "indicator" }, false },
|
||||
ControlElement { "Label", QStringList { "background" } },
|
||||
ControlElement { "MenuItem", QStringList { "arrow" } },
|
||||
ControlElement { "Page", QStringList { "header", "footer" } },
|
||||
ControlElement { "Popup", QStringList { "background", "contentItem" } },
|
||||
ControlElement { "RangeSlider", QStringList { "handle" } },
|
||||
ControlElement { "Slider", QStringList { "handle" } },
|
||||
ControlElement { "$internal$.QQuickSwipe",
|
||||
QStringList { "leftItem", "behindItem", "rightItem" }, false },
|
||||
ControlElement { "TextArea", QStringList { "background" } },
|
||||
ControlElement { "TextField", QStringList { "background" } },
|
||||
};
|
||||
|
||||
for (const QString &module : { u"QtQuick.Controls.macOS"_qs, u"QtQuick.Controls.Windows"_qs }) {
|
||||
if (!manager->hasImportedModule(module))
|
||||
continue;
|
||||
|
||||
QQmlSA::Element control = resolveType(module, "Control");
|
||||
|
||||
for (ControlElement &element : m_elements) {
|
||||
auto type = resolveType(element.isInModuleControls ? module : "QtQuick.Templates",
|
||||
element.name);
|
||||
|
||||
if (type.isNull())
|
||||
continue;
|
||||
|
||||
element.inheritsControl = !element.isControl && type->inherits(control);
|
||||
element.element = type;
|
||||
}
|
||||
|
||||
m_elements.removeIf([](const ControlElement &element) { return element.element.isNull(); });
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlsNativeValidatorPass::shouldRun(const QQmlSA::Element &element)
|
||||
{
|
||||
for (const ControlElement &controlElement : m_elements) {
|
||||
// If our element inherits control, we don't have to individually check for them here.
|
||||
if (controlElement.inheritsControl)
|
||||
continue;
|
||||
if (element->inherits(controlElement.element))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ControlsNativeValidatorPass::run(const QQmlSA::Element &element)
|
||||
{
|
||||
for (const ControlElement &controlElement : m_elements) {
|
||||
if (element->inherits(controlElement.element)) {
|
||||
for (const QString &propertyName : controlElement.restrictedProperties) {
|
||||
if (element->hasOwnPropertyBindings(propertyName)) {
|
||||
emitWarning(QStringLiteral("Not allowed to override \"%1\" because native "
|
||||
"styles cannot be customized: See "
|
||||
"https://doc-snapshots.qt.io/qt6-dev/"
|
||||
"qtquickcontrols2-customize.html#customization-"
|
||||
"reference for more information.")
|
||||
.arg(propertyName),
|
||||
element->sourceLocation());
|
||||
}
|
||||
}
|
||||
// Since all the different types we have rules for don't inherit from each other (except
|
||||
// for Control) we don't have to keep checking whether other types match once we've
|
||||
// found one that has been inherited from.
|
||||
if (!controlElement.isControl)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QmlLintQuickPlugin::registerPasses(QQmlSA::PassManager *manager,
|
||||
const QQmlSA::Element &rootElement)
|
||||
{
|
||||
Q_UNUSED(rootElement);
|
||||
manager->registerElementPass(std::make_unique<LayoutChildrenValidatorPass>(manager));
|
||||
if (manager->hasImportedModule(u"QtQuick.Layouts"_qs))
|
||||
manager->registerElementPass(std::make_unique<LayoutChildrenValidatorPass>(manager));
|
||||
|
||||
if (manager->hasImportedModule(u"QtQuick.Controls.macOS"_qs)
|
||||
|| manager->hasImportedModule(u"QtQuick.Controls.Windows"_qs))
|
||||
manager->registerElementPass(std::make_unique<ControlsNativeValidatorPass>(manager));
|
||||
}
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
#ifndef QUICKLINTPLUGIN_H
|
||||
#define QUICKLINTPLUGIN_H
|
||||
|
||||
#include <QtPlugin>
|
||||
#include <QtCore/qplugin.h>
|
||||
#include <QtCore/qlist.h>
|
||||
|
||||
#include <QtQmlCompiler/private/qqmlsa_p.h>
|
||||
|
||||
class QmlLintQuickPlugin : public QObject, public QQmlSA::LintPlugin
|
||||
|
@ -54,4 +56,26 @@ private:
|
|||
QQmlSA::Element m_layout;
|
||||
};
|
||||
|
||||
class ControlsNativeValidatorPass : public QQmlSA::ElementPass
|
||||
{
|
||||
public:
|
||||
ControlsNativeValidatorPass(QQmlSA::PassManager *manager);
|
||||
|
||||
bool shouldRun(const QQmlSA::Element &element) override;
|
||||
void run(const QQmlSA::Element &element) override;
|
||||
|
||||
private:
|
||||
struct ControlElement
|
||||
{
|
||||
QString name;
|
||||
QStringList restrictedProperties;
|
||||
bool isInModuleControls = true;
|
||||
bool isControl = false;
|
||||
bool inheritsControl = false;
|
||||
QQmlSA::Element element = {};
|
||||
};
|
||||
|
||||
QList<ControlElement> m_elements;
|
||||
};
|
||||
|
||||
#endif // QUICKLINTPLUGIN_H
|
||||
|
|
Loading…
Reference in New Issue