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:
Eike Ziller
2022-09-27 10:03:37 +02:00
61 changed files with 932 additions and 201 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -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}.

View File

@@ -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.

View File

@@ -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

View File

@@ -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}.

View File

@@ -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());

View File

@@ -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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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

View File

@@ -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
}

View File

@@ -25,6 +25,7 @@ public:
protected:
explicit Quick3DNodeInstance(QObject *node);
void invokeDummyViewCreate() const override;
private:
QQuick3DNode *quick3DNode() const;

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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();
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();

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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,
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)
}
}
}

View File

@@ -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
}
}

View File

@@ -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,
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

View File

@@ -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 {}
}
}
}
}

View File

@@ -135,6 +135,7 @@ Section {
BoolButtonRowButton {
visible: section.showEasingCurve
buttonIcon: StudioTheme.Constants.curveDesigner
checkable: false
EasingCurveEditor {
id: easingCurveEditor

View File

@@ -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: {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
onPressed: function(event) {
if (event.button === Qt.RightButton)
contextMenu.popup(root)
mouse.accepted = false
}
}
onPersistentSelectionChanged: {
if (!persistentSelection)
root.deselect()
}
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

View File

@@ -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

View File

@@ -84,8 +84,8 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
DSscrollBarTrack=ff404040
DSscrollBarHandle=ff505050
DSscrollBarTrack=ff3E3E3E
DSscrollBarHandle=ff4C4C4C
DSsectionHeadBackground=ff1f1f1f

View File

@@ -86,8 +86,8 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
DSscrollBarTrack=ff404040
DSscrollBarHandle=ff505050
DSscrollBarTrack=ff3E3E3E
DSscrollBarHandle=ff4C4C4C
DSsectionHeadBackground=ff1f1f1f

View File

@@ -88,8 +88,8 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
DSscrollBarTrack=ff404040
DSscrollBarHandle=ff505050
DSscrollBarTrack=ff3E3E3E
DSscrollBarHandle=ff4C4C4C
DSsectionHeadBackground=ff1f1f1f

View File

@@ -82,8 +82,8 @@ DSsliderHandleHover=ff606060
DSsliderHandleFocus=ff0492c9
DSsliderHandleInteraction=ff2aafd3
DSscrollBarTrack=ff404040
DSscrollBarHandle=ff505050
DSscrollBarTrack=ff3E3E3E
DSscrollBarHandle=ff4C4C4C
DSsectionHeadBackground=ff1f1f1f

View File

@@ -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;

View File

@@ -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 = {});

View File

@@ -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

View File

@@ -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();

View File

@@ -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;

View File

@@ -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,

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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)

View File

@@ -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,33 +107,62 @@ 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) {
applyBundleMaterialToDropTarget({}, metaInfo);
});
}
return createWidgetInfo(m_widget.data(),
"MaterialBrowser",
WidgetInfo::LeftPane,
0,
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::widgetInfo", [&] {
ModelNode newMatNode = createModelNode(metaInfo.typeName(), metaInfo.majorVersion(),
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);
@@ -164,19 +195,11 @@ WidgetInfo MaterialBrowserView::widgetInfo()
} else {
qmlObjNode.setBindingProperty("materials", newMatNode.id());
}
m_bundleMaterialDropTarget = {};
}
m_bundleMaterialAddToSelected = false;
});
});
}
return createWidgetInfo(m_widget.data(),
"MaterialBrowser",
WidgetInfo::LeftPane,
0,
tr("Material Browser"));
});
}
void MaterialBrowserView::modelAttached(Model *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();
ModelNode defaultMat = getBundleMaterialDefaultInstance(m_draggedBundleMaterial->type());
if (defaultMat.isValid())
applyBundleMaterialToDropTarget(defaultMat);
else
m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial);
m_draggedBundleMaterial = nullptr;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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())

View File

@@ -35,11 +35,13 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name,
if (file.open()) {
QString qtQuickVersion;
QString qtQuick3DVersion;
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{
R"(import QtQuick %1

View File

@@ -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();

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -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>

View File

@@ -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"

View File

@@ -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();
}