forked from qt-creator/qt-creator
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:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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
|
||||
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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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();
|
||||
|
@@ -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());
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user