forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/8.0' into 9.0
Conflicts: src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp Change-Id: I38f196e8f42cf11f7b613e7a723745600e35c5e9
This commit is contained in:
BIN
doc/qtdesignstudio/images/3d-view-context-menu.png
Normal file
BIN
doc/qtdesignstudio/images/3d-view-context-menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
@@ -16,10 +16,18 @@
|
||||
|
||||
\image studio-qtquick-3d-components.png "Qt Quick 3D components in Components"
|
||||
|
||||
Add a camera by dragging-and-dropping one of the camera components from
|
||||
\uicontrol Components > \uicontrol {Qt Quick 3D} > \uicontrol
|
||||
{Qt Quick 3D} to the \l {3D} view or to a 3D view in \l Navigator.
|
||||
If the cameras are not displayed in \uicontrol {Components}, add the
|
||||
To add a camera component to your UI, do one of the following:
|
||||
\list
|
||||
\li Drag a camera component from \uicontrol Components >
|
||||
\uicontrol {Qt Quick 3D} to the \l {3D} view or to
|
||||
\l Navigator > \uicontrol {View3D} > \uicontrol Scene.
|
||||
\li Right-click in the \uicontrol 3D view and select
|
||||
\uicontrol Create > \uicontrol Cameras from the context menu.
|
||||
\note You can only create \uicontrol {Camera Perspective} and
|
||||
\uicontrol {Camera Ortographic} this way.
|
||||
\endlist
|
||||
|
||||
If you cannot find the camera components in \uicontrol {Components}, add the
|
||||
\uicontrol QtQuick3D module to your project, as described in
|
||||
\l {Adding and Removing Modules}.
|
||||
|
||||
|
@@ -48,6 +48,16 @@
|
||||
Additionally, you can toggle the visibility of the grid, selection boxes,
|
||||
icon gizmos, and camera frustums in the 3D scene.
|
||||
|
||||
There is a context menu in the \uicontrol 3D view. To open it, right-click
|
||||
in the \uicontrol 3D view. From the context menu you can:
|
||||
\list
|
||||
\li Create cameras, lights, and models.
|
||||
\li Open \uicontrol {Material Editor} and edit materials.
|
||||
\li Delete components
|
||||
\endlist
|
||||
|
||||
\image 3d-view-context-menu.png
|
||||
|
||||
To refresh the contents of the \uicontrol{3D} view, press \key P or
|
||||
select the \inlineimage icons/reset.png
|
||||
(\uicontrol {Reset View}) button.
|
||||
|
@@ -12,9 +12,14 @@
|
||||
As a secondary light source, you can use \l{Setting the Light Probe}
|
||||
{image-based lighting}.
|
||||
|
||||
To add light components to your UI, drag-and-drop them from
|
||||
\uicontrol Components > \uicontrol {Qt Quick 3D} to the \l {3D} view or to
|
||||
\l Navigator > \uicontrol {Scene Environment} > \uicontrol Scene.
|
||||
To add a light component to your UI, do one of the following:
|
||||
\list
|
||||
\li Drag a light component from \uicontrol Components >
|
||||
\uicontrol {Qt Quick 3D} to the \l {3D} view or to
|
||||
\l Navigator > \uicontrol {View3D} > \uicontrol Scene.
|
||||
\li Right-click in the \uicontrol 3D view and select
|
||||
\uicontrol Create > \uicontrol Lights from the context menu.
|
||||
\endlist
|
||||
|
||||
If you cannot find the light components in \uicontrol {Components}, add the
|
||||
\uicontrol {Qt Quick 3D} module to your project as instructed in
|
||||
|
@@ -13,14 +13,21 @@
|
||||
|
||||
\image studio-3d-models.png "Various 3D models in the 3D view"
|
||||
|
||||
A Model component loads mesh data from a file. You can modify how the
|
||||
A model component loads mesh data from a file. You can modify how the
|
||||
component is shaded by using materials. For more information, see
|
||||
\l {Materials and Shaders} and \l {Creating Custom Materials}.
|
||||
|
||||
You can drag-and-drop a model from \uicontrol Components
|
||||
> \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D} to the \l {3D} view or
|
||||
to \l Navigator > \uicontrol {Scene Environment} > \uicontrol Scene. If the
|
||||
models are not displayed in \uicontrol {Components}, you should add the
|
||||
To add a model component to your UI, do one of the following:
|
||||
\list
|
||||
\li Drag a model component from \uicontrol Components >
|
||||
\uicontrol {Qt Quick 3D} to the \l {3D} view or to
|
||||
\l Navigator > \uicontrol {View3D} > \uicontrol Scene.
|
||||
\li Right-click in the \uicontrol 3D view and select
|
||||
\uicontrol Create > \uicontrol Primitives from the context menu.
|
||||
\note You can not create \uicontrol Empty models this way.
|
||||
\endlist
|
||||
|
||||
If you cannot find the model components in \uicontrol {Components}, add the
|
||||
\uicontrol QtQuick3D module to your project, as described in
|
||||
\l {Adding and Removing Modules}.
|
||||
|
||||
|
@@ -511,7 +511,7 @@ void NodeInstanceServer::setupOnlyWorkingImports(const QStringList &workingImpor
|
||||
quickView()->setContent(fileUrl(), m_importComponent, quickView()->rootObject());
|
||||
|
||||
m_importComponent->setData(componentCode.append("\nItem {}\n"), fileUrl());
|
||||
m_importComponentObject = m_importComponent->create();
|
||||
m_importComponentObject = m_importComponent->create(engine()->rootContext());
|
||||
|
||||
Q_ASSERT(m_importComponent && m_importComponentObject);
|
||||
Q_ASSERT_X(m_importComponent->errors().isEmpty(), __FUNCTION__, m_importComponent->errorString().toLatin1());
|
||||
|
@@ -94,5 +94,21 @@ bool QmlStateNodeInstance::resetStateProperty(const ObjectNodeInstance::Pointer
|
||||
return QmlPrivateGate::States::resetStateProperty(object(), target->object(), propertyName, resetValue);
|
||||
}
|
||||
|
||||
void QmlStateNodeInstance::reparent(const ObjectNodeInstance::Pointer &oldParentInstance,
|
||||
const PropertyName &oldParentProperty,
|
||||
const ObjectNodeInstance::Pointer &newParentInstance,
|
||||
const PropertyName &newParentProperty)
|
||||
{
|
||||
ServerNodeInstance oldState = nodeInstanceServer()->activeStateInstance();
|
||||
|
||||
ObjectNodeInstance::reparent(oldParentInstance,
|
||||
oldParentProperty,
|
||||
newParentInstance,
|
||||
newParentProperty);
|
||||
|
||||
if (oldState.isValid())
|
||||
oldState.activateState();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -27,6 +27,10 @@ public:
|
||||
bool updateStateBinding(const ObjectNodeInstance::Pointer &target, const PropertyName &propertyName, const QString &expression) override;
|
||||
bool resetStateProperty(const ObjectNodeInstance::Pointer &target, const PropertyName &propertyName, const QVariant &resetValue) override;
|
||||
|
||||
void reparent(const ObjectNodeInstance::Pointer &oldParentInstance,
|
||||
const PropertyName &oldParentProperty,
|
||||
const ObjectNodeInstance::Pointer &newParentInstance,
|
||||
const PropertyName &newParentProperty) override;
|
||||
|
||||
protected:
|
||||
QmlStateNodeInstance(QObject *object);
|
||||
|
@@ -15,12 +15,13 @@ Quick3DMaterialNodeInstance::~Quick3DMaterialNodeInstance()
|
||||
{
|
||||
}
|
||||
|
||||
void Quick3DMaterialNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
|
||||
InstanceContainer::NodeFlags flags)
|
||||
void Quick3DMaterialNodeInstance::invokeDummyViewCreate() const
|
||||
{
|
||||
m_dummyRootViewCreateFunction = "createViewForMaterial";
|
||||
|
||||
Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags);
|
||||
QMetaObject::invokeMethod(m_dummyRootView, "createViewForMaterial",
|
||||
Q_ARG(QVariant, QVariant::fromValue(object())),
|
||||
Q_ARG(QVariant, ""),
|
||||
Q_ARG(QVariant, ""),
|
||||
Q_ARG(QVariant, ""));
|
||||
}
|
||||
|
||||
Quick3DMaterialNodeInstance::Pointer Quick3DMaterialNodeInstance::create(QObject *object)
|
||||
|
@@ -19,11 +19,10 @@ public:
|
||||
|
||||
~Quick3DMaterialNodeInstance() override;
|
||||
static Pointer create(QObject *objectToBeWrapped);
|
||||
void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
|
||||
InstanceContainer::NodeFlags flags) override;
|
||||
|
||||
protected:
|
||||
explicit Quick3DMaterialNodeInstance(QObject *node);
|
||||
void invokeDummyViewCreate() const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -24,6 +24,12 @@ Quick3DNodeInstance::Quick3DNodeInstance(QObject *node)
|
||||
{
|
||||
}
|
||||
|
||||
void Quick3DNodeInstance::invokeDummyViewCreate() const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_dummyRootView, "createViewForNode",
|
||||
Q_ARG(QVariant, QVariant::fromValue(object())));
|
||||
}
|
||||
|
||||
Quick3DNodeInstance::~Quick3DNodeInstance()
|
||||
{
|
||||
}
|
||||
@@ -58,8 +64,6 @@ void Quick3DNodeInstance::initialize(
|
||||
}
|
||||
}
|
||||
|
||||
m_dummyRootViewCreateFunction = "createViewForNode";
|
||||
|
||||
Quick3DRenderableNodeInstance::initialize(objectNodeInstance, flags);
|
||||
#endif // QUICK3D_MODULE
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ public:
|
||||
|
||||
protected:
|
||||
explicit Quick3DNodeInstance(QObject *node);
|
||||
void invokeDummyViewCreate() const override;
|
||||
|
||||
private:
|
||||
QQuick3DNode *quick3DNode() const;
|
||||
|
@@ -28,7 +28,7 @@ Quick3DRenderableNodeInstance::~Quick3DRenderableNodeInstance()
|
||||
}
|
||||
|
||||
void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
|
||||
InstanceContainer::NodeFlags flags)
|
||||
InstanceContainer::NodeFlags flags)
|
||||
{
|
||||
#ifdef QUICK3D_MODULE
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
@@ -45,8 +45,7 @@ void Quick3DRenderableNodeInstance::initialize(const ObjectNodeInstance::Pointer
|
||||
component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml"));
|
||||
m_dummyRootView = qobject_cast<QQuickItem *>(component.create());
|
||||
|
||||
QMetaObject::invokeMethod(m_dummyRootView, m_dummyRootViewCreateFunction,
|
||||
Q_ARG(QVariant, QVariant::fromValue(object())));
|
||||
invokeDummyViewCreate();
|
||||
|
||||
nodeInstanceServer()->setRootItem(m_dummyRootView);
|
||||
}
|
||||
@@ -192,6 +191,10 @@ Qt5NodeInstanceServer *Quick3DRenderableNodeInstance::qt5NodeInstanceServer() co
|
||||
return qobject_cast<Qt5NodeInstanceServer *>(nodeInstanceServer());
|
||||
}
|
||||
|
||||
void Quick3DRenderableNodeInstance::invokeDummyViewCreate() const
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QmlDesigner
|
||||
|
||||
|
@@ -35,10 +35,8 @@ public:
|
||||
protected:
|
||||
explicit Quick3DRenderableNodeInstance(QObject *node);
|
||||
Qt5NodeInstanceServer *qt5NodeInstanceServer() const;
|
||||
virtual void invokeDummyViewCreate() const;
|
||||
|
||||
QByteArray m_dummyRootViewCreateFunction;
|
||||
|
||||
private:
|
||||
QQuickItem *m_dummyRootView = nullptr;
|
||||
};
|
||||
|
||||
|
@@ -846,6 +846,10 @@ void QuickItemNodeInstance::setPropertyVariant(const PropertyName &name, const Q
|
||||
|
||||
void QuickItemNodeInstance::setPropertyBinding(const PropertyName &name, const QString &expression)
|
||||
{
|
||||
static QList<PropertyName> anchorsTargets = {"anchors.top",
|
||||
"acnhors.bottom",
|
||||
"anchors.left",
|
||||
"achors.right"};
|
||||
if (ignoredProperties().contains(name))
|
||||
return;
|
||||
|
||||
@@ -857,7 +861,15 @@ void QuickItemNodeInstance::setPropertyBinding(const PropertyName &name, const Q
|
||||
|
||||
markRepeaterParentDirty();
|
||||
|
||||
ObjectNodeInstance::setPropertyBinding(name, expression);
|
||||
if (anchorsTargets.contains(name)) {
|
||||
//When resolving anchor targets we have to provide the root context the ids are defined in.
|
||||
QmlPrivateGate::setPropertyBinding(object(),
|
||||
context()->engine()->rootContext(),
|
||||
name,
|
||||
expression);
|
||||
} else {
|
||||
ObjectNodeInstance::setPropertyBinding(name, expression);
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
||||
|
@@ -414,10 +414,12 @@ Item {
|
||||
|
||||
width: parent.width
|
||||
|
||||
SearchBox {
|
||||
StudioControls.SearchBox {
|
||||
id: searchBox
|
||||
|
||||
width: parent.width - addAssetButton.width - 5
|
||||
|
||||
onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
|
@@ -199,10 +199,12 @@ Item {
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
SearchBox {
|
||||
StudioControls.SearchBox {
|
||||
id: searchBox
|
||||
|
||||
width: parent.width - addModuleButton.width - 5
|
||||
|
||||
onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
|
@@ -208,10 +208,12 @@ Item {
|
||||
width: root.width
|
||||
enabled: !materialBrowserModel.hasMaterialRoot && materialBrowserModel.hasQuick3DImport
|
||||
|
||||
SearchBox {
|
||||
StudioControls.SearchBox {
|
||||
id: searchBox
|
||||
|
||||
width: root.width - addMaterialButton.width
|
||||
|
||||
onSearchChanged: (searchText) => rootView.handleSearchFilterChanged(searchText)
|
||||
}
|
||||
|
||||
IconButton {
|
||||
|
@@ -26,6 +26,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StatesEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
@@ -369,6 +370,23 @@ Rectangle {
|
||||
width: stateGroupLabel.visible ? StudioTheme.Values.defaultControlWidth
|
||||
: root.width - 2 * root.padding
|
||||
|
||||
HelperWidgets.Tooltip { id: comboBoxTooltip }
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: stateGroupComboBox.hovered
|
||||
onTriggered: comboBoxTooltip.showText(stateGroupComboBox,
|
||||
hoverHandler.point.position,
|
||||
qsTr("Switch State Group"))
|
||||
}
|
||||
|
||||
onHoverChanged: {
|
||||
if (!stateGroupComboBox.hovered)
|
||||
comboBoxTooltip.hideText()
|
||||
}
|
||||
|
||||
HoverHandler { id: hoverHandler }
|
||||
|
||||
popup.onOpened: editDialog.close()
|
||||
|
||||
// currentIndex needs special treatment, because if model is changed, it will be
|
||||
@@ -398,25 +416,28 @@ Rectangle {
|
||||
spacing: StudioTheme.Values.toolbarSpacing
|
||||
leftPadding: toolBar.doubleRow ? root.padding : 0
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
HelperWidgets.AbstractButton {
|
||||
buttonIcon: StudioTheme.Constants.plus
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
tooltip: qsTr("Create State Group")
|
||||
onClicked: statesEditorModel.addStateGroup("stateGroup")
|
||||
}
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
HelperWidgets.AbstractButton {
|
||||
buttonIcon: StudioTheme.Constants.minus
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: statesEditorModel.activeStateGroupIndex !== 0
|
||||
tooltip: qsTr("Remove State Group")
|
||||
onClicked: statesEditorModel.removeStateGroup()
|
||||
}
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
HelperWidgets.AbstractButton {
|
||||
id: editButton
|
||||
buttonIcon: StudioTheme.Constants.edit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: statesEditorModel.activeStateGroupIndex !== 0
|
||||
checked: editDialog.visible
|
||||
tooltip: qsTr("Rename State Group")
|
||||
onClicked: {
|
||||
if (editDialog.opened)
|
||||
editDialog.close()
|
||||
@@ -439,20 +460,22 @@ Rectangle {
|
||||
spacing: StudioTheme.Values.toolbarSpacing
|
||||
rightPadding: root.padding
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
HelperWidgets.AbstractButton {
|
||||
buttonIcon: StudioTheme.Constants.gridView
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: !root.tinyMode
|
||||
tooltip: qsTr("Show thumbnails")
|
||||
onClicked: {
|
||||
for (var i = 0; i < statesRepeater.count; ++i)
|
||||
statesRepeater.itemAt(i).setPropertyChangesVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
HelperWidgets.AbstractButton {
|
||||
buttonIcon: StudioTheme.Constants.textFullJustification
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: !root.tinyMode
|
||||
tooltip: qsTr("Show property changes")
|
||||
onClicked: {
|
||||
for (var i = 0; i < statesRepeater.count; ++i)
|
||||
statesRepeater.itemAt(i).setPropertyChangesVisible(true)
|
||||
@@ -503,6 +526,7 @@ Rectangle {
|
||||
anchors.leftMargin: root.leftMargin
|
||||
|
||||
ScrollBar.horizontal: StateScrollBar {
|
||||
id: horizontalBar
|
||||
parent: scrollView
|
||||
x: scrollView.leftPadding
|
||||
y: scrollView.height - height
|
||||
@@ -511,6 +535,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
ScrollBar.vertical: StateScrollBar {
|
||||
id: verticalBar
|
||||
parent: scrollView
|
||||
x: scrollView.mirrored ? 0 : scrollView.width - width
|
||||
y: scrollView.topPadding
|
||||
@@ -568,6 +593,7 @@ Rectangle {
|
||||
NumberAnimation {
|
||||
properties: "x,y"
|
||||
easing.type: Easing.OutQuad
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,11 +602,6 @@ Rectangle {
|
||||
|
||||
property int grabIndex: -1
|
||||
|
||||
function executeDrop(from, to) {
|
||||
statesEditorModel.drop(from, to)
|
||||
statesRepeater.grabIndex = -1
|
||||
}
|
||||
|
||||
model: statesEditorModel
|
||||
|
||||
onItemAdded: root.responsiveResize(root.width, root.height)
|
||||
@@ -623,24 +644,27 @@ Rectangle {
|
||||
return
|
||||
}
|
||||
|
||||
statesEditorModel.move(
|
||||
(drag.source as StateThumbnail).visualIndex,
|
||||
stateThumbnail.visualIndex)
|
||||
statesEditorModel.move(dragSource.visualIndex,
|
||||
stateThumbnail.visualIndex)
|
||||
}
|
||||
|
||||
onDropped: function (drop) {
|
||||
let dragSource = (drop.source as StateThumbnail)
|
||||
let dropSource = (drop.source as StateThumbnail)
|
||||
|
||||
if (dragSource === undefined)
|
||||
if (dropSource === undefined)
|
||||
return
|
||||
|
||||
if (dragSource.extendString !== stateThumbnail.extendString
|
||||
if (dropSource.extendString !== stateThumbnail.extendString
|
||||
|| stateThumbnail.extendedState) {
|
||||
return
|
||||
}
|
||||
|
||||
statesRepeater.executeDrop(statesRepeater.grabIndex,
|
||||
stateThumbnail.visualIndex)
|
||||
if (statesRepeater.grabIndex === dropSource.visualIndex)
|
||||
return
|
||||
|
||||
statesEditorModel.drop(statesRepeater.grabIndex,
|
||||
dropSource.visualIndex)
|
||||
statesRepeater.grabIndex = -1
|
||||
}
|
||||
|
||||
// Extend Groups Visualization
|
||||
@@ -742,6 +766,10 @@ Rectangle {
|
||||
|
||||
hasWhenCondition: delegateRoot.hasWhenCondition
|
||||
|
||||
scrollViewActive: horizontalBar.active || verticalBar.active
|
||||
|
||||
dragParent: scrollView
|
||||
|
||||
// Fix ScrollView taking over the dragging event
|
||||
onGrabbing: {
|
||||
frame.interactive = false
|
||||
@@ -749,14 +777,6 @@ Rectangle {
|
||||
}
|
||||
onLetGo: frame.interactive = true
|
||||
|
||||
// Fix for ScrollView clipping while dragging of StateThumbnail
|
||||
onDragActiveChanged: {
|
||||
if (stateThumbnail.dragActive)
|
||||
parent = scrollViewWrapper
|
||||
else
|
||||
parent = delegateRoot
|
||||
}
|
||||
|
||||
stateName: delegateRoot.stateName
|
||||
thumbnailImageSource: delegateRoot.stateImageSource
|
||||
whenCondition: delegateRoot.whenConditionString
|
||||
@@ -771,15 +791,16 @@ Rectangle {
|
||||
|
||||
onClone: root.cloneState(delegateRoot.internalNodeId)
|
||||
onExtend: root.extendState(delegateRoot.internalNodeId)
|
||||
onRemove: root.deleteState(delegateRoot.internalNodeId)
|
||||
onRemove: {
|
||||
if (delegateRoot.isDefault)
|
||||
statesEditorModel.resetDefaultState()
|
||||
|
||||
root.deleteState(delegateRoot.internalNodeId)
|
||||
}
|
||||
|
||||
onStateNameFinished: statesEditorModel.renameState(
|
||||
delegateRoot.internalNodeId,
|
||||
stateThumbnail.stateName)
|
||||
|
||||
onWhenConditionFinished: statesEditorModel.setWhenCondition(
|
||||
delegateRoot.internalNodeId,
|
||||
stateThumbnail.whenCondition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,19 +36,19 @@ T.ScrollBar {
|
||||
implicitContentHeight + topPadding + bottomPadding)
|
||||
|
||||
contentItem: Rectangle {
|
||||
implicitWidth: scrollBar.interactive ? 6 : 2
|
||||
implicitHeight: scrollBar.interactive ? 6 : 2
|
||||
implicitWidth: scrollBar.interactive ? 14 : 8
|
||||
implicitHeight: scrollBar.interactive ? 14 : 8
|
||||
radius: width / 2
|
||||
opacity: 0.0
|
||||
color: scrollBar.pressed ? StudioTheme.Values.themeSliderActiveTrackHover
|
||||
: StudioTheme.Values.themeSliderHandle
|
||||
color: scrollBar.pressed ? StudioTheme.Values.themeScrollBarHandle //"#4C4C4C"//DARK
|
||||
: StudioTheme.Values.themeScrollBarTrack //"#3E3E3E"//DARK
|
||||
|
||||
states: State {
|
||||
name: "active"
|
||||
when: scrollBar.active && scrollBar.size < 1.0
|
||||
PropertyChanges {
|
||||
target: scrollBar.contentItem
|
||||
opacity: 0.75
|
||||
opacity: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -25,9 +25,9 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import QtQuick.Layouts 6.0
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -55,6 +55,10 @@ Item {
|
||||
|
||||
property bool hasWhenCondition: false
|
||||
|
||||
property bool scrollViewActive: false
|
||||
|
||||
property Item dragParent
|
||||
|
||||
property int visualIndex: 0
|
||||
|
||||
property int internalNodeId
|
||||
@@ -65,7 +69,6 @@ Item {
|
||||
signal extend
|
||||
signal remove
|
||||
signal stateNameFinished
|
||||
signal whenConditionFinished
|
||||
|
||||
signal grabbing
|
||||
signal letGo
|
||||
@@ -88,7 +91,7 @@ Item {
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
enabled: !root.baseState && !root.extendedState
|
||||
enabled: !root.baseState && !root.extendedState && !root.scrollViewActive
|
||||
onGrabChanged: function (transition, point) {
|
||||
if (transition === PointerDevice.GrabPassive
|
||||
|| transition === PointerDevice.GrabExclusive)
|
||||
@@ -148,13 +151,14 @@ Item {
|
||||
rows: 1
|
||||
spacing: stateBackground.thumbSpacing
|
||||
|
||||
StudioControls.AbstractButton {
|
||||
HelperWidgets.AbstractButton {
|
||||
id: defaultButton
|
||||
width: 50
|
||||
height: stateBackground.controlHeight
|
||||
checkedInverted: true
|
||||
buttonIcon: qsTr("Default")
|
||||
iconFont: StudioTheme.Constants.font
|
||||
tooltip: qsTr("Set State as default")
|
||||
onClicked: {
|
||||
root.defaultClicked()
|
||||
root.focusSignal()
|
||||
@@ -607,8 +611,16 @@ Item {
|
||||
running: false
|
||||
interval: 50
|
||||
repeat: false
|
||||
onTriggered: statesEditorModel.setWhenCondition(root.internalNodeId,
|
||||
bindingEditor.newWhenCondition)
|
||||
onTriggered: {
|
||||
if (whenCondition.previousCondition === bindingEditor.newWhenCondition)
|
||||
return
|
||||
|
||||
if ( bindingEditor.newWhenCondition !== "")
|
||||
statesEditorModel.setWhenCondition(root.internalNodeId,
|
||||
bindingEditor.newWhenCondition)
|
||||
else
|
||||
statesEditorModel.resetWhenCondition(root.internalNodeId)
|
||||
}
|
||||
}
|
||||
|
||||
stateModelNodeProperty: statesEditorModel.stateModelNode(root.internalNodeId)
|
||||
@@ -634,6 +646,8 @@ Item {
|
||||
indicatorVisible: true
|
||||
indicator.icon.text: StudioTheme.Constants.edit
|
||||
indicator.onClicked: {
|
||||
whenCondition.previousCondition = whenCondition.text
|
||||
|
||||
bindingEditor.showWidget()
|
||||
bindingEditor.text = whenCondition.text
|
||||
bindingEditor.prepareBindings()
|
||||
@@ -650,13 +664,18 @@ Item {
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
if (whenCondition.previousCondition === whenCondition.text)
|
||||
// The check for contenxtMenuAboutToShow is necessary in order to make a the
|
||||
// popup stay open if the when condition was changed. Otherwise editingFinished
|
||||
// will be called and the model will be reset. The popup will trigger a focus
|
||||
// change and editingFinished is triggered.
|
||||
if (whenCondition.previousCondition === whenCondition.text
|
||||
|| whenCondition.contextMenuAboutToShow)
|
||||
return
|
||||
|
||||
whenCondition.previousCondition = whenCondition.text
|
||||
|
||||
if (whenCondition.text !== "")
|
||||
root.whenConditionFinished()
|
||||
statesEditorModel.setWhenCondition(root.internalNodeId, root.whenCondition)
|
||||
else
|
||||
statesEditorModel.resetWhenCondition(root.internalNodeId)
|
||||
|
||||
@@ -750,6 +769,11 @@ Item {
|
||||
name: "drag"
|
||||
when: dragHandler.active
|
||||
|
||||
ParentChange {
|
||||
target: root
|
||||
parent: root.dragParent
|
||||
}
|
||||
|
||||
AnchorChanges {
|
||||
target: root
|
||||
anchors.horizontalCenter: undefined
|
||||
|
@@ -0,0 +1,109 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import HelperWidgets 2.0
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme 1.0 as StudioTheme
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Section {
|
||||
caption: qsTr("Timer")
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
SectionLayout {
|
||||
PropertyLabel {
|
||||
text: qsTr("Interval")
|
||||
tooltip: qsTr("Sets the interval between triggers, in milliseconds.")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
SpinBox {
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
minimumValue: 0
|
||||
maximumValue: 9999999
|
||||
backendValue: backendValues.interval
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Repeat")
|
||||
tooltip: qsTr("Sets whether the timer is triggered repeatedly at the specified interval or just once.")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.repeat.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.repeat
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Running")
|
||||
tooltip: qsTr("Sets whether the timer is running or not.")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.running.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.running
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
|
||||
PropertyLabel {
|
||||
text: qsTr("Triggered on start")
|
||||
tooltip: qsTr("Sets the timer to trigger when started.")
|
||||
}
|
||||
|
||||
SecondColumnLayout {
|
||||
CheckBox {
|
||||
text: backendValues.triggeredOnStart.valueToString
|
||||
implicitWidth: StudioTheme.Values.twoControlColumnWidth
|
||||
+ StudioTheme.Values.actionIndicatorWidth
|
||||
backendValue: backendValues.triggeredOnStart
|
||||
}
|
||||
|
||||
ExpandingSpacer {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -135,6 +135,7 @@ Section {
|
||||
BoolButtonRowButton {
|
||||
visible: section.showEasingCurve
|
||||
buttonIcon: StudioTheme.Constants.curveDesigner
|
||||
checkable: false
|
||||
|
||||
EasingCurveEditor {
|
||||
id: easingCurveEditor
|
||||
|
@@ -266,15 +266,6 @@ SecondColumnLayout {
|
||||
id: spacer
|
||||
}
|
||||
|
||||
StudioControls.Menu {
|
||||
id: contextMenu
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Open Color Dialog")
|
||||
onTriggered: colorPalette.showColorDialog(colorEditor.color)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: popupLoader.determineActiveColorMode()
|
||||
|
||||
onBackendValueChanged: {
|
||||
|
@@ -178,6 +178,15 @@ T.Popup {
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.Menu {
|
||||
id: contextMenu
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("Open Color Dialog")
|
||||
onTriggered: colorPalette.showColorDialog(colorEditor.color)
|
||||
}
|
||||
}
|
||||
|
||||
GradientModel {
|
||||
id: gradientModel
|
||||
anchorBackendProperty: anchorBackend
|
||||
|
@@ -61,7 +61,6 @@ RoundedPanel 2.0 RoundedPanel.qml
|
||||
ScrollView 2.0 ScrollView.qml
|
||||
SecondColumnLayout 2.0 SecondColumnLayout.qml
|
||||
Section 2.0 Section.qml
|
||||
SearchBox 2.0 SearchBox.qml
|
||||
SectionLayout 2.0 SectionLayout.qml
|
||||
Spacer 2.0 Spacer.qml
|
||||
SpinBox 2.0 SpinBox.qml
|
||||
|
@@ -11,6 +11,8 @@ Item {
|
||||
|
||||
property alias text: searchFilterText.text
|
||||
|
||||
signal searchChanged(string searchText);
|
||||
|
||||
function clear()
|
||||
{
|
||||
searchFilterText.text = "";
|
||||
@@ -58,7 +60,7 @@ Item {
|
||||
selectByMouse: true
|
||||
hoverEnabled: true
|
||||
|
||||
onTextChanged: rootView.handleSearchfilterChanged(text)
|
||||
onTextChanged: root.searchChanged(text)
|
||||
|
||||
Label {
|
||||
text: StudioTheme.Constants.search
|
@@ -28,6 +28,8 @@ T.TextField {
|
||||
|
||||
property string preFocusText: ""
|
||||
|
||||
property bool contextMenuAboutToShow: false
|
||||
|
||||
horizontalAlignment: Qt.AlignLeft
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
@@ -40,7 +42,7 @@ T.TextField {
|
||||
|
||||
readOnly: false
|
||||
selectByMouse: true
|
||||
persistentSelection: focus // QTBUG-73807
|
||||
persistentSelection: contextMenu.visible || root.focus
|
||||
clip: true
|
||||
|
||||
width: StudioTheme.Values.defaultControlWidth
|
||||
@@ -56,28 +58,28 @@ T.TextField {
|
||||
enabled: true
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: function(mouse) {
|
||||
if (mouse.button === Qt.RightButton)
|
||||
contextMenu.popup(root)
|
||||
|
||||
mouse.accepted = false
|
||||
}
|
||||
}
|
||||
|
||||
onPersistentSelectionChanged: {
|
||||
if (!persistentSelection)
|
||||
root.deselect()
|
||||
onPressed: function(event) {
|
||||
if (event.button === Qt.RightButton)
|
||||
contextMenu.popup(root)
|
||||
}
|
||||
|
||||
ContextMenu {
|
||||
id: contextMenu
|
||||
myTextEdit: root
|
||||
|
||||
onClosed: root.forceActiveFocus()
|
||||
onAboutToShow: root.contextMenuAboutToShow = true
|
||||
onAboutToHide: root.contextMenuAboutToShow = false
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (root.activeFocus)
|
||||
// OtherFocusReason in this case means, if the TextField gets focus after the context menu
|
||||
// was closed due to an menu item click.
|
||||
if (root.activeFocus && root.focusReason !== Qt.OtherFocusReason)
|
||||
root.preFocusText = root.text
|
||||
}
|
||||
|
||||
@@ -143,7 +145,7 @@ T.TextField {
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
when: root.enabled && !root.hover && !root.edit
|
||||
when: root.enabled && !root.hover && !root.edit && !contextMenu.visible
|
||||
PropertyChanges {
|
||||
target: textFieldBackground
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
@@ -162,7 +164,7 @@ T.TextField {
|
||||
State {
|
||||
name: "globalHover"
|
||||
when: (actionIndicator.hover || translationIndicator.hover || indicator.hover)
|
||||
&& !root.edit && root.enabled
|
||||
&& !root.edit && root.enabled && !contextMenu.visible
|
||||
PropertyChanges {
|
||||
target: textFieldBackground
|
||||
color: StudioTheme.Values.themeControlBackgroundGlobalHover
|
||||
@@ -177,7 +179,7 @@ T.TextField {
|
||||
State {
|
||||
name: "hover"
|
||||
when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover
|
||||
&& !indicator.hover && !root.edit && root.enabled
|
||||
&& !indicator.hover && !root.edit && root.enabled && !contextMenu.visible
|
||||
PropertyChanges {
|
||||
target: textFieldBackground
|
||||
color: StudioTheme.Values.themeControlBackgroundHover
|
||||
@@ -191,7 +193,7 @@ T.TextField {
|
||||
},
|
||||
State {
|
||||
name: "edit"
|
||||
when: root.edit
|
||||
when: root.edit || contextMenu.visible
|
||||
PropertyChanges {
|
||||
target: textFieldBackground
|
||||
color: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
|
@@ -30,6 +30,7 @@ RealSpinBoxIndicator 1.0 RealSpinBoxIndicator.qml
|
||||
RealSpinBoxInput 1.0 RealSpinBoxInput.qml
|
||||
ScrollBar 1.0 ScrollBar.qml
|
||||
ScrollView 1.0 ScrollView.qml
|
||||
SearchBox 1.0 SearchBox.qml
|
||||
SecondColumnLayout 1.0 SecondColumnLayout.qml
|
||||
Section 1.0 Section.qml
|
||||
SectionLabel 1.0 SectionLabel.qml
|
||||
|
@@ -84,8 +84,8 @@ DSsliderHandleHover=ff606060
|
||||
DSsliderHandleFocus=ff0492c9
|
||||
DSsliderHandleInteraction=ff2aafd3
|
||||
|
||||
DSscrollBarTrack=ff404040
|
||||
DSscrollBarHandle=ff505050
|
||||
DSscrollBarTrack=ff3E3E3E
|
||||
DSscrollBarHandle=ff4C4C4C
|
||||
|
||||
DSsectionHeadBackground=ff1f1f1f
|
||||
|
||||
|
@@ -86,8 +86,8 @@ DSsliderHandleHover=ff606060
|
||||
DSsliderHandleFocus=ff0492c9
|
||||
DSsliderHandleInteraction=ff2aafd3
|
||||
|
||||
DSscrollBarTrack=ff404040
|
||||
DSscrollBarHandle=ff505050
|
||||
DSscrollBarTrack=ff3E3E3E
|
||||
DSscrollBarHandle=ff4C4C4C
|
||||
|
||||
DSsectionHeadBackground=ff1f1f1f
|
||||
|
||||
|
@@ -88,8 +88,8 @@ DSsliderHandleHover=ff606060
|
||||
DSsliderHandleFocus=ff0492c9
|
||||
DSsliderHandleInteraction=ff2aafd3
|
||||
|
||||
DSscrollBarTrack=ff404040
|
||||
DSscrollBarHandle=ff505050
|
||||
DSscrollBarTrack=ff3E3E3E
|
||||
DSscrollBarHandle=ff4C4C4C
|
||||
|
||||
DSsectionHeadBackground=ff1f1f1f
|
||||
|
||||
|
@@ -82,8 +82,8 @@ DSsliderHandleHover=ff606060
|
||||
DSsliderHandleFocus=ff0492c9
|
||||
DSsliderHandleInteraction=ff2aafd3
|
||||
|
||||
DSscrollBarTrack=ff404040
|
||||
DSscrollBarHandle=ff505050
|
||||
DSscrollBarTrack=ff3E3E3E
|
||||
DSscrollBarHandle=ff4C4C4C
|
||||
|
||||
DSsectionHeadBackground=ff1f1f1f
|
||||
|
||||
|
@@ -166,7 +166,7 @@ QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets()
|
||||
return {};
|
||||
}
|
||||
|
||||
void AssetsLibraryWidget::handleSearchfilterChanged(const QString &filterText)
|
||||
void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText)
|
||||
{
|
||||
if (filterText == m_filterText || (m_assetsModel->isEmpty() && filterText.contains(m_filterText)))
|
||||
return;
|
||||
|
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos);
|
||||
Q_INVOKABLE void handleAddAsset();
|
||||
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
|
||||
Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
|
||||
Q_INVOKABLE void handleExtFilesDrop(const QList<QUrl> &simpleFilePaths,
|
||||
const QList<QUrl> &complexFilePaths,
|
||||
const QString &targetDirPath = {});
|
||||
|
@@ -292,4 +292,68 @@ void ActionEditor::updateWindowName(const QString &targetName)
|
||||
}
|
||||
}
|
||||
|
||||
void ActionEditor::invokeEditor(SignalHandlerProperty signalHandler,
|
||||
std::function<void(SignalHandlerProperty)> onReject,
|
||||
QObject * parent)
|
||||
{
|
||||
if (!signalHandler.isValid())
|
||||
return;
|
||||
|
||||
ModelNode connectionNode = signalHandler.parentModelNode();
|
||||
if (!connectionNode.isValid())
|
||||
return;
|
||||
|
||||
if (!connectionNode.bindingProperty("target").isValid())
|
||||
return;
|
||||
|
||||
ModelNode targetNode = connectionNode.bindingProperty("target").resolveToModelNode();
|
||||
if (!targetNode.isValid())
|
||||
return;
|
||||
|
||||
const QString source = signalHandler.source();
|
||||
|
||||
QPointer<ActionEditor> editor = new ActionEditor(parent);
|
||||
|
||||
editor->showWidget();
|
||||
editor->setModelNode(connectionNode);
|
||||
editor->setConnectionValue(source);
|
||||
editor->prepareConnections();
|
||||
editor->updateWindowName(targetNode.validId() + "." + signalHandler.name());
|
||||
|
||||
QObject::connect(editor, &ActionEditor::accepted, [=]() {
|
||||
if (!editor)
|
||||
return;
|
||||
if (editor->m_modelNode.isValid()) {
|
||||
editor->m_modelNode.view()->executeInTransaction("ActionEditor::"
|
||||
"invokeEditorAccepted",
|
||||
[=]() {
|
||||
editor->m_modelNode
|
||||
.signalHandlerProperty(
|
||||
signalHandler.name())
|
||||
.setSource(
|
||||
editor->connectionValue());
|
||||
});
|
||||
}
|
||||
|
||||
//closing editor widget somewhy triggers rejected() signal. Lets disconect before it affects us:
|
||||
editor->disconnect();
|
||||
editor->deleteLater();
|
||||
});
|
||||
|
||||
QObject::connect(editor, &ActionEditor::rejected, [=]() {
|
||||
if (!editor)
|
||||
return;
|
||||
|
||||
if (onReject) {
|
||||
editor->m_modelNode.view()->executeInTransaction("ActionEditor::"
|
||||
"invokeEditorOnRejectFunc",
|
||||
[=]() { onReject(signalHandler); });
|
||||
}
|
||||
|
||||
//closing editor widget somewhy triggers rejected() signal 2nd time. Lets disconect before it affects us:
|
||||
editor->disconnect();
|
||||
editor->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
} // QmlDesigner namespace
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <bindingeditor/actioneditordialog.h>
|
||||
#include <qmldesignercorelib_global.h>
|
||||
#include <modelnode.h>
|
||||
#include <signalhandlerproperty.h>
|
||||
|
||||
#include <QtQml>
|
||||
#include <QObject>
|
||||
@@ -44,6 +45,10 @@ public:
|
||||
|
||||
Q_INVOKABLE void updateWindowName(const QString &targetName = {});
|
||||
|
||||
static void invokeEditor(SignalHandlerProperty signalHandler,
|
||||
std::function<void(SignalHandlerProperty)> onReject = nullptr,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void accepted();
|
||||
void rejected();
|
||||
|
@@ -12,6 +12,7 @@ namespace ComponentCoreConstants {
|
||||
const char rootCategory[] = "";
|
||||
|
||||
const char selectionCategory[] = "Selection";
|
||||
const char connectionsCategory[] = "Connections";
|
||||
const char arrangeCategory[] = "Arrange";
|
||||
const char qmlPreviewCategory[] = "QmlPreview";
|
||||
const char editCategory[] = "Edit";
|
||||
@@ -76,6 +77,7 @@ const char openSignalDialogCommandId[] = "OpenSignalDialog";
|
||||
const char update3DAssetCommandId[] = "Update3DAsset";
|
||||
|
||||
const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection");
|
||||
const char connectionsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connections");
|
||||
const char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Connect");
|
||||
const char selectEffectDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select Effect");
|
||||
const char arrangeCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Arrange");
|
||||
@@ -183,6 +185,7 @@ const char editListModelDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen
|
||||
|
||||
const int priorityFirst = 280;
|
||||
const int prioritySelectionCategory = 220;
|
||||
const int priorityConnectionsCategory = 210;
|
||||
const int priorityQmlPreviewCategory = 200;
|
||||
const int priorityStackCategory = 180;
|
||||
const int priorityEditCategory = 160;
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <documentmanager.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <viewmanager.h>
|
||||
#include <actioneditor.h>
|
||||
|
||||
#include <listmodeleditor/listmodeleditordialog.h>
|
||||
#include <listmodeleditor/listmodeleditormodel.h>
|
||||
@@ -433,6 +434,302 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
QString prependSignal(QString signalHandlerName)
|
||||
{
|
||||
if (signalHandlerName.isNull() || signalHandlerName.isEmpty())
|
||||
return {};
|
||||
|
||||
QChar firstChar = signalHandlerName.at(0).toUpper();
|
||||
signalHandlerName[0] = firstChar;
|
||||
signalHandlerName.prepend(QLatin1String("on"));
|
||||
|
||||
return signalHandlerName;
|
||||
}
|
||||
|
||||
QStringList getSignalsList(const ModelNode &node)
|
||||
{
|
||||
if (!node.isValid())
|
||||
return {};
|
||||
|
||||
if (!node.hasMetaInfo())
|
||||
return {};
|
||||
|
||||
QStringList signalsList;
|
||||
NodeMetaInfo nodeMetaInfo = node.metaInfo();
|
||||
|
||||
for (const auto &signalName : nodeMetaInfo.signalNames()) {
|
||||
signalsList << QString::fromUtf8(signalName);
|
||||
}
|
||||
|
||||
//on...Changed are the most regular signals, we assign them the lowest priority,
|
||||
//we don't need them right now
|
||||
// QStringList signalsWithChanged = signalsList.filter("Changed");
|
||||
|
||||
//these are item specific, like MouseArea.clicked, they have higher priority
|
||||
QStringList signalsWithoutChanged = signalsList;
|
||||
signalsWithoutChanged.removeIf([](QString str) {
|
||||
if (str.endsWith("Changed"))
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
QStringList finalResult;
|
||||
finalResult.append(signalsWithoutChanged);
|
||||
|
||||
|
||||
if (finalResult.isEmpty())
|
||||
finalResult = signalsList;
|
||||
|
||||
finalResult.removeDuplicates();
|
||||
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
struct SlotEntry
|
||||
{
|
||||
QString category;
|
||||
QString name;
|
||||
std::function<void(SignalHandlerProperty)> action;
|
||||
};
|
||||
|
||||
QList<SlotEntry> getSlotsLists(const ModelNode &node)
|
||||
{
|
||||
if (!node.isValid())
|
||||
return {};
|
||||
|
||||
if (!node.view()->rootModelNode().isValid())
|
||||
return {};
|
||||
|
||||
QList<SlotEntry> resultList;
|
||||
|
||||
ModelNode rootNode = node.view()->rootModelNode();
|
||||
QmlObjectNode rootObjectNode(rootNode);
|
||||
|
||||
const QString stateCategory = "Change State";
|
||||
|
||||
//For now we are using category as part of the state name
|
||||
//We should change it, once we extend number of categories
|
||||
const SlotEntry defaultState = {stateCategory,
|
||||
(stateCategory + " to " + "Default State"),
|
||||
[rootNode](SignalHandlerProperty signalHandler) {
|
||||
signalHandler.setSource(
|
||||
QString("%1.state = \"\"").arg(rootNode.id()));
|
||||
}};
|
||||
resultList.push_back(defaultState);
|
||||
|
||||
for (const auto &stateName : rootObjectNode.states().names()) {
|
||||
SlotEntry entry = {stateCategory,
|
||||
(stateCategory + " to " + stateName),
|
||||
[rootNode, stateName](SignalHandlerProperty signalHandler) {
|
||||
signalHandler.setSource(
|
||||
QString("%1.state = \"%2\"").arg(rootNode.id(), stateName));
|
||||
}};
|
||||
|
||||
resultList.push_back(entry);
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
//creates connection without signalHandlerProperty
|
||||
ModelNode createNewConnection(ModelNode targetNode)
|
||||
{
|
||||
NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->metaInfo("QtQuick.Connections");
|
||||
ModelNode newConnectionNode = targetNode.view()
|
||||
->createModelNode("QtQuick.Connections",
|
||||
connectionsMetaInfo.majorVersion(),
|
||||
connectionsMetaInfo.minorVersion());
|
||||
if (QmlItemNode::isValidQmlItemNode(targetNode))
|
||||
targetNode.nodeAbstractProperty("data").reparentHere(newConnectionNode);
|
||||
|
||||
newConnectionNode.bindingProperty("target").setExpression(targetNode.id());
|
||||
|
||||
return newConnectionNode;
|
||||
}
|
||||
|
||||
void removeSignal(SignalHandlerProperty signalHandler)
|
||||
{
|
||||
auto connectionNode = signalHandler.parentModelNode();
|
||||
auto connectionSignals = connectionNode.signalProperties();
|
||||
if (connectionSignals.size() > 1) {
|
||||
if (connectionSignals.contains(signalHandler))
|
||||
connectionNode.removeProperty(signalHandler.name());
|
||||
} else {
|
||||
connectionNode.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionsModelNodeActionGroup : public ActionGroup
|
||||
{
|
||||
public:
|
||||
ConnectionsModelNodeActionGroup(const QString &displayName,
|
||||
const QByteArray &menuId,
|
||||
int priority)
|
||||
: ActionGroup(displayName,
|
||||
menuId,
|
||||
priority,
|
||||
&SelectionContextFunctors::always,
|
||||
&SelectionContextFunctors::selectionEnabled)
|
||||
{}
|
||||
|
||||
void updateContext() override
|
||||
{
|
||||
menu()->clear();
|
||||
|
||||
const auto selection = selectionContext();
|
||||
if (!selection.isValid())
|
||||
return;
|
||||
if (!selection.singleNodeIsSelected())
|
||||
return;
|
||||
if (!action()->isEnabled())
|
||||
return;
|
||||
|
||||
ModelNode currentNode = selection.currentSingleSelectedNode();
|
||||
QmlObjectNode currentObjectNode(currentNode);
|
||||
|
||||
QStringList signalsList = getSignalsList(currentNode);
|
||||
QList<SlotEntry> slotsList = getSlotsLists(currentNode);
|
||||
currentNode.validId();
|
||||
|
||||
for (const ModelNode &connectionNode : currentObjectNode.getAllConnections()) {
|
||||
for (const AbstractProperty &property : connectionNode.properties()) {
|
||||
if (property.isSignalHandlerProperty() && property.name() != "target") {
|
||||
const auto signalHandler = property.toSignalHandlerProperty();
|
||||
|
||||
const QString propertyName = QString::fromUtf8(signalHandler.name());
|
||||
|
||||
QMenu *activeSignalHandlerGroup = new QMenu(propertyName, menu());
|
||||
|
||||
QMenu *editSignalGroup = new QMenu("Change Signal", menu());
|
||||
|
||||
for (const auto &signalStr : signalsList) {
|
||||
if (prependSignal(signalStr).toUtf8() == signalHandler.name())
|
||||
continue;
|
||||
|
||||
ActionTemplate *newSignalAction = new ActionTemplate(
|
||||
(signalStr + "Id").toLatin1(),
|
||||
signalStr,
|
||||
[signalStr, signalHandler](const SelectionContext &) {
|
||||
signalHandler.parentModelNode().view()->executeInTransaction(
|
||||
"ConnectionsModelNodeActionGroup::"
|
||||
"changeSignal",
|
||||
[signalStr, signalHandler]() {
|
||||
auto connectionNode = signalHandler.parentModelNode();
|
||||
auto newHandler = connectionNode.signalHandlerProperty(
|
||||
prependSignal(signalStr).toLatin1());
|
||||
newHandler.setSource(signalHandler.source());
|
||||
connectionNode.removeProperty(signalHandler.name());
|
||||
});
|
||||
});
|
||||
editSignalGroup->addAction(newSignalAction);
|
||||
}
|
||||
|
||||
activeSignalHandlerGroup->addMenu(editSignalGroup);
|
||||
|
||||
if (!slotsList.isEmpty()) {
|
||||
QMenu *editSlotGroup = new QMenu("Change Slot", menu());
|
||||
|
||||
for (const auto &slot : slotsList) {
|
||||
ActionTemplate *newSlotAction = new ActionTemplate(
|
||||
(slot.name + "Id").toLatin1(),
|
||||
slot.name,
|
||||
[slot, signalHandler](const SelectionContext &) {
|
||||
signalHandler.parentModelNode()
|
||||
.view()
|
||||
->executeInTransaction("ConnectionsModelNodeActionGroup::"
|
||||
"changeSlot",
|
||||
[slot, signalHandler]() {
|
||||
slot.action(signalHandler);
|
||||
});
|
||||
});
|
||||
editSlotGroup->addAction(newSlotAction);
|
||||
}
|
||||
activeSignalHandlerGroup->addMenu(editSlotGroup);
|
||||
}
|
||||
|
||||
ActionTemplate *openEditorAction = new ActionTemplate(
|
||||
(propertyName + "OpenEditorId").toLatin1(),
|
||||
QString(
|
||||
QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")),
|
||||
[=](const SelectionContext &) {
|
||||
signalHandler.parentModelNode().view()->executeInTransaction(
|
||||
"ConnectionsModelNodeActionGroup::"
|
||||
"openConnectionsEditor",
|
||||
[signalHandler]() { ActionEditor::invokeEditor(signalHandler); });
|
||||
});
|
||||
|
||||
activeSignalHandlerGroup->addAction(openEditorAction);
|
||||
|
||||
ActionTemplate *removeSignalHandlerAction = new ActionTemplate(
|
||||
(propertyName + "RemoveSignalHandlerId").toLatin1(),
|
||||
QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove this handler")),
|
||||
[signalHandler](const SelectionContext &) {
|
||||
signalHandler.parentModelNode().view()->executeInTransaction(
|
||||
"ConnectionsModelNodeActionGroup::"
|
||||
"removeSignalHandler",
|
||||
[signalHandler]() {
|
||||
removeSignal(signalHandler);
|
||||
});
|
||||
});
|
||||
|
||||
activeSignalHandlerGroup->addAction(removeSignalHandlerAction);
|
||||
|
||||
menu()->addMenu(activeSignalHandlerGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//singular add connection:
|
||||
QMenu *addConnection = new QMenu(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
|
||||
"Add signal handler")),
|
||||
menu());
|
||||
|
||||
for (const auto &signalStr : signalsList) {
|
||||
QMenu *newSignal = new QMenu(signalStr, addConnection);
|
||||
|
||||
for (const auto &slot : slotsList) {
|
||||
ActionTemplate *newSlot = new ActionTemplate(
|
||||
QString(signalStr + slot.name + "Id").toLatin1(),
|
||||
slot.name,
|
||||
[=](const SelectionContext &) {
|
||||
currentNode.view()->executeInTransaction(
|
||||
"ConnectionsModelNodeActionGroup::addConnection", [=]() {
|
||||
ModelNode newConnectionNode = createNewConnection(currentNode);
|
||||
slot.action(newConnectionNode.signalHandlerProperty(
|
||||
prependSignal(signalStr).toLatin1()));
|
||||
});
|
||||
});
|
||||
newSignal->addAction(newSlot);
|
||||
}
|
||||
|
||||
ActionTemplate *openEditorAction = new ActionTemplate(
|
||||
(signalStr + "OpenEditorId").toLatin1(),
|
||||
QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Connections Editor")),
|
||||
[=](const SelectionContext &) {
|
||||
currentNode.view()->executeInTransaction(
|
||||
"ConnectionsModelNodeActionGroup::"
|
||||
"openConnectionsEditor",
|
||||
[=]() {
|
||||
ModelNode newConnectionNode = createNewConnection(currentNode);
|
||||
|
||||
SignalHandlerProperty newHandler
|
||||
= newConnectionNode.signalHandlerProperty(
|
||||
prependSignal(signalStr).toLatin1());
|
||||
|
||||
newHandler.setSource(
|
||||
QString("console.log(\"%1.%2\")").arg(currentNode.id(), signalStr));
|
||||
ActionEditor::invokeEditor(newHandler, removeSignal);
|
||||
});
|
||||
});
|
||||
newSignal->addAction(openEditorAction);
|
||||
|
||||
addConnection->addMenu(newSignal);
|
||||
}
|
||||
|
||||
menu()->addMenu(addConnection);
|
||||
}
|
||||
};
|
||||
|
||||
class DocumentError : public std::exception
|
||||
{
|
||||
public:
|
||||
@@ -953,6 +1250,11 @@ void DesignerActionManager::createDefaultDesignerActions()
|
||||
selectionCategory,
|
||||
prioritySelectionCategory));
|
||||
|
||||
addDesignerAction(new ConnectionsModelNodeActionGroup(
|
||||
connectionsCategoryDisplayName,
|
||||
connectionsCategory,
|
||||
priorityConnectionsCategory));
|
||||
|
||||
addDesignerAction(new ActionGroup(
|
||||
arrangeCategoryDisplayName,
|
||||
arrangeCategory,
|
||||
|
@@ -527,9 +527,17 @@ void DebugView::instancesToken(const QString &/*tokenName*/, int /*tokenNumber*/
|
||||
|
||||
}
|
||||
|
||||
void DebugView::currentStateChanged(const ModelNode &/*node*/)
|
||||
void DebugView::currentStateChanged(const ModelNode &node)
|
||||
{
|
||||
if (isDebugViewEnabled()) {
|
||||
QTextStream message;
|
||||
QString string;
|
||||
message.setString(&string);
|
||||
|
||||
message << node;
|
||||
|
||||
log("::currentStateChanged:", string);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugView::nodeOrderChanged([[maybe_unused]] const NodeListProperty &listProperty)
|
||||
|
@@ -205,7 +205,7 @@ QList<QToolButton *> ItemLibraryWidget::createToolBarWidgets()
|
||||
}
|
||||
|
||||
|
||||
void ItemLibraryWidget::handleSearchfilterChanged(const QString &filterText)
|
||||
void ItemLibraryWidget::handleSearchFilterChanged(const QString &filterText)
|
||||
{
|
||||
if (filterText != m_filterText) {
|
||||
m_filterText = filterText;
|
||||
|
@@ -69,7 +69,7 @@ public:
|
||||
Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos);
|
||||
Q_INVOKABLE void removeImport(const QString &importUrl);
|
||||
Q_INVOKABLE void addImportForItem(const QString &importUrl);
|
||||
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
|
||||
Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
|
||||
Q_INVOKABLE void handleAddImport(int index);
|
||||
Q_INVOKABLE void goIntoComponent(const QString &source);
|
||||
|
||||
|
@@ -97,8 +97,9 @@ QString BundleImporter::importComponent(const QString &qmlFile,
|
||||
FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile));
|
||||
const bool qmlFileExists = qmlSourceFile.exists();
|
||||
const QString qmlType = qmlSourceFile.baseName();
|
||||
m_pendingTypes.append(QStringLiteral("%1.%2")
|
||||
.arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), qmlType));
|
||||
m_pendingTypes.append(QStringLiteral("%1.%2.%3")
|
||||
.arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
|
||||
m_bundleId, qmlType));
|
||||
if (!qmldirContent.contains(qmlFile)) {
|
||||
qmldirContent.append(qmlType);
|
||||
qmldirContent.append(" 1.0 ");
|
||||
@@ -159,10 +160,6 @@ QString BundleImporter::importComponent(const QString &qmlFile,
|
||||
// If import is not yet possible, import statement needs to be added asynchronously to
|
||||
// avoid errors, as code model update takes a while.
|
||||
m_importAddPending = true;
|
||||
|
||||
// Full reset is not necessary if new import directory appearing will trigger scanning,
|
||||
// but if directory existed but was not valid possible import, we need to do a reset.
|
||||
m_fullReset = bundleImportPathExists;
|
||||
}
|
||||
}
|
||||
m_importTimerCount = 0;
|
||||
|
@@ -30,9 +30,10 @@ namespace QmlDesigner {
|
||||
BundleMaterial::BundleMaterial(QObject *parent,
|
||||
const QString &name,
|
||||
const QString &qml,
|
||||
const TypeName &type,
|
||||
const QUrl &icon,
|
||||
const QStringList &files)
|
||||
: QObject(parent), m_name(name), m_qml(qml), m_icon(icon), m_files(files) {}
|
||||
: QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) {}
|
||||
|
||||
bool BundleMaterial::filter(const QString &searchText)
|
||||
{
|
||||
@@ -54,6 +55,11 @@ QString BundleMaterial::qml() const
|
||||
return m_qml;
|
||||
}
|
||||
|
||||
TypeName BundleMaterial::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
QStringList BundleMaterial::files() const
|
||||
{
|
||||
return m_files;
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qmldesignercorelib_global.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
@@ -43,6 +45,7 @@ public:
|
||||
BundleMaterial(QObject *parent,
|
||||
const QString &name,
|
||||
const QString &qml,
|
||||
const TypeName &type,
|
||||
const QUrl &icon,
|
||||
const QStringList &files);
|
||||
|
||||
@@ -50,6 +53,7 @@ public:
|
||||
|
||||
QUrl icon() const;
|
||||
QString qml() const;
|
||||
TypeName type() const;
|
||||
QStringList files() const;
|
||||
bool visible() const;
|
||||
|
||||
@@ -59,6 +63,7 @@ signals:
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_qml;
|
||||
TypeName m_type;
|
||||
QUrl m_icon;
|
||||
QStringList m_files;
|
||||
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "bundleimporter.h"
|
||||
#include "bundlematerial.h"
|
||||
#include "bundlematerialcategory.h"
|
||||
#include "qmldesignerconstants.h"
|
||||
#include "utils/qtcassert.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
@@ -121,6 +122,8 @@ void MaterialBrowserBundleModel::loadMaterialBundle()
|
||||
|
||||
m_matBundleExists = true;
|
||||
|
||||
const QString bundleId = m_matBundleObj.value("id").toString();
|
||||
|
||||
const QJsonObject catsObj = m_matBundleObj.value("categories").toObject();
|
||||
const QStringList categories = catsObj.keys();
|
||||
for (const QString &cat : categories) {
|
||||
@@ -136,8 +139,14 @@ void MaterialBrowserBundleModel::loadMaterialBundle()
|
||||
for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
|
||||
files.append(asset.toString());
|
||||
|
||||
auto bundleMat = new BundleMaterial(category, mat, matObj.value("qml").toString(),
|
||||
QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())), files);
|
||||
QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString()));
|
||||
QString qml = matObj.value("qml").toString();
|
||||
TypeName type = QLatin1String("%1.%2.%3").arg(
|
||||
QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
|
||||
bundleId,
|
||||
qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
|
||||
|
||||
auto bundleMat = new BundleMaterial(category, mat, qml, type, icon, files);
|
||||
|
||||
category->addBundleMaterial(bundleMat);
|
||||
}
|
||||
@@ -149,7 +158,7 @@ void MaterialBrowserBundleModel::loadMaterialBundle()
|
||||
for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
|
||||
sharedFiles.append(file.toString());
|
||||
|
||||
m_importer = new Internal::BundleImporter(matBundleDir.path(), "MaterialBundle", sharedFiles);
|
||||
m_importer = new Internal::BundleImporter(matBundleDir.path(), bundleId, sharedFiles);
|
||||
connect(m_importer, &Internal::BundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
|
||||
if (metaInfo.isValid())
|
||||
emit addBundleMaterialToProjectRequested(metaInfo);
|
||||
@@ -223,7 +232,10 @@ void MaterialBrowserBundleModel::applyToSelected(BundleMaterial *mat, bool add)
|
||||
|
||||
void MaterialBrowserBundleModel::addMaterial(BundleMaterial *mat)
|
||||
{
|
||||
m_importer->importComponent(mat->qml(), mat->files());
|
||||
QString err = m_importer->importComponent(mat->qml(), mat->files());
|
||||
|
||||
if (!err.isEmpty())
|
||||
qWarning() << __FUNCTION__ << err;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -266,8 +266,7 @@ void MaterialBrowserModel::removeMaterial(const ModelNode &material)
|
||||
|
||||
void MaterialBrowserModel::deleteSelectedMaterial()
|
||||
{
|
||||
if (isValidIndex(m_selectedIndex))
|
||||
m_materialList[m_selectedIndex].destroy();
|
||||
deleteMaterial(m_selectedIndex);
|
||||
}
|
||||
|
||||
void MaterialBrowserModel::updateSelectedMaterial()
|
||||
@@ -365,7 +364,11 @@ void MaterialBrowserModel::pasteMaterialProperties(int idx)
|
||||
|
||||
void MaterialBrowserModel::deleteMaterial(int idx)
|
||||
{
|
||||
m_materialList[idx].destroy();
|
||||
if (isValidIndex(idx)) {
|
||||
ModelNode node = m_materialList[idx];
|
||||
if (node.isValid())
|
||||
QmlObjectNode(node).destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialBrowserModel::renameMaterial(int idx, const QString &newName)
|
||||
|
@@ -93,6 +93,8 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
||||
mat.setVariantProperty(prop.name(), prop.toVariantProperty().value());
|
||||
else if (prop.isBindingProperty())
|
||||
mat.setBindingProperty(prop.name(), prop.toBindingProperty().expression());
|
||||
else if (!all)
|
||||
mat.removeProperty(prop.name());
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -105,70 +107,23 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
||||
MaterialBrowserBundleModel *matBrowserBundleModel = m_widget->materialBrowserBundleModel().data();
|
||||
|
||||
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::applyToSelectedTriggered, this,
|
||||
[&] (BundleMaterial *material, bool add) {
|
||||
[&] (BundleMaterial *bundleMat, bool add) {
|
||||
if (!m_selectedModel.isValid())
|
||||
return;
|
||||
|
||||
m_bundleMaterialDropTarget = m_selectedModel;
|
||||
m_bundleMaterialAddToSelected = add;
|
||||
m_widget->materialBrowserBundleModel()->addMaterial(material);
|
||||
|
||||
ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
|
||||
if (defaultMat.isValid())
|
||||
applyBundleMaterialToDropTarget(defaultMat);
|
||||
else
|
||||
m_widget->materialBrowserBundleModel()->addMaterial(bundleMat);
|
||||
});
|
||||
|
||||
connect(matBrowserBundleModel, &MaterialBrowserBundleModel::addBundleMaterialToProjectRequested, this,
|
||||
[&] (const QmlDesigner::NodeMetaInfo &metaInfo) {
|
||||
ModelNode matLib = materialLibraryNode();
|
||||
if (!matLib.isValid())
|
||||
return;
|
||||
|
||||
executeInTransaction("MaterialBrowserView::widgetInfo", [&] {
|
||||
ModelNode newMatNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(),
|
||||
metaInfo.minorVersion());
|
||||
matLib.defaultNodeListProperty().reparentHere(newMatNode);
|
||||
|
||||
static QRegularExpression rgx("([A-Z])([a-z]*)");
|
||||
QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed();
|
||||
QString newId = model()->generateIdFromName(newName, "material");
|
||||
newMatNode.setIdWithRefactoring(newId);
|
||||
|
||||
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
|
||||
objNameProp.setValue(newName);
|
||||
|
||||
if (m_bundleMaterialDropTarget.isValid()) {
|
||||
QmlObjectNode qmlObjNode(m_bundleMaterialDropTarget);
|
||||
if (m_bundleMaterialAddToSelected) {
|
||||
// TODO: unify this logic as it exist elsewhere also
|
||||
auto expToList = [](const QString &exp) {
|
||||
QString copy = exp;
|
||||
copy = copy.remove("[").remove("]");
|
||||
|
||||
QStringList tmp = copy.split(',', Qt::SkipEmptyParts);
|
||||
for (QString &str : tmp)
|
||||
str = str.trimmed();
|
||||
|
||||
return tmp;
|
||||
};
|
||||
|
||||
auto listToExp = [](QStringList &stringList) {
|
||||
if (stringList.size() > 1)
|
||||
return QString("[" + stringList.join(",") + "]");
|
||||
|
||||
if (stringList.size() == 1)
|
||||
return stringList.first();
|
||||
|
||||
return QString();
|
||||
};
|
||||
QStringList matList = expToList(qmlObjNode.expression("materials"));
|
||||
matList.append(newMatNode.id());
|
||||
QString updatedExp = listToExp(matList);
|
||||
qmlObjNode.setBindingProperty("materials", updatedExp);
|
||||
} else {
|
||||
qmlObjNode.setBindingProperty("materials", newMatNode.id());
|
||||
}
|
||||
m_bundleMaterialDropTarget = {};
|
||||
}
|
||||
|
||||
m_bundleMaterialAddToSelected = false;
|
||||
});
|
||||
applyBundleMaterialToDropTarget({}, metaInfo);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,6 +134,74 @@ WidgetInfo MaterialBrowserView::widgetInfo()
|
||||
tr("Material Browser"));
|
||||
}
|
||||
|
||||
void MaterialBrowserView::applyBundleMaterialToDropTarget(const ModelNode &bundleMat,
|
||||
const NodeMetaInfo &metaInfo)
|
||||
{
|
||||
if (!bundleMat.isValid() && !metaInfo.isValid())
|
||||
return;
|
||||
|
||||
ModelNode matLib = materialLibraryNode();
|
||||
if (!matLib.isValid())
|
||||
return;
|
||||
|
||||
executeInTransaction("MaterialBrowserView::applyBundleMaterialToDropTarget", [&] {
|
||||
ModelNode newMatNode;
|
||||
if (metaInfo.isValid()) {
|
||||
newMatNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(),
|
||||
metaInfo.minorVersion());
|
||||
matLib.defaultNodeListProperty().reparentHere(newMatNode);
|
||||
|
||||
static QRegularExpression rgx("([A-Z])([a-z]*)");
|
||||
QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed();
|
||||
if (newName.endsWith(" Material"))
|
||||
newName.chop(9); // remove trailing " Material"
|
||||
QString newId = model()->generateIdFromName(newName, "material");
|
||||
newMatNode.setIdWithRefactoring(newId);
|
||||
|
||||
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
|
||||
objNameProp.setValue(newName);
|
||||
} else {
|
||||
newMatNode = bundleMat;
|
||||
}
|
||||
|
||||
if (m_bundleMaterialDropTarget.isValid()) {
|
||||
QmlObjectNode qmlObjNode(m_bundleMaterialDropTarget);
|
||||
if (m_bundleMaterialAddToSelected) {
|
||||
// TODO: unify this logic as it exist elsewhere also
|
||||
auto expToList = [](const QString &exp) {
|
||||
QString copy = exp;
|
||||
copy = copy.remove("[").remove("]");
|
||||
|
||||
QStringList tmp = copy.split(',', Qt::SkipEmptyParts);
|
||||
for (QString &str : tmp)
|
||||
str = str.trimmed();
|
||||
|
||||
return tmp;
|
||||
};
|
||||
|
||||
auto listToExp = [](QStringList &stringList) {
|
||||
if (stringList.size() > 1)
|
||||
return QString("[" + stringList.join(",") + "]");
|
||||
|
||||
if (stringList.size() == 1)
|
||||
return stringList.first();
|
||||
|
||||
return QString();
|
||||
};
|
||||
QStringList matList = expToList(qmlObjNode.expression("materials"));
|
||||
matList.append(newMatNode.id());
|
||||
QString updatedExp = listToExp(matList);
|
||||
qmlObjNode.setBindingProperty("materials", updatedExp);
|
||||
} else {
|
||||
qmlObjNode.setBindingProperty("materials", newMatNode.id());
|
||||
}
|
||||
|
||||
m_bundleMaterialDropTarget = {};
|
||||
m_bundleMaterialAddToSelected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void MaterialBrowserView::modelAttached(Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
@@ -347,6 +370,28 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
|
||||
m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath);
|
||||
}
|
||||
|
||||
ModelNode MaterialBrowserView::getBundleMaterialDefaultInstance(const TypeName &type)
|
||||
{
|
||||
const QList<ModelNode> materials = m_widget->materialBrowserModel()->materials();
|
||||
for (const ModelNode &mat : materials) {
|
||||
if (mat.type() == type) {
|
||||
bool isDefault = true;
|
||||
const QList<AbstractProperty> props = mat.properties();
|
||||
for (const AbstractProperty &prop : props) {
|
||||
if (prop.name() != "objectName") {
|
||||
isDefault = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefault)
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void MaterialBrowserView::importsChanged([[maybe_unused]] const QList<Import> &addedImports,
|
||||
[[maybe_unused]] const QList<Import> &removedImports)
|
||||
{
|
||||
@@ -383,7 +428,14 @@ void MaterialBrowserView::customNotification(const AbstractView *view,
|
||||
m_widget->materialBrowserModel()->deleteSelectedMaterial();
|
||||
} else if (identifier == "drop_bundle_material") {
|
||||
m_bundleMaterialDropTarget = nodeList.first();
|
||||
m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial);
|
||||
|
||||
|
||||
ModelNode defaultMat = getBundleMaterialDefaultInstance(m_draggedBundleMaterial->type());
|
||||
if (defaultMat.isValid())
|
||||
applyBundleMaterialToDropTarget(defaultMat);
|
||||
else
|
||||
m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial);
|
||||
|
||||
m_draggedBundleMaterial = nullptr;
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <abstractview.h>
|
||||
#include "abstractview.h"
|
||||
#include "nodemetainfo.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
@@ -45,6 +46,8 @@ private:
|
||||
void refreshModel(bool updateImages);
|
||||
bool isMaterial(const ModelNode &node) const;
|
||||
void loadPropertyGroups();
|
||||
void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo = {});
|
||||
ModelNode getBundleMaterialDefaultInstance(const TypeName &type);
|
||||
|
||||
QPointer<MaterialBrowserWidget> m_widget;
|
||||
ModelNode m_bundleMaterialDropTarget;
|
||||
|
@@ -192,7 +192,7 @@ void MaterialBrowserWidget::contextHelp(const Core::IContext::HelpCallback &call
|
||||
callback({});
|
||||
}
|
||||
|
||||
void MaterialBrowserWidget::handleSearchfilterChanged(const QString &filterText)
|
||||
void MaterialBrowserWidget::handleSearchFilterChanged(const QString &filterText)
|
||||
{
|
||||
if (filterText != m_filterText) {
|
||||
m_filterText = filterText;
|
||||
|
@@ -51,7 +51,7 @@ public:
|
||||
QPointer<MaterialBrowserBundleModel> materialBrowserBundleModel() const;
|
||||
void updateMaterialPreview(const ModelNode &node, const QPixmap &pixmap);
|
||||
|
||||
Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText);
|
||||
Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
|
||||
Q_INVOKABLE void startDragMaterial(int index, const QPointF &mousePos);
|
||||
Q_INVOKABLE void startDragBundleMaterial(QmlDesigner::BundleMaterial *bundleMat, const QPointF &mousePos);
|
||||
|
||||
|
@@ -720,7 +720,7 @@ void StatesEditorView::propertiesRemoved(const QList<AbstractProperty>& property
|
||||
for (const AbstractProperty &property : propertyList) {
|
||||
if (property.name() == "states" && property.parentModelNode() == activeStateGroup().modelNode())
|
||||
resetModel();
|
||||
if (property.name() == "when"
|
||||
if ((property.name() == "when" || property.name() == "name")
|
||||
&& QmlModelState::isValidQmlModelState(property.parentModelNode()))
|
||||
resetModel();
|
||||
if (property.name() == "extend")
|
||||
@@ -848,7 +848,8 @@ void StatesEditorView::variantPropertiesChanged(const QList<VariantProperty> &pr
|
||||
auto guard = qScopeGuard([&]() { m_block = false; });
|
||||
|
||||
for (const VariantProperty &property : propertyList) {
|
||||
if (property.name() == "name" && QmlModelState::isValidQmlModelState(property.parentModelNode()))
|
||||
if (property.name() == "name"
|
||||
&& QmlModelState::isValidQmlModelState(property.parentModelNode()))
|
||||
resetModel();
|
||||
else if (property.name() == "state"
|
||||
&& property.parentModelNode() == activeStateGroup().modelNode())
|
||||
|
@@ -35,10 +35,12 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name,
|
||||
if (file.open()) {
|
||||
QString qtQuickVersion;
|
||||
QString qtQuick3DVersion;
|
||||
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target()->kit());
|
||||
if (qtVersion && qtVersion->qtVersion() < QVersionNumber(6, 0, 0)) {
|
||||
qtQuickVersion = "2.15";
|
||||
qtQuick3DVersion = "1.15";
|
||||
if (target()) {
|
||||
QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(target()->kit());
|
||||
if (qtVersion && qtVersion->qtVersion() < QVersionNumber(6, 0, 0)) {
|
||||
qtQuickVersion = "2.15";
|
||||
qtQuick3DVersion = "1.15";
|
||||
}
|
||||
}
|
||||
|
||||
QString content{
|
||||
|
@@ -3,6 +3,8 @@
|
||||
#ifndef STYLESHEETMERGER_H
|
||||
#define STYLESHEETMERGER_H
|
||||
|
||||
#include "qmldesignercorelib_global.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
#include <modelnode.h>
|
||||
@@ -19,8 +21,8 @@ struct ReparentInfo {
|
||||
bool alreadyReparented;
|
||||
};
|
||||
|
||||
|
||||
class StylesheetMerger {
|
||||
class QMLDESIGNERCORE_EXPORT StylesheetMerger
|
||||
{
|
||||
public:
|
||||
StylesheetMerger(AbstractView*, AbstractView*);
|
||||
void merge();
|
||||
|
@@ -5,13 +5,13 @@
|
||||
|
||||
#include "texttomodelmerger.h"
|
||||
#include "modeltotextmerger.h"
|
||||
#include "model_p.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <customnotifications.h>
|
||||
#include <filemanager/astobjecttextextractor.h>
|
||||
#include <filemanager/firstdefinitionfinder.h>
|
||||
#include <filemanager/objectlengthcalculator.h>
|
||||
#include <model_p.h>
|
||||
#include <modelnode.h>
|
||||
#include <modelnodepositionstorage.h>
|
||||
#include <nodeproperty.h>
|
||||
|
BIN
src/plugins/qmldesigner/qtquickplugin/images/timer-16px.png
Normal file
BIN
src/plugins/qmldesigner/qtquickplugin/images/timer-16px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 339 B |
BIN
src/plugins/qmldesigner/qtquickplugin/images/timer-24px.png
Normal file
BIN
src/plugins/qmldesigner/qtquickplugin/images/timer-24px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 712 B |
BIN
src/plugins/qmldesigner/qtquickplugin/images/timer-24px@2x.png
Normal file
BIN
src/plugins/qmldesigner/qtquickplugin/images/timer-24px@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@@ -98,5 +98,8 @@
|
||||
<file>images/timeline-16px.png</file>
|
||||
<file>images/keyframe-16px.png</file>
|
||||
<file>images/timeline-animation-16px.png</file>
|
||||
<file>images/timer-16px.png</file>
|
||||
<file>images/timer-24px.png</file>
|
||||
<file>images/timer-24px@2x.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@@ -487,6 +487,25 @@ MetaInfo {
|
||||
}
|
||||
}
|
||||
|
||||
Type {
|
||||
name: "QtQml.Timer"
|
||||
icon: ":/qtquickplugin/images/timer-16px.png"
|
||||
|
||||
Hints {
|
||||
visibleInNavigator: true
|
||||
canBeDroppedInNavigator: true
|
||||
canBeDroppedInFormEditor: false
|
||||
canBeContainer: false
|
||||
}
|
||||
|
||||
ItemLibraryEntry {
|
||||
name: "Timer"
|
||||
category: "d.Qt Quick - Animation"
|
||||
libraryIcon: ":/qtquickplugin/images/timer-24px.png"
|
||||
version: "2.0"
|
||||
}
|
||||
}
|
||||
|
||||
Type {
|
||||
name: "QtQml.Component"
|
||||
icon: ":/qtquickplugin/images/component-icon16.png"
|
||||
|
@@ -21,7 +21,14 @@ FileStoreIo::FileStoreIo(const QString &fileName)
|
||||
|
||||
QByteArray FileStoreIo::read() const
|
||||
{
|
||||
m_file->open(QFile::ReadOnly | QFile::Text);
|
||||
if (!m_file->exists())
|
||||
return {};
|
||||
|
||||
if (!m_file->open(QFile::ReadOnly | QFile::Text)) {
|
||||
qWarning() << "Cannot load User Preset(s)";
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray data = m_file->readAll();
|
||||
m_file->close();
|
||||
|
||||
@@ -30,7 +37,11 @@ QByteArray FileStoreIo::read() const
|
||||
|
||||
void FileStoreIo::write(const QByteArray &data)
|
||||
{
|
||||
m_file->open(QFile::WriteOnly | QFile::Text);
|
||||
if (!m_file->open(QFile::WriteOnly | QFile::Text)) {
|
||||
qWarning() << "Cannot save User Preset(s)";
|
||||
return;
|
||||
}
|
||||
|
||||
m_file->write(data);
|
||||
m_file->close();
|
||||
}
|
||||
|
Reference in New Issue
Block a user