QmlDesigner: Highlight material editor properties upon asset drag

When starting an asset drag in the assets view, highlight all
supported properties in the material editor.

Change-Id: I60935756e4c1384edcc284068163d08ebe529a05
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Mahmoud Badri
2022-05-30 15:31:13 +03:00
parent 0a84ef4b2c
commit 175343e24a
11 changed files with 101 additions and 53 deletions

View File

@@ -41,6 +41,8 @@ StudioControls.ComboBox {
onModelChanged: colorLogic.invalidate()
hasActiveDrag: comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag
// This is available in all editors.
onValueTypeChanged: {
@@ -83,16 +85,15 @@ StudioControls.ComboBox {
onEntered: (drag) => {
dropArea.assetPath = drag.getDataAsString(drag.keys[0]).split(",")[0]
drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.isSupportedDrop(dropArea.assetPath)
comboBox.hasActiveDrag = drag.accepted
drag.accepted = comboBox.backendValue !== undefined && comboBox.backendValue.hasActiveDrag
comboBox.hasActiveHoverDrag = drag.accepted
}
onExited: comboBox.hasActiveDrag = false
onExited: comboBox.hasActiveHoverDrag = false
onDropped: {
comboBox.backendValue.commitDrop(dropArea.assetPath)
comboBox.hasActiveDrag = false
comboBox.hasActiveHoverDrag = false
}
}

View File

@@ -39,7 +39,8 @@ T.ComboBox {
&& myComboBox.enabled
property bool edit: myComboBox.activeFocus && myComboBox.editable
property bool open: comboBoxPopup.opened
property bool hasActiveDrag: false
property bool hasActiveDrag: false // an item that can be dropped on the combobox is being dragged
property bool hasActiveHoverDrag: false // an item that can be dropped on the combobox is being hovered on the combobox
property bool dirty: false // user modification flag
@@ -49,6 +50,9 @@ T.ComboBox {
property alias textInput: comboBoxInput
property int borderWidth: myComboBox.hasActiveHoverDrag ? StudioTheme.Values.borderHover
: StudioTheme.Values.border
signal compressedActivated(int index, int reason)
enum ActivatedReason { EditingFinished, Other }
@@ -57,7 +61,7 @@ T.ComboBox {
height: StudioTheme.Values.defaultControlHeight
leftPadding: actionIndicator.width
rightPadding: popupIndicator.width + StudioTheme.Values.border
rightPadding: popupIndicator.width + myComboBox.borderWidth
font.pixelSize: StudioTheme.Values.myFontSize
wheelEnabled: false
@@ -87,6 +91,7 @@ T.ComboBox {
myControl: myComboBox
text: myComboBox.editText
borderWidth: myComboBox.borderWidth
onEditingFinished: {
comboBoxInput.deselect()
@@ -108,16 +113,16 @@ T.ComboBox {
myControl: myComboBox
myPopup: myComboBox.popup
x: comboBoxInput.x + comboBoxInput.width
y: StudioTheme.Values.border
width: StudioTheme.Values.checkIndicatorWidth - StudioTheme.Values.border
height: StudioTheme.Values.checkIndicatorHeight - (StudioTheme.Values.border * 2)
y: myComboBox.borderWidth
width: StudioTheme.Values.checkIndicatorWidth - myComboBox.borderWidth
height: StudioTheme.Values.checkIndicatorHeight - myComboBox.borderWidth * 2
}
background: Rectangle {
id: comboBoxBackground
color: StudioTheme.Values.themeControlBackground
border.color: StudioTheme.Values.themeControlOutline
border.width: StudioTheme.Values.border
border.width: myComboBox.borderWidth
x: actionIndicator.width
width: myComboBox.width - actionIndicator.width
height: myComboBox.height
@@ -144,7 +149,7 @@ T.ComboBox {
width: comboBoxPopup.width - comboBoxPopup.leftPadding - comboBoxPopup.rightPadding
- (comboBoxPopupScrollBar.visible ? comboBoxPopupScrollBar.contentItem.implicitWidth
+ 2 : 0) // TODO Magic number
height: StudioTheme.Values.height - 2 * StudioTheme.Values.border
height: StudioTheme.Values.height - 2 * myComboBox.borderWidth
padding: 0
enabled: model.enabled === undefined ? true : model.enabled
@@ -198,9 +203,9 @@ T.ComboBox {
popup: T.Popup {
id: comboBoxPopup
x: actionIndicator.width + StudioTheme.Values.border
x: actionIndicator.width + myComboBox.borderWidth
y: myComboBox.height
width: myComboBox.width - actionIndicator.width - (StudioTheme.Values.border * 2)
width: myComboBox.width - actionIndicator.width - myComboBox.borderWidth * 2
// TODO Setting the height on the popup solved the problem with the popup of height 0,
// but it has the problem that it sometimes extend over the border of the actual window
// and is then cut off.
@@ -208,7 +213,7 @@ T.ComboBox {
+ comboBoxPopup.bottomPadding,
myComboBox.Window.height - topMargin - bottomMargin,
StudioTheme.Values.maxComboBoxPopupHeight)
padding: StudioTheme.Values.border
padding: myComboBox.borderWidth
margins: 0 // If not defined margin will be -1
closePolicy: T.Popup.CloseOnPressOutside | T.Popup.CloseOnPressOutsideParent
| T.Popup.CloseOnEscape | T.Popup.CloseOnReleaseOutside
@@ -252,8 +257,9 @@ T.ComboBox {
PropertyChanges {
target: comboBoxBackground
color: StudioTheme.Values.themeControlBackground
border.color: hasActiveDrag ? StudioTheme.Values.themeInteraction
: StudioTheme.Values.themeControlOutline
border.color: myComboBox.hasActiveDrag ? StudioTheme.Values.themeInteraction
: StudioTheme.Values.themeControlOutline
border.width: myComboBox.borderWidth
}
},
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via

View File

@@ -34,6 +34,7 @@ TextInput {
property bool edit: textInput.activeFocus
property bool hover: mouseArea.containsMouse && textInput.enabled
property int borderWidth: StudioTheme.Values.border
z: 2
font: myControl.font
@@ -55,11 +56,11 @@ TextInput {
Rectangle {
id: textInputBackground
x: StudioTheme.Values.border
y: StudioTheme.Values.border
x: textInput.borderWidth
y: textInput.borderWidth
z: -1
width: textInput.width
height: StudioTheme.Values.height - (StudioTheme.Values.border * 2)
height: StudioTheme.Values.height - textInput.borderWidth * 2
color: StudioTheme.Values.themeControlBackground
border.width: 0
}

View File

@@ -86,6 +86,7 @@ QtObject {
property real marginTopBottom: 4
property real border: 1
property real borderHover: 3
property real maxComboBoxPopupHeight: Math.round(300 * values.scaleFactor)
property real maxTextAreaPopupHeight: Math.round(150 * values.scaleFactor)

View File

@@ -81,30 +81,20 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event)
if (obj == m_assetsWidget.data())
QMetaObject::invokeMethod(m_assetsWidget->rootObject(), "handleViewFocusOut");
} else if (event->type() == QMouseEvent::MouseMove) {
if (!m_assetsToDrag.isEmpty()) {
if (!m_assetsToDrag.isEmpty() && !m_model.isNull()) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 10) {
auto drag = new QDrag(this);
drag->setPixmap(m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128}));
QMimeData *mimeData = new QMimeData;
mimeData->setData(Constants::MIME_TYPE_ASSETS, m_assetsToDrag.join(',').toUtf8());
drag->setMimeData(mimeData);
drag->exec();
drag->deleteLater();
m_model->startDrag(mimeData,
m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128}));
m_assetsToDrag.clear();
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
m_assetsToDrag.clear();
QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator");
if (view) {
NavigatorWidget *navView = qobject_cast<NavigatorWidget *>(view);
if (navView) {
navView->setDragType("");
navView->update();
}
}
if (m_model)
m_model->endDrag();
}
return QObject::eventFilter(obj, event);

View File

@@ -29,6 +29,7 @@
#include "materialeditorcontextobject.h"
#include "propertyeditorvalue.h"
#include "materialeditortransaction.h"
#include "assetslibrarywidget.h"
#include <qmldesignerconstants.h>
#include <qmltimeline.h>
@@ -49,6 +50,7 @@
#include <coreplugin/messagebox.h>
#include <designmodewidget.h>
#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
@@ -798,6 +800,42 @@ void MaterialEditorView::customNotification(const AbstractView *view, const QStr
}
}
void QmlDesigner::MaterialEditorView::highlightSupportedProperties(bool highlight)
{
DesignerPropertyMap &propMap = m_qmlBackEnd->backendValuesPropertyMap();
const QStringList propNames = propMap.keys();
for (const QString &propName : propNames) {
if (propName.endsWith("Map")) {
QObject *propEditorValObj = propMap.value(propName).value<QObject *>();
PropertyEditorValue *propEditorVal = qobject_cast<PropertyEditorValue *>(propEditorValObj);
propEditorVal->setHasActiveDrag(highlight);
}
}
}
void MaterialEditorView::dragStarted(QMimeData *mimeData)
{
if (!mimeData->hasFormat(Constants::MIME_TYPE_ASSETS))
return;
const QStringList assetPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',');
bool isImage = Utils::anyOf(assetPaths, [] (const QString &assetPath) {
QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first;
return assetType == Constants::MIME_TYPE_ASSET_IMAGE;
});
if (!isImage) // only image assets are dnd supported
return;
highlightSupportedProperties();
}
void MaterialEditorView::dragEnded()
{
highlightSupportedProperties(false);
}
// from model to material editor
void MaterialEditorView::setValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value)
{

View File

@@ -73,6 +73,9 @@ public:
void customNotification(const AbstractView *view, const QString &identifier,
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
void dragStarted(QMimeData *mimeData) override;
void dragEnded() override;
void changeValue(const QString &name);
void changeExpression(const QString &name);
void exportPropertyAsAlias(const QString &name);
@@ -93,6 +96,7 @@ private:
static QString materialEditorResourcesPath();
void reloadQml();
void highlightSupportedProperties(bool highlight = true);
QString generateIdFromName(const QString &name);
void ensureMaterialLibraryNode();

View File

@@ -274,6 +274,19 @@ bool PropertyEditorValue::isTranslated() const
return false;
}
bool PropertyEditorValue::hasActiveDrag() const
{
return m_hasActiveDrag;
}
void PropertyEditorValue::setHasActiveDrag(bool val)
{
if (m_hasActiveDrag != val) {
m_hasActiveDrag = val;
emit hasActiveDragChanged();
}
}
static bool isAllowedSubclassType(const QString &type, const QmlDesigner::NodeMetaInfo &metaInfo)
{
if (!metaInfo.isValid())
@@ -371,18 +384,6 @@ void PropertyEditorValue::setEnumeration(const QString &scope, const QString &na
setValueWithEmit(QVariant::fromValue(newEnumeration));
}
bool PropertyEditorValue::isSupportedDrop(const QString &path)
{
QString suffix = "*." + QFileInfo(path).suffix().toLower();
if (m_modelNode.isSubclassOf("QtQuick3D.Material") && nameAsQString().endsWith("Map"))
return QmlDesigner::AssetsLibraryModel::supportedImageSuffixes().contains(suffix);
// TODO: handle support for other object properties dnd here (like image source)
return false;
}
void PropertyEditorValue::exportPropertyAsAlias()
{
emit exportPropertyAsAliasRequested(nameAsQString());

View File

@@ -82,6 +82,7 @@ class PropertyEditorValue : public QObject
Q_PROPERTY(bool isBound READ isBound NOTIFY isBoundChanged FINAL)
Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged FINAL)
Q_PROPERTY(bool isTranslated READ isTranslated NOTIFY expressionChanged FINAL)
Q_PROPERTY(bool hasActiveDrag READ hasActiveDrag WRITE setHasActiveDrag NOTIFY hasActiveDragChanged FINAL)
Q_PROPERTY(bool isIdList READ isIdList NOTIFY expressionChanged FINAL)
Q_PROPERTY(QStringList expressionAsList READ getExpressionAsList NOTIFY expressionChanged FINAL)
@@ -117,6 +118,9 @@ public:
bool isTranslated() const;
bool hasActiveDrag() const;
void setHasActiveDrag(bool val);
bool isAvailable() const;
QmlDesigner::PropertyName name() const;
@@ -148,7 +152,6 @@ public:
public slots:
void resetValue();
void setEnumeration(const QString &scope, const QString &name);
bool isSupportedDrop(const QString &path);
signals:
void valueChanged(const QString &name, const QVariant&);
@@ -164,6 +167,7 @@ signals:
void isBoundChanged();
void isValidChanged();
void isExplicitChanged();
void hasActiveDragChanged();
private:
QStringList generateStringList(const QString &string) const;
@@ -176,6 +180,7 @@ private:
bool m_isInSubState;
bool m_isInModel;
bool m_isBound;
bool m_hasActiveDrag = false;
bool m_isValid; // if the property value belongs to a non-existing complexProperty it is invalid
PropertyEditorNodeWrapper *m_complexNode;
};

View File

@@ -35,6 +35,7 @@
#include <import.h>
QT_BEGIN_NAMESPACE
class QPixmap;
class QUrl;
QT_END_NAMESPACE
@@ -44,7 +45,7 @@ namespace Internal {
class ModelPrivate;
class WriteLocker;
class NodeMetaInfoPrivate;
} //Internal
} // namespace Internal
class AnchorLine;
class ModelNode;
@@ -130,7 +131,7 @@ public:
QString generateNewId(const QString &prefixName) const;
QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const;
void startDrag(QMimeData *mimeData, const QString iconPath = {});
void startDrag(QMimeData *mimeData, const QPixmap &icon);
void endDrag();
protected:
@@ -140,4 +141,4 @@ private:
Internal::ModelPrivate *d;
};
}
} // namespace QmlDesigner

View File

@@ -1510,12 +1510,12 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP
return newId;
}
void Model::startDrag(QMimeData *mimeData, const QString iconPath)
void Model::startDrag(QMimeData *mimeData, const QPixmap &icon)
{
d->notifyDragStarted(mimeData);
auto drag = new QDrag(this);
drag->setPixmap(iconPath);
drag->setPixmap(icon);
drag->setMimeData(mimeData);
drag->exec();
drag->deleteLater();