QmlDesigner: Open PropertyEditor after MaterialBrowser actions

The actions are:
* Create
* Duplicate
* Delete
* DoubleClick

Also customNotifications are removed for MaterialEditor and
TextureEditor

Task-number: QDS-14903
Change-Id: Ia56a5f6ff443b105994d98451e875001666a9767
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Ali Kianian
2025-03-11 09:33:40 +02:00
parent b9408c7ed7
commit fe2f389c38
20 changed files with 183 additions and 120 deletions

View File

@@ -230,10 +230,11 @@ Item {
if (searchBox.activeFocus) if (searchBox.activeFocus)
return return
if (!materialBrowserModel.isEmpty && rootView.materialSectionFocused && materialsSection.expanded) let materialIsFocused = !materialBrowserModel.isEmpty && rootView.materialSectionFocused && materialsSection.expanded
materialBrowserModel.openMaterialEditor() let textureIsFocused = !materialBrowserTexturesModel.isEmpty && !rootView.materialSectionFocused && texturesSection.expanded
else if (!materialBrowserTexturesModel.isEmpty && !rootView.materialSectionFocused && texturesSection.expanded)
materialBrowserTexturesModel.openTextureEditor() if (materialIsFocused || textureIsFocused)
rootView.openPropertyEditor()
} }
Keys.onEnterPressed: root.handleEnterPress() Keys.onEnterPressed: root.handleEnterPress()

View File

@@ -73,7 +73,11 @@ Item {
} }
onPressed: (mouse) => handleClick(mouse) onPressed: (mouse) => handleClick(mouse)
onDoubleClicked: MaterialBrowserBackend.materialBrowserModel.openMaterialEditor()
onDoubleClicked: {
MaterialBrowserBackend.materialBrowserModel.selectMaterial(index, false)
MaterialBrowserBackend.rootView.openPropertyEditor()
}
} }
Column { Column {

View File

@@ -44,7 +44,11 @@ Item {
} }
onPressed: (mouse) => handleClick(mouse) onPressed: (mouse) => handleClick(mouse)
onDoubleClicked: MaterialBrowserBackend.materialBrowserTexturesModel.openTextureEditor()
onDoubleClicked: {
MaterialBrowserBackend.materialBrowserTexturesModel.selectTexture(index, false)
MaterialBrowserBackend.rootView.openPropertyEditor()
}
} }
ToolTip { ToolTip {

View File

@@ -76,7 +76,7 @@ Column {
extraButtonIcon: StudioTheme.Constants.material_medium extraButtonIcon: StudioTheme.Constants.material_medium
extraButtonToolTip: qsTr("Edit material") extraButtonToolTip: qsTr("Edit material")
onExtraButtonClicked: (idx) => { backendValues.materials.openMaterialEditor(idx) } onExtraButtonClicked: (idx) => { backendValues.materials.editMaterial(idx) }
} }
ExpandingSpacer {} ExpandingSpacer {}

View File

@@ -13,6 +13,7 @@
#include <qmldesignertr.h> #include <qmldesignertr.h>
#include <qmlitemnode.h> #include <qmlitemnode.h>
#include <qmlobjectnode.h> #include <qmlobjectnode.h>
#include <uniquename.h>
#include <variantproperty.h> #include <variantproperty.h>
#include <coreplugin/messagebox.h> #include <coreplugin/messagebox.h>
@@ -474,5 +475,124 @@ void assignMaterialTo3dModel(AbstractView *view, const ModelNode &modelNode,
QmlObjectNode(modelNode).setBindingProperty("materials", newMaterialNode.id()); QmlObjectNode(modelNode).setBindingProperty("materials", newMaterialNode.id());
} }
ModelNode createMaterial(AbstractView *view)
{
QTC_ASSERT(view && view->model(), return {});
ModelNode newMatNode;
view->executeInTransaction(__FUNCTION__, [&] {
ModelNode matLib = materialLibraryNode(view);
if (!matLib.isValid())
return;
#ifdef QDS_USE_PROJECTSTORAGE
newMatNode = view->createModelNode("PrincipledMaterial");
#else
NodeMetaInfo metaInfo = view->model()->qtQuick3DPrincipledMaterialMetaInfo();
newMatNode = view->createModelNode("QtQuick3D.PrincipledMaterial",
metaInfo.majorVersion(),
metaInfo.minorVersion());
#endif
QmlObjectNode(newMatNode).setNameAndId("New Material", "material");
matLib.defaultNodeListProperty().reparentHere(newMatNode);
newMatNode.selectNode();
});
return newMatNode;
}
void renameMaterial(const ModelNode &material, const QString &newName)
{
QTC_ASSERT(material, return);
QmlObjectNode(material).setNameAndId(newName, "material");
}
void duplicateMaterial(AbstractView *view, const ModelNode &material)
{
QTC_ASSERT(view && view->model() && material, return);
TypeName matType = material.type();
QmlObjectNode sourceMat(material);
ModelNode duplicateMatNode;
QList<AbstractProperty> dynamicProps;
view->executeInTransaction(__FUNCTION__, [&] {
ModelNode matLib = Utils3D::materialLibraryNode(view);
QTC_ASSERT(matLib.isValid(), return);
// create the duplicate material
#ifdef QDS_USE_PROJECTSTORAGE
QmlObjectNode duplicateMat = view->createModelNode(matType);
#else
NodeMetaInfo metaInfo = view->model()->metaInfo(matType);
QmlObjectNode duplicateMat = view->createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion());
#endif
duplicateMatNode = duplicateMat.modelNode();
// generate and set a unique name
QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString();
if (!newName.contains("copy", Qt::CaseInsensitive))
newName.append(" copy");
const QList<ModelNode> mats = matLib.directSubModelNodesOfType(
view->model()->qtQuick3DMaterialMetaInfo());
QStringList matNames;
for (const ModelNode &mat : mats)
matNames.append(mat.variantProperty("objectName").value().toString());
newName = UniqueName::generate(newName,
[&](const QString &name) { return matNames.contains(name); });
VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName");
objNameProp.setValue(newName);
// generate and set an id
duplicateMatNode.setIdWithoutRefactoring(view->model()->generateNewId(newName, "material"));
// sync properties. Only the base state is duplicated.
const QList<AbstractProperty> props = material.properties();
for (const AbstractProperty &prop : props) {
if (prop.name() == "objectName" || prop.name() == "data")
continue;
if (prop.isVariantProperty()) {
if (prop.isDynamic()) {
dynamicProps.append(prop);
} else {
VariantProperty variantProp = duplicateMatNode.variantProperty(prop.name());
variantProp.setValue(prop.toVariantProperty().value());
}
} else if (prop.isBindingProperty()) {
if (prop.isDynamic()) {
dynamicProps.append(prop);
} else {
BindingProperty bindingProp = duplicateMatNode.bindingProperty(prop.name());
bindingProp.setExpression(prop.toBindingProperty().expression());
}
}
}
matLib.defaultNodeListProperty().reparentHere(duplicateMat);
duplicateMat.modelNode().selectNode();
});
// For some reason, creating dynamic properties in the same transaction doesn't work, so
// let's do it in separate transaction.
// TODO: Fix the issue and merge transactions (QDS-8094)
if (!dynamicProps.isEmpty()) {
view->executeInTransaction(__FUNCTION__, [&] {
for (const AbstractProperty &prop : std::as_const(dynamicProps)) {
if (prop.isVariantProperty()) {
VariantProperty variantProp = duplicateMatNode.variantProperty(prop.name());
variantProp.setDynamicTypeNameAndValue(prop.dynamicTypeName(),
prop.toVariantProperty().value());
} else if (prop.isBindingProperty()) {
BindingProperty bindingProp = duplicateMatNode.bindingProperty(prop.name());
bindingProp.setDynamicTypeNameAndExpression(prop.dynamicTypeName(),
prop.toBindingProperty().expression());
}
}
});
}
}
} // namespace Utils3D } // namespace Utils3D
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -56,6 +56,10 @@ ModelNode createMaterial(AbstractView *view, const TypeName &typeName);
ModelNode createMaterial(AbstractView *view, const NodeMetaInfo &metaInfo); ModelNode createMaterial(AbstractView *view, const NodeMetaInfo &metaInfo);
#endif #endif
ModelNode createMaterial(AbstractView *view);
void renameMaterial(const ModelNode &material, const QString &newName);
void duplicateMaterial(AbstractView *view, const ModelNode &material);
void addQuick3DImportAndView3D(AbstractView *view); void addQuick3DImportAndView3D(AbstractView *view);
void assignMaterialTo3dModel(AbstractView *view, const ModelNode &modelNode, void assignMaterialTo3dModel(AbstractView *view, const ModelNode &modelNode,
const ModelNode &materialNode = {}); const ModelNode &materialNode = {});

View File

@@ -512,11 +512,6 @@ void MaterialBrowserModel::applyToSelected(qint64 internalId, bool add)
} }
} }
void MaterialBrowserModel::openMaterialEditor()
{
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor", true);
}
// This is provided as invokable instead of property, as it is difficult to know when ModelNode // This is provided as invokable instead of property, as it is difficult to know when ModelNode
// becomes invalid. Much simpler to evaluate this on demand. // becomes invalid. Much simpler to evaluate this on demand.
bool MaterialBrowserModel::isCopiedMaterialValid() const bool MaterialBrowserModel::isCopiedMaterialValid() const

View File

@@ -80,7 +80,6 @@ public:
Q_INVOKABLE void renameMaterial(int idx, const QString &newName); Q_INVOKABLE void renameMaterial(int idx, const QString &newName);
Q_INVOKABLE void addNewMaterial(); Q_INVOKABLE void addNewMaterial();
Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false); Q_INVOKABLE void applyToSelected(qint64 internalId, bool add = false);
Q_INVOKABLE void openMaterialEditor();
Q_INVOKABLE bool isCopiedMaterialValid() const; Q_INVOKABLE bool isCopiedMaterialValid() const;
Q_INVOKABLE bool isVisible(int idx) const; Q_INVOKABLE bool isVisible(int idx) const;

View File

@@ -377,11 +377,6 @@ void MaterialBrowserTexturesModel::applyToSelectedModel(qint64 internalId)
} }
} }
void MaterialBrowserTexturesModel::openTextureEditor()
{
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("TextureEditor", true);
}
void MaterialBrowserTexturesModel::updateSceneEnvState() void MaterialBrowserTexturesModel::updateSceneEnvState()
{ {
emit updateSceneEnvStateRequested(); emit updateSceneEnvStateRequested();

View File

@@ -63,7 +63,6 @@ public:
Q_INVOKABLE void setTextureName(int idx, const QString &newName); Q_INVOKABLE void setTextureName(int idx, const QString &newName);
Q_INVOKABLE void applyToSelectedMaterial(qint64 internalId); Q_INVOKABLE void applyToSelectedMaterial(qint64 internalId);
Q_INVOKABLE void applyToSelectedModel(qint64 internalId); Q_INVOKABLE void applyToSelectedModel(qint64 internalId);
Q_INVOKABLE void openTextureEditor();
Q_INVOKABLE void updateSceneEnvState(); Q_INVOKABLE void updateSceneEnvState();
Q_INVOKABLE void updateSelectionState(); Q_INVOKABLE void updateSelectionState();
Q_INVOKABLE void applyAsLightProbe(qint64 internalId); Q_INVOKABLE void applyAsLightProbe(qint64 internalId);

View File

@@ -55,6 +55,11 @@ static QString propertyEditorResourcesPath()
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toUrlishString(); return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toUrlishString();
} }
static void openPropertyEditor()
{
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Properties");
}
MaterialBrowserView::MaterialBrowserView(AsynchronousImageCache &imageCache, MaterialBrowserView::MaterialBrowserView(AsynchronousImageCache &imageCache,
ExternalDependenciesInterface &externalDependencies) ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies} : AbstractView{externalDependencies}
@@ -85,21 +90,25 @@ WidgetInfo MaterialBrowserView::widgetInfo()
Utils3D::applyMaterialToModels(this, material, Utils3D::getSelectedModels(this), add); Utils3D::applyMaterialToModels(this, material, Utils3D::getSelectedModels(this), add);
}); });
connect(matBrowserModel, &MaterialBrowserModel::renameMaterialTriggered, this, connect(matBrowserModel,
&MaterialBrowserModel::renameMaterialTriggered,
this,
[&](const ModelNode &material, const QString &newName) { [&](const ModelNode &material, const QString &newName) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor"); Utils3D::renameMaterial(material, newName);
emitCustomNotification("rename_material", {material}, {newName}); openPropertyEditor();
}); });
connect(matBrowserModel, &MaterialBrowserModel::addNewMaterialTriggered, this, [&] { connect(matBrowserModel, &MaterialBrowserModel::addNewMaterialTriggered, this, [&] {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor"); Utils3D::createMaterial(this);
emitCustomNotification("add_new_material"); openPropertyEditor();
}); });
connect(matBrowserModel, &MaterialBrowserModel::duplicateMaterialTriggered, this, connect(matBrowserModel,
&MaterialBrowserModel::duplicateMaterialTriggered,
this,
[&](const ModelNode &material) { [&](const ModelNode &material) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor"); Utils3D::duplicateMaterial(this, material);
emitCustomNotification("duplicate_material", {material}); openPropertyEditor();
}); });
connect(matBrowserModel, &MaterialBrowserModel::pasteMaterialPropertiesTriggered, this, connect(matBrowserModel, &MaterialBrowserModel::pasteMaterialPropertiesTriggered, this,
@@ -174,10 +183,13 @@ WidgetInfo MaterialBrowserView::widgetInfo()
// custom notifications below are sent to the TextureEditor // custom notifications below are sent to the TextureEditor
MaterialBrowserTexturesModel *texturesModel = m_widget->materialBrowserTexturesModel().data(); MaterialBrowserTexturesModel *texturesModel = m_widget->materialBrowserTexturesModel().data();
connect(texturesModel, &MaterialBrowserTexturesModel::duplicateTextureTriggered, this, connect(texturesModel,
&MaterialBrowserTexturesModel::duplicateTextureTriggered,
this,
[&](const ModelNode &texture) { [&](const ModelNode &texture) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("TextureEditor"); QTC_ASSERT(texture.isValid(), return);
emitCustomNotification("duplicate_texture", {texture}); CreateTexture(this).execute(texture);
openPropertyEditor();
}); });
connect(texturesModel, &MaterialBrowserTexturesModel::applyToSelectedMaterialTriggered, this, connect(texturesModel, &MaterialBrowserTexturesModel::applyToSelectedMaterialTriggered, this,
@@ -199,8 +211,8 @@ WidgetInfo MaterialBrowserView::widgetInfo()
}); });
connect(texturesModel, &MaterialBrowserTexturesModel::addNewTextureTriggered, this, [&] { connect(texturesModel, &MaterialBrowserTexturesModel::addNewTextureTriggered, this, [&] {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("TextureEditor"); ModelNode texture = CreateTexture(this).execute();
emitCustomNotification("add_new_texture"); openPropertyEditor();
}); });
connect(texturesModel, &MaterialBrowserTexturesModel::updateSceneEnvStateRequested, this, [this] { connect(texturesModel, &MaterialBrowserTexturesModel::updateSceneEnvStateRequested, this, [this] {

View File

@@ -398,6 +398,11 @@ void MaterialBrowserWidget::addQtQuick3D()
Utils3D::addQuick3DImportAndView3D(m_materialBrowserView.data()); Utils3D::addQuick3DImportAndView3D(m_materialBrowserView.data());
} }
void MaterialBrowserWidget::openPropertyEditor()
{
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Properties", true);
}
QString MaterialBrowserWidget::qmlSourcesPath() QString MaterialBrowserWidget::qmlSourcesPath()
{ {
#ifdef SHARE_QML_PATH #ifdef SHARE_QML_PATH

View File

@@ -67,6 +67,7 @@ public:
Q_INVOKABLE void importMaterial(); Q_INVOKABLE void importMaterial();
Q_INVOKABLE void exportMaterial(int idx); Q_INVOKABLE void exportMaterial(int idx);
Q_INVOKABLE void addQtQuick3D(); Q_INVOKABLE void addQtQuick3D();
Q_INVOKABLE void openPropertyEditor();
StudioQuickWidget *quickWidget() const; StudioQuickWidget *quickWidget() const;

View File

@@ -461,27 +461,7 @@ void MaterialEditorView::handleToolBarAction(int action)
} }
case MaterialEditorContextObject::AddNewMaterial: { case MaterialEditorContextObject::AddNewMaterial: {
if (!model()) Utils3D::createMaterial(this);
break;
ModelNode newMatNode;
executeInTransaction(__FUNCTION__, [&] {
ModelNode matLib = Utils3D::materialLibraryNode(this);
if (!matLib.isValid())
return;
#ifdef QDS_USE_PROJECTSTORAGE
ModelNode newMatNode = createModelNode("PrincipledMaterial");
#else
NodeMetaInfo metaInfo = model()->qtQuick3DPrincipledMaterialMetaInfo();
newMatNode = createModelNode("QtQuick3D.PrincipledMaterial",
metaInfo.majorVersion(),
metaInfo.minorVersion());
#endif
renameMaterial(newMatNode, "New Material");
matLib.defaultNodeListProperty().reparentHere(newMatNode);
});
QTimer::singleShot(0, this, [newMatNode]() {
Utils3D::selectMaterial(newMatNode);
});
break; break;
} }
@@ -1098,19 +1078,6 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
} }
} }
void MaterialEditorView::customNotification([[maybe_unused]] const AbstractView *view,
const QString &identifier,
const QList<ModelNode> &nodeList,
const QList<QVariant> &data)
{
if (identifier == "rename_material")
renameMaterial(m_selectedMaterial, data.first().toString());
else if (identifier == "add_new_material")
handleToolBarAction(MaterialEditorContextObject::AddNewMaterial);
else if (identifier == "duplicate_material")
duplicateMaterial(nodeList.first());
}
void MaterialEditorView::nodeReparented(const ModelNode &node, void MaterialEditorView::nodeReparented(const ModelNode &node,
[[maybe_unused]] const NodeAbstractProperty &newPropertyParent, [[maybe_unused]] const NodeAbstractProperty &newPropertyParent,
[[maybe_unused]] const NodeAbstractProperty &oldPropertyParent, [[maybe_unused]] const NodeAbstractProperty &oldPropertyParent,

View File

@@ -61,8 +61,6 @@ public:
const QPixmap &pixmap, const QPixmap &pixmap,
const QByteArray &requestId) override; const QByteArray &requestId) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void nodeReparented(const ModelNode &node, void nodeReparented(const ModelNode &node,
const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &newPropertyParent,

View File

@@ -545,11 +545,11 @@ void PropertyEditorValue::commitDrop(const QString &dropData)
m_modelNode.model()->endDrag(); m_modelNode.model()->endDrag();
} }
void PropertyEditorValue::openMaterialEditor(int idx) void PropertyEditorValue::editMaterial(int idx)
{ {
if (ModelNode material = Utils3D::getMaterialOfModel(m_modelNode, idx)) { if (ModelNode material = Utils3D::getMaterialOfModel(m_modelNode, idx)) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor", true); QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Properties", true);
Utils3D::selectMaterial(material); material.selectNode();
} }
} }

View File

@@ -171,7 +171,7 @@ public:
Q_INVOKABLE bool idListRemove(int idx); Q_INVOKABLE bool idListRemove(int idx);
Q_INVOKABLE bool idListReplace(int idx, const QString &value); Q_INVOKABLE bool idListReplace(int idx, const QString &value);
Q_INVOKABLE void commitDrop(const QString &dropData); Q_INVOKABLE void commitDrop(const QString &dropData);
Q_INVOKABLE void openMaterialEditor(int idx); Q_INVOKABLE void editMaterial(int idx);
Q_INVOKABLE void setForceBound(bool b); Q_INVOKABLE void setForceBound(bool b);

View File

@@ -16,12 +16,6 @@ namespace QmlDesigner {
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
static void renameMaterial(ModelNode &material, const QString &newName)
{
QTC_ASSERT(material.isValid(), return);
QmlObjectNode(material).setNameAndId(newName, "material");
}
QmlMaterialNodeProxy::QmlMaterialNodeProxy() QmlMaterialNodeProxy::QmlMaterialNodeProxy()
: QObject() : QObject()
, m_previewUpdateTimer(this) , m_previewUpdateTimer(this)
@@ -100,29 +94,7 @@ void QmlMaterialNodeProxy::toolBarAction(int action)
} }
case ToolBarAction::AddNewMaterial: { case ToolBarAction::AddNewMaterial: {
if (!materialNode()) Utils3D::createMaterial(materialView());
break;
ModelNode newMatNode;
AbstractView *view = materialView();
view->executeInTransaction(__FUNCTION__, [&] {
ModelNode matLib = Utils3D::materialLibraryNode(view);
if (!matLib.isValid())
return;
#ifdef QDS_USE_PROJECTSTORAGE
ModelNode newMatNode = view->createModelNode("PrincipledMaterial");
#else
NodeMetaInfo metaInfo = materialView()->model()->qtQuick3DPrincipledMaterialMetaInfo();
newMatNode = materialView()->createModelNode("QtQuick3D.PrincipledMaterial",
metaInfo.majorVersion(),
metaInfo.minorVersion());
#endif
renameMaterial(newMatNode, "New Material");
Utils3D::materialLibraryNode(view).defaultNodeListProperty().reparentHere(newMatNode);
});
QTimer::singleShot(0, this, [newMatNode]() {
newMatNode.model()->setSelectedModelNodes({newMatNode});
});
break; break;
} }

View File

@@ -889,17 +889,6 @@ void TextureEditorView::duplicateTexture(const ModelNode &texture)
CreateTexture(this).execute(texture); CreateTexture(this).execute(texture);
} }
void TextureEditorView::customNotification([[maybe_unused]] const AbstractView *view,
const QString &identifier,
const QList<ModelNode> &nodeList,
[[maybe_unused]] const QList<QVariant> &data)
{
if (identifier == "add_new_texture")
handleToolBarAction(TextureEditorContextObject::AddNewTexture);
else if (identifier == "duplicate_texture")
duplicateTexture(nodeList.first());
}
void QmlDesigner::TextureEditorView::highlightSupportedProperties(bool highlight) void QmlDesigner::TextureEditorView::highlightSupportedProperties(bool highlight)
{ {
if (!m_selectedTexture.isValid()) if (!m_selectedTexture.isValid())

View File

@@ -63,8 +63,6 @@ public:
void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override; void instancePropertyChanged(const QList<QPair<ModelNode, PropertyName> > &propertyList) override;
void importsChanged(const Imports &addedImports, const Imports &removedImports) override; void importsChanged(const Imports &addedImports, const Imports &removedImports) override;
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void dragStarted(QMimeData *mimeData) override; void dragStarted(QMimeData *mimeData) override;
void dragEnded() override; void dragEnded() override;