Merge remote-tracking branch 'origin/qds/dev'

Change-Id: I8aa6ace80911b440485f9006f066aa2113cca63d
This commit is contained in:
Tim Jenssen
2023-12-19 16:52:50 +01:00
54 changed files with 574 additions and 382 deletions

View File

@@ -3,7 +3,7 @@
/*!
\page creator-editor-refactoring.html
\previouspage creator-editor-locator.html
\previouspage creator-jump-to-the-code.html
\nextpage creator-editor-quick-fixes.html
\title Refactoring

View File

@@ -37,6 +37,14 @@
through projects, files, classes, functions, documentation and
file systems.
\li \l{Jump to the Code}
Jump to the code for a specific component directly from
the \uicontrol {2D} view or \uicontrol {Navigator} view.
You can also jump to the code of a particular
\uicontrol {State} or \uicontrol {Connection} from their
corresponding views.
\endlist
*/

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\previouspage creator-editor-locator.html
\page creator-jump-to-the-code.html
\nextpage creator-editor-refactoring.html
\title Jump to the Code
Jump to the code is a feature that gives you instant access to a particular
part of the code. It takes you to the \uicontrol {Code} view location of a particular
component, \uicontrol {State}, or \uicontrol {Connection}.
You can jump to the code from:
\list
\li \uicontrol {Navigator} view
\li \uicontrol {2D} view
\li \uicontrol {States} view
\li \uicontrol {Connections} view
\endlist
\section1 Jump to the Code from the Navigator View
\list 1
\li Right-click on a component in the \uicontrol {Navigator} view.
\li Select \uicontrol {Jump to the Code}.
\endlist
\image jump-to-the-code-from-navigator-view.webp
\section1 Jump to the Code from the 2D View
\list 1
\li Right-click on a component in the \uicontrol {2D} view.
\li Select \uicontrol {Jump to the Code}.
\endlist
\image jump-to-the-code-from-2D-view.webp
\note Alternatively, you can select the component in the \uicontrol {Navigator} view
or in the \uicontrol {2D} view and press \key {F4}. That takes you to the code
location in the \uicontrol {Code} view.
\section1 Jump to the Code from the States View
\list 1
\li Locate the state you want to check in the \uicontrol {States} view.
\li Select \inlineimage icons/browse-button.png
to open additional options.
\li Select \uicontrol {Jump to the Code}.
\endlist
\image jump-to-the-code-from-state-view.webp
\section1 Jump to the Code from the Connections View
\list 1
\li Select a connection in the \uicontrol {Connections} view.
\li Select \inlineimage icons/jump-to-code-16px.png
to jump to the code segment related to the connection.
\endlist
\image jump-to-the-code-from-connections-view.webp
*/

View File

@@ -4,7 +4,7 @@
/*!
\previouspage creator-editor-finding.html
\page creator-editor-locator.html
\nextpage creator-editor-refactoring.html
\nextpage creator-jump-to-the-code.html
\title Searching with the Locator

View File

@@ -31,7 +31,7 @@
\li \e fire-color-table.png
\endlist
You can download the assets from
Download the assets
\l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/fire-particles/FireParticles/content/images}
{here}.
@@ -48,14 +48,14 @@
\li In the \uicontrol Presets section, select \uicontrol General >
\uicontrol {3D}.
\li In the \uicontrol Details section, set the name to \e FireParticles
and select the folder where you want to save the project.
, and select the folder where you want to save the project.
\li Select \uicontrol {Create}.
\endlist
Next, remove unwanted default components from the project:
Next, remove any unwanted default components from the project:
\list 1
\li In \uicontrol {Navigator}, select \e Text and select the \key Delete
\li In the \uicontrol {Navigator} view, select \e Text and then select the \key Delete
key.
\li In the same way, delete \e {cubeModel}.
\endlist
@@ -63,11 +63,10 @@
For a better visual effect, set the background color to black:
\list
\li In \uicontrol {Navigator}, select \e Rectangle and in
\li In the \uicontrol {Navigator} view, select \e Rectangle and in
\uicontrol {Properties}, set \uicontrol {Fill Color} to #000000.
\endlist
\section2 Adding a Particle System to Your Scene
To add a particle system, you first need to import the QtQuick3D.Particles3D
@@ -79,16 +78,19 @@
\li Find QtQuick3D.Particles3D, and select it to add it to your project.
\endlist
\image fire-particles-components.png
In this project, you use an animated sprite to simulate a fire. For
this, use the Animated Sprite particle system template:
this, use the \uicontrol {Animated Sprite} particle system template:
\list
\li From \uicontrol Components >
\uicontrol{Qt Quick 3D Particle System Templates} drag an
\uicontrol {Animated Sprite} component to the \uicontrol{3D} view. You can
also drag it to \e scene in \uicontrol {Navigator}.
\uicontrol {Animated Sprite} component [1] to \e scene in \uicontrol {Navigator}.
\endlist
\image fire-particles-animated-sprite-comp.png
\image fire-particles-navigator.png
You now have the particle system in place. To preview it, select
@@ -97,26 +99,33 @@
\section2 Adding Sprites and Sprite Animations
First, import the sprite to use for the fire:
In this project, you use a spritesheet with 3 sprites.
A spritesheet is a single image file that contains multiple sprites arranged
in a grid. You use these sprites to create a looping animation to add variation
to the movement of the flames.
First, import the spritesheet you downloaded in the beginning of this tutorial
(\e{fire-sprites.png}) to use for the fire:
\list 1
\li In \uicontrol {Assets}, select \inlineimage icons/plus.png
\li In the \uicontrol {Assets} view, select \inlineimage icons/plus.png
.
\li Select \e {fire-sprites.png}.
\li In the \uicontrol {Add Assets} dialog, find and select \e {fire-sprites.png}.
\endlist
\image fire-particles-assets.png
Next, add the sprite to the particle system and create the animation:
Next, add the spritesheet to the particle system, and create the animation:
\list 1
\li From \uicontrol{Assets}, drag \e {fire-sprites.png} to
\e animatedTexture in \uicontrol {Navigator}.
\li In \uicontrol{Navigator}, select \e{animatedSequence} and in
\uicontrol {Properties}, set:
\li From the \uicontrol{Assets} view, drag \e {fire-sprites.png} to
\e animatedTexture in the \uicontrol {Navigator} view.
\image fire-particles-drag-sprites.png
\li In the \uicontrol{Navigator} view, select \e{animatedSequence} and in
the \uicontrol {Properties} view, set:
\list
\li \uicontrol{Frame Count} to 3.
There are 3 sprites in \e {fire-sprites.png} so you want to
There are three sprites in \e {fire-sprites.png} so you want to
divide the image into 3 different sprites for this animation.
\li \uicontrol Interpolate to true. This makes the animation between
the sprites smooth.
@@ -132,21 +141,25 @@
change the color of a single sprite during its life span. In this
project, you use a gradient color table ranging from yellow to dark orange.
This results in the sprites being yellow when they are emitted and dark
orange at the end of the life span.
orange at the end of their life span.
To set the color table:
\list 1
\li Import \e{fire-color-table.png} to your project.
\li From \uicontrol{Components}, drag a \uicontrol Texture to
\li Go to the \uicontrol {Asstes} view and import \e{fire-color-table.png} to your project.
\li From the \uicontrol{Components} view, drag a \uicontrol Texture [1] to
\e animatedSpriteParticle in \uicontrol{Navigator}.
\image fire-particles-drag-texture.png
\li Rename the texture to \e {fireColorTable}.
\li In \uicontrol {Navigator}, select \e {fireColorTable} and in
\uicontrol {Properties}, set \uicontrol Source to
\li In the \uicontrol {Navigator} view, select \e {fireColorTable} and in
the \uicontrol {Properties} view, set \uicontrol Source to
\e{fire-color-table.png}.
\li In \uicontrol {Navigator}, select \e {animatedSpriteParticle} and:
\li In the \uicontrol {Navigator} view, select \e {animatedSpriteParticle} and in the
\uicontrol Properties view:
\list
\li Ensure that \uicontrol Sprite is set to \e {animatedTexture}.
\li Set \uicontrol {Particle Scale} to 5.
\li Ensure that \uicontrol Sprite is set to \e {animatedTexture}. This sets which image
to emit from the particle emitter, which in this case is the spritesheet added earlier.
\li Set \uicontrol {Particle Scale} to 5 to adjust the size of the flames.
\li Set \uicontrol {Color Table} to \e{fireColorTable}.
\endlist
\endlist
@@ -158,48 +171,71 @@
The next step is to adjust the particle emitter properties:
\list 1
\li In \uicontrol{Navigator}, select \e animatedSpriteEmitter and in
\uicontrol {Properties}, set:
\li First, in the \uicontrol{Navigator} view, select \e animatedSpriteEmitter and then, in
the \uicontrol {Properties} view, set:
\list
\li \uicontrol {Emit Rate} to 300.
\li \uicontrol {Life Span} to 2500.
\li \uicontrol {Life Span Variation} to 100.
\li \uicontrol {Particle End Scale} to 1,50.
\li \uicontrol {Emit Rate} to 300 to emit 300 particles per second.
\li \uicontrol {Life Span} to 2500 to set the life span of each particle to
2500 milliseconds (2.5 seconds).
\li \uicontrol {Life Span Variation} to 100 to set variation to the particle
life spans. The life span of each particle is now between 2.4 and 2.6 seconds to make
the fire look more realistic.
\li \uicontrol {Particle End Scale} to 1.50. When a particle is emitted, its
scale is 1.00 by default. By the end of its life span, it will grow to a scale of 1.50.
\endlist
This sets the emitter to emit 300 particles per second. The life span of
each particle is ranging from 2.4 to 2.6 seconds. The size of each
particle is increasing slightly throughout its life span.
\li In \uicontrol{Navigator}, select \e animatedSpriteParticle and in
\uicontrol {Properties}, set:
\image fire-particles-particle-emitter.png
\li First, in the \uicontrol{Navigator} view, select \e animatedSpriteParticle and then, in
the \uicontrol {Properties} view, set:
\list
\li \uicontrol {Blend Mode} to Screen.
\li \uicontrol {Max Amount} to 1000.
\li \uicontrol {Color Variation} > \uicontrol W to 0,50. This adds
\li \uicontrol {Blend Mode} to Screen to blend the fire nicely with the background.
\li \uicontrol {Max Amount} to 1000 to define the maximum amount of particles visible at the
same time. Setting a higher number allocates more memory.
\li \uicontrol {Color Variation} > \uicontrol W to 0.50. This adds
randomness to the opacity of the sprites.
\endlist
\li In \uicontrol{Navigator}, select \e animatedSpriteDirection and in
\uicontrol {Properties}, set:
\image fire-particle-sprite-particle.png
\li The \e animatedSpriteDirection component defines the direction and the velocity of
the particles emitted from the particle emitter. In this tutorial, you want the particles to
go straight up with a small variation.
In the \uicontrol{Navigator} view, select \e animatedSpriteDirection and in the
\uicontrol {Properties} view, set:
\list
\li \uicontrol Direction > \uicontrol Y to 20.
\li \uicontrol {Direction Variation} > \uicontrol X to 3.
\li \uicontrol {Direction Variation} > \uicontrol Y to 10.
\endlist
This makes the particles go straight up with a small amount of randomness
to the direction.
\li \uicontrol Direction > \uicontrol X to 0 and \uicontrol Direction > \uicontrol Y
to 20. This makes the particles go straight up (along the Y axis) at the velocity of 20.
\li \uicontrol {Direction Variation} > \uicontrol X to 3. This adds small variation along
the X axis, making the flames a little bit wider.
\li \uicontrol {Direction Variation} > \uicontrol Y to 10. This adds variation to the
velocity of the particles along the Y axis. Because this makes some particles slower,
the flames appears thicker in the lower part.
\endlist
\section1 Previewing
\image fire-particles-direction.png
Now, the fire effect is done. Before you preview it, adjust the camera:
\endlist
\section1 Running the Project
Now, the fire effect is ready. Before you run it, position the camera to show the effect
from a suitable perspective:
\list 1
\li In \uicontrol {Navigator}, select \e sceneCamera and in
\uicontrol {Properties}, set:
\li In the \uicontrol {Navigator} view, select \e sceneCamera and in the
\uicontrol {Properties} view, set:
\list
\li \uicontrol {Field of View} to 20.
\li \uicontrol {Translation} > \uicontrol Y to 35.
\li \uicontrol {Translation} > \uicontrol Z to 350.
\endlist
\endlist
Now, preview the fire effect by selecting \key Alt + \key{P}.
\image fire-particles-camera.png
Now, run the project by selecting \key Ctrl + \key{R} or the
\inlineimage icons/run_project.png
button in the top toolbar.
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -214,6 +214,7 @@
\list
\li \l{Finding and Replacing}
\li \l{Searching with the Locator}
\li \l{Jump to the Code}
\endlist
\li \l{Refactoring}
\li \l{Applying Refactoring Actions}

View File

@@ -120,7 +120,7 @@ StudioControls.Dialog {
actionIndicator.visible: false
translationIndicator.visible: false
validator: RegularExpressionValidator {
regularExpression: /^\w+$/
regularExpression: /^[\w ]+$/
}
Keys.onEnterPressed: btnImport.onClicked()

View File

@@ -87,7 +87,7 @@ StudioControls.Dialog {
actionIndicator.visible: false
translationIndicator.visible: false
validator: RegularExpressionValidator {
regularExpression: /^\w+$/
regularExpression: /^[\w ]+$/
}
Keys.onEnterPressed: btnCreate.onClicked()

View File

@@ -25,7 +25,6 @@ Item {
QtObject {
id: priv
property bool useBlurItem1: true
property bool useBlurItem2: rootItem.blurMax > 2
property bool useBlurItem3: rootItem.blurMax > 8
property bool useBlurItem4: rootItem.blurMax > 16
@@ -34,12 +33,12 @@ Item {
BlurItem {
id: blurredItemSource1
property Item src: priv.useBlurItem1 ? source : null
property Item src: source
// Size of the first blurred item is by default half of the source.
// Increase for quality and decrease for performance & more blur.
readonly property int blurItemSize: 8
width: src ? Math.ceil(src.width / 16) * blurItemSize : 0
height: src ? Math.ceil(src.height / 16) * blurItemSize : 0
width: Math.ceil(rootItem.width / 16) * blurItemSize
height: Math.ceil(rootItem.height / 16) * blurItemSize
}
BlurItem {
id: blurredItemSource2

View File

@@ -86,6 +86,9 @@ T.TextField {
// was closed due to an menu item click.
if (control.activeFocus && control.focusReason !== Qt.OtherFocusReason)
control.preFocusText = control.text
if (!control.activeFocus)
control.deselect()
}
onEditChanged: {

View File

@@ -189,7 +189,8 @@ struct DockAreaWidgetPrivate
{
DockAreaWidget *q = nullptr;
QBoxLayout *m_layout = nullptr;
DockAreaLayout *m_contentsLayout = nullptr;
// DockAreaLayout is not a QObject -> std::unique_ptr manages deletion
std::unique_ptr<DockAreaLayout> m_contentsLayout;
DockAreaTitleBar *m_titleBar = nullptr;
DockManager *m_dockManager = nullptr;
AutoHideDockContainer *m_autoHideDockContainer = nullptr;
@@ -328,7 +329,7 @@ DockAreaWidget::DockAreaWidget(DockManager *dockManager, DockContainerWidget *pa
setLayout(d->m_layout);
d->createTitleBar();
d->m_contentsLayout = new DockAreaLayout(d->m_layout);
d->m_contentsLayout = std::make_unique<DockAreaLayout>(d->m_layout);
if (d->m_dockManager)
emit d->m_dockManager->dockAreaCreated(this);
}
@@ -336,8 +337,6 @@ DockAreaWidget::DockAreaWidget(DockManager *dockManager, DockContainerWidget *pa
DockAreaWidget::~DockAreaWidget()
{
qCInfo(adsLog) << Q_FUNC_INFO;
delete d->m_contentsLayout;
delete d;
}
DockManager *DockAreaWidget::dockManager() const

View File

@@ -9,6 +9,8 @@
#include <QFrame>
#include <memory>
QT_BEGIN_NAMESPACE
class QAbstractButton;
class QXmlStreamWriter;
@@ -33,7 +35,7 @@ class ADS_EXPORT DockAreaWidget : public QFrame
{
Q_OBJECT
private:
DockAreaWidgetPrivate *d; ///< private data (pimpl)
std::unique_ptr<DockAreaWidgetPrivate> d; ///< private data (pimpl)
friend struct DockAreaWidgetPrivate;
friend class DockContainerWidget;
friend class DockContainerWidgetPrivate;

View File

@@ -137,9 +137,9 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
QString trimmedLine = codeLine.trimmed();
if (trimmedLine.startsWith("@requires")) {
// Get the required node, remove "@requires "
QString nodeName = trimmedLine.sliced(10);
if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName))
m_requiredNodes << nodeName;
QString nodeId = trimmedLine.sliced(10).toLower();
if (!nodeId.isEmpty() && !m_requiredNodes.contains(nodeId))
m_requiredNodes << nodeId;
}
}
}

View File

@@ -124,6 +124,10 @@ void EffectMakerModel::addNode(const QString &nodeQenPath)
const QString path = EffectUtils::nodesSourcesPath() + "/common/" + requiredId + ".qen";
auto requiredNode = new CompositionNode({}, path);
connect(qobject_cast<EffectMakerUniformsModel *>(requiredNode->uniformsModel()),
&EffectMakerUniformsModel::dataChanged, this, [this] {
setHasUnsavedChanges(true);
});
requiredNode->setRefCount(1);
m_nodes.prepend(requiredNode);
}
@@ -490,6 +494,9 @@ QJsonObject nodeToJson(const CompositionNode &node)
QString type = Uniform::stringFromType(uniform->type());
uniformObject.insert("type", type);
if (!uniform->displayName().isEmpty())
uniformObject.insert("displayName", QString(uniform->displayName()));
QString value = variantAsDataString(uniform->type(), uniform->value());
if (uniform->type() == Uniform::Type::Sampler)
value = QFileInfo(value).fileName();

View File

@@ -114,6 +114,11 @@ QString Uniform::description() const
return m_description;
}
QString Uniform::displayName() const
{
return m_displayName;
}
QString Uniform::customValue() const
{
return m_customValue;

View File

@@ -59,6 +59,7 @@ public:
QString name() const;
QString description() const;
QString displayName() const;
QString customValue() const;
void setCustomValue(const QString &newCustomValue);

View File

@@ -115,9 +115,8 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
m_assetsWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
m_assetsWidget->engine()->addImageProvider("qmldesigner_assets", m_assetsIconProvider);
connect(m_assetsModel, &AssetsLibraryModel::fileChanged, [](const QString &changeFilePath) {
QmlDesignerPlugin::instance()->emitAssetChanged(changeFilePath);
});
connect(m_assetsModel, &AssetsLibraryModel::fileChanged,
QmlDesignerPlugin::instance(), &QmlDesignerPlugin::assetChanged);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});

View File

@@ -179,15 +179,16 @@ bool CollectionDetailsModel::setHeaderData(int section,
return headerChanged;
}
bool CollectionDetailsModel::insertRows(int row, int count, const QModelIndex &parent)
bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent)
{
if (count < 1)
return false;
row = qBound(0, row, rowCount());
beginInsertRows(parent, row, row + count);
beginResetModel();
m_currentCollection.insertEmptyElements(row, count);
endInsertRows();
endResetModel();
selectRow(row);
return true;

View File

@@ -3,11 +3,9 @@
#include "collectioneditorutils.h"
#include "abstractview.h"
#include "bindingproperty.h"
#include "model.h"
#include "nodemetainfo.h"
#include "propertymetainfo.h"
#include "variantproperty.h"
#include <variant>
@@ -137,33 +135,6 @@ QString getSourceCollectionType(const ModelNode &node)
return {};
}
void assignCollectionToNode(AbstractView *view,
const ModelNode &modelNode,
const ModelNode &collectionSourceNode,
const QString &collectionName)
{
QTC_ASSERT(modelNode.isValid() && collectionSourceNode.isValid(), return);
QString sourceId = isDataStoreNode(collectionSourceNode) ? "DataStore"
: collectionSourceNode.id();
if (sourceId.isEmpty() || !canAcceptCollectionAsModel(modelNode))
return;
VariantProperty sourceProperty = collectionSourceNode.variantProperty(collectionName.toLatin1());
if (!sourceProperty.exists())
return;
BindingProperty modelProperty = modelNode.bindingProperty("model");
QString identifier = QString("%1.%2").arg(sourceId, QString::fromLatin1(sourceProperty.name()));
view->executeInTransaction("CollectionEditor::assignCollectionToNode",
[&modelProperty, &identifier]() {
modelProperty.setExpression(identifier);
});
}
Utils::FilePath dataStoreJsonFilePath()
{
return collectionPath("models.json");

View File

@@ -24,11 +24,6 @@ QString getSourceCollectionType(const QmlDesigner::ModelNode &node);
QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode);
void assignCollectionToNode(AbstractView *view,
const ModelNode &modelNode,
const ModelNode &collectionSourceNode,
const QString &collectionName);
Utils::FilePath dataStoreJsonFilePath();
Utils::FilePath dataStoreQmlFilePath();

View File

@@ -394,27 +394,24 @@ void CollectionSourceModel::updateNodeSource(const ModelNode &node)
updateCollectionList(index);
}
void CollectionSourceModel::onSelectedCollectionChanged(int collectionIndex)
void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *collectionList,
int collectionIndex)
{
CollectionListModel *collectionList = qobject_cast<CollectionListModel *>(sender());
if (collectionIndex > -1 && collectionList) {
if (collectionIndex > -1) {
if (m_previousSelectedList && m_previousSelectedList != collectionList)
m_previousSelectedList->selectCollectionIndex(-1);
m_previousSelectedList = collectionList;
emit collectionSelected(collectionList->sourceNode(),
collectionList->collectionNameAt(collectionIndex));
emit collectionSelected(collectionList->collectionNameAt(collectionIndex));
selectSourceIndex(sourceIndex(collectionList->sourceNode()));
}
}
void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, const QString &newName)
void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList,
const QString &oldName, const QString &newName)
{
CollectionListModel *collectionList = qobject_cast<CollectionListModel *>(sender());
QTC_ASSERT(collectionList, return);
auto emitRenameWarning = [this](const QString &msg) -> void {
emit this->warning(tr("Rename Model"), msg);
};
@@ -496,15 +493,14 @@ void CollectionSourceModel::onCollectionNameChanged(const QString &oldName, cons
return;
}
emit collectionRenamed(oldName, newName);
updateCollectionList(nodeIndex);
}
}
void CollectionSourceModel::onCollectionsRemoved(const QStringList &removedCollections)
void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList,
const QStringList &removedCollections)
{
CollectionListModel *collectionList = qobject_cast<CollectionListModel *>(sender());
QTC_ASSERT(collectionList, return);
auto emitDeleteWarning = [this](const QString &msg) -> void {
emit warning(tr("Delete Model"), msg);
};
@@ -551,10 +547,12 @@ void CollectionSourceModel::onCollectionsRemoved(const QStringList &removedColle
if (document.isObject()) {
QJsonObject rootObject = document.object();
QStringList collectionsRemovedFromDocument;
for (const QString &collectionName : removedCollections) {
bool sourceContainsCollection = rootObject.contains(collectionName);
if (sourceContainsCollection) {
rootObject.remove(collectionName);
collectionsRemovedFromDocument << collectionName;
} else {
emitDeleteWarning(tr("The model group doesn't contain the model name (%1).")
.arg(sourceContainsCollection));
@@ -576,6 +574,9 @@ void CollectionSourceModel::onCollectionsRemoved(const QStringList &removedColle
return;
}
for (const QString &collectionName : std::as_const(collectionsRemovedFromDocument))
emit this->collectionRemoved(collectionName);
updateCollectionList(nodeIndex);
}
}
@@ -606,7 +607,7 @@ void CollectionSourceModel::setSelectedIndex(int idx)
} else if (m_previousSelectedList) {
m_previousSelectedList->selectCollectionIndex(-1);
m_previousSelectedList = {};
emit this->collectionSelected(sourceNodeAt(idx), {});
emit this->collectionSelected({});
}
}
}
@@ -630,37 +631,38 @@ void CollectionSourceModel::updateCollectionList(QModelIndex index)
return;
ModelNode sourceNode = sourceNodeAt(index.row());
QSharedPointer<CollectionListModel> currentList = m_collectionList.at(index.row());
QSharedPointer<CollectionListModel> newList = loadCollection(sourceNode, currentList);
if (currentList != newList) {
QSharedPointer<CollectionListModel> oldList = m_collectionList.at(index.row());
QSharedPointer<CollectionListModel> newList = loadCollection(sourceNode, oldList);
if (oldList != newList) {
m_collectionList.replace(index.row(), newList);
emit dataChanged(index, index, {CollectionsRole});
emit collectionNamesChanged(sourceNode, newList->stringList());
registerCollection(newList);
}
}
void CollectionSourceModel::registerCollection(const QSharedPointer<CollectionListModel> &collection)
{
connect(collection.data(),
&CollectionListModel::selectedIndexChanged,
this,
&CollectionSourceModel::onSelectedCollectionChanged,
Qt::UniqueConnection);
CollectionListModel *collectionList = collection.data();
if (collectionList == nullptr)
return;
connect(collection.data(),
&CollectionListModel::collectionNameChanged,
this,
&CollectionSourceModel::onCollectionNameChanged,
Qt::UniqueConnection);
connect(collectionList, &CollectionListModel::selectedIndexChanged, this,
[this, collectionList](int idx) {
onSelectedCollectionChanged(collectionList, idx);
}, Qt::UniqueConnection);
connect(collection.data(),
&CollectionListModel::collectionsRemoved,
this,
&CollectionSourceModel::onCollectionsRemoved,
Qt::UniqueConnection);
connect(collectionList, &CollectionListModel::collectionNameChanged, this,
[this, collectionList](const QString &oldName, const QString &newName) {
onCollectionNameChanged(collectionList, oldName, newName);
}, Qt::UniqueConnection);
if (collection.data() && collection->sourceNode())
emit collectionNamesChanged(collection->sourceNode(), collection->stringList());
connect(collectionList, &CollectionListModel::collectionsRemoved, this,
[this, collectionList](const QStringList &removedCollections) {
onCollectionsRemoved(collectionList, removedCollections);
}, Qt::UniqueConnection);
if (collectionList->sourceNode().isValid())
emit collectionNamesInitialized(collection->stringList());
}
QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const

View File

@@ -70,15 +70,20 @@ public:
signals:
void selectedIndexChanged(int idx);
void collectionSelected(const ModelNode &sourceNode, const QString &collectionName);
void collectionNamesChanged(const ModelNode &sourceNode, QStringList collections);
void collectionSelected(const QString &collectionName);
void collectionNamesInitialized(const QStringList &initialList);
void collectionRenamed(const QString &oldname, const QString &newName);
void collectionRemoved(const QString &collectionName);
void isEmptyChanged(bool);
void warning(const QString &title, const QString &body);
private slots:
void onSelectedCollectionChanged(int collectionIndex);
void onCollectionNameChanged(const QString &oldName, const QString &newName);
void onCollectionsRemoved(const QStringList &removedCollections);
void onSelectedCollectionChanged(CollectionListModel *collectionList, int collectionIndex);
void onCollectionNameChanged(CollectionListModel *collectionList, const QString &oldName,
const QString &newName);
void onCollectionsRemoved(CollectionListModel *collectionList,
const QStringList &removedCollections);
private:
void setSelectedIndex(int idx);

View File

@@ -70,8 +70,8 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
connect(sourceModel,
&CollectionSourceModel::collectionSelected,
this,
[this](const ModelNode &sourceNode, const QString &collection) {
m_widget->collectionDetailsModel()->loadCollection(sourceNode, collection);
[this](const QString &collection) {
m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection);
});
connect(sourceModel, &CollectionSourceModel::isEmptyChanged, this, [this](bool isEmpty) {
@@ -80,12 +80,25 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo()
});
connect(sourceModel,
&CollectionSourceModel::collectionNamesChanged,
&CollectionSourceModel::collectionNamesInitialized,
this,
[this](const ModelNode &sourceNode, const QStringList &collectionNames) {
if (sourceNode == m_dataStore->modelNode())
[this](const QStringList &collectionNames) {
m_dataStore->setCollectionNames(collectionNames);
});
connect(sourceModel,
&CollectionSourceModel::collectionRenamed,
this,
[this](const QString &oldName, const QString &newName) {
m_dataStore->renameCollection(oldName, newName);
});
connect(sourceModel,
&CollectionSourceModel::collectionRemoved,
this,
[this](const QString &collectionName) {
m_dataStore->removeCollection(collectionName);
});
}
return createWidgetInfo(m_widget.data(),
@@ -200,6 +213,12 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt
});
}
void CollectionView::assignCollectionToSelectedNode(const QString &collectionName)
{
QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return);
m_dataStore->assignCollectionToNode(this, singleSelectedModelNode(), collectionName);
}
void CollectionView::registerDeclarativeType()
{
CollectionDetails::registerDeclarativeType();

View File

@@ -43,6 +43,8 @@ public:
void addResource(const QUrl &url, const QString &name, const QString &type);
void assignCollectionToSelectedNode(const QString &collectionName);
static void registerDeclarativeType();
void resetDataStoreNode();

View File

@@ -337,17 +337,7 @@ bool CollectionWidget::addCollectionToDataStore(const QString &collectionName)
void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName)
{
ModelNode dsNode = dataStoreNode();
ModelNode targetNode = m_view->singleSelectedModelNode();
QTC_ASSERT(dsNode.isValid() && targetNode.isValid(), return);
if (dsNode.id().isEmpty()) {
warn(tr("Assigning the model"), tr("The model must have a valid id to be assigned."));
return;
}
CollectionEditor::assignCollectionToNode(m_view, targetNode, dsNode, collectionName);
m_view->assignCollectionToSelectedNode(collectionName);
}
void CollectionWidget::ensureDataStoreExists()

View File

@@ -3,6 +3,7 @@
#include "datastoremodelnode.h"
#include "abstractview.h"
#include "collectioneditorconstants.h"
#include "collectioneditorutils.h"
#include "model/qmltextgenerator.h"
@@ -22,6 +23,9 @@
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
namespace {
QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node)
@@ -42,6 +46,19 @@ QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node)
return defaultsNodeProps + dynamicPropertyNames;
}
bool isValidCollectionPropertyName(const QString &collectionId)
{
static const QmlDesigner::PropertyNameList reservedKeywords = {
QmlDesigner::CollectionEditor::SOURCEFILE_PROPERTY,
QmlDesigner::CollectionEditor::JSONBACKEND_TYPENAME,
"backend",
"models",
};
return QmlDesigner::ModelNode::isValidId(collectionId)
&& !reservedKeywords.contains(collectionId.toLatin1());
}
} // namespace
namespace QmlDesigner {
@@ -85,15 +102,13 @@ void DataStoreModelNode::reloadModel()
m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString();
if (forceUpdate) {
updateDataStoreProperties();
updateSingletonFile();
}
if (forceUpdate)
update();
}
QStringList DataStoreModelNode::collectionNames() const
{
return m_collectionNames;
return m_collectionPropertyNames.keys();
}
Model *DataStoreModelNode::model() const
@@ -137,23 +152,60 @@ void DataStoreModelNode::updateDataStoreProperties()
static TypeName childNodeTypename = "ChildListModel";
QSet<QString> collectionNamesToBeAdded;
const QStringList allCollectionNames = m_collectionPropertyNames.keys();
for (const QString &collectionName : allCollectionNames)
collectionNamesToBeAdded << collectionName;
const QList<AbstractProperty> formerPropertyNames = rootNode.dynamicProperties();
for (const AbstractProperty &property : formerPropertyNames)
// Remove invalid collection names from the properties
for (const AbstractProperty &property : formerPropertyNames) {
if (!property.isNodeProperty())
continue;
NodeProperty nodeProprty = property.toNodeProperty();
if (!nodeProprty.hasDynamicTypeName(childNodeTypename))
continue;
ModelNode childNode = nodeProprty.modelNode();
if (childNode.hasProperty(CollectionEditor::JSONCHILDMODELNAME_PROPERTY)) {
QString modelName = childNode.property(CollectionEditor::JSONCHILDMODELNAME_PROPERTY)
.toVariantProperty()
.value()
.toString();
if (collectionNamesToBeAdded.contains(modelName)) {
m_collectionPropertyNames.insert(modelName, property.name());
collectionNamesToBeAdded.remove(modelName);
} else {
rootNode.removeProperty(property.name());
}
} else {
rootNode.removeProperty(property.name());
}
}
rootNode.setIdWithoutRefactoring("models");
for (const QString &collectionName : std::as_const(m_collectionNames)) {
PropertyName newName = collectionName.toLatin1();
QStringList collectionNamesLeft = collectionNamesToBeAdded.values();
Utils::sort(collectionNamesLeft);
for (const QString &collectionName : std::as_const(collectionNamesLeft)) {
PropertyName newPropertyName = getUniquePropertyName(collectionName);
if (newPropertyName.isEmpty()) {
qWarning() << __FUNCTION__ << __LINE__
<< QString("The property name cannot be generated from \"%1\"").arg(collectionName);
continue;
}
ModelNode collectionNode = model()->createModelNode(childNodeTypename);
VariantProperty modelNameProperty = collectionNode.variantProperty(
CollectionEditor::JSONCHILDMODELNAME_PROPERTY);
modelNameProperty.setValue(newName);
modelNameProperty.setValue(collectionName);
NodeProperty nodeProp = rootNode.nodeProperty(newName);
NodeProperty nodeProp = rootNode.nodeProperty(newPropertyName);
nodeProp.setDynamicTypeNameAndsetModelNode(childNodeTypename, collectionNode);
m_collectionPropertyNames.insert(collectionName, newPropertyName);
}
// Backend Property
@@ -186,13 +238,127 @@ void DataStoreModelNode::updateSingletonFile()
file.finalize();
}
void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames)
void DataStoreModelNode::update()
{
if (m_collectionNames != newCollectionNames) {
m_collectionNames = newCollectionNames;
updateDataStoreProperties();
updateSingletonFile();
}
PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName)
{
ModelNode dataStoreNode = modelNode();
QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {});
QString newProperty;
// convert to camel case
QStringList nameWords = collectionName.split(' ');
nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
for (int i = 1; i < nameWords.size(); ++i)
nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
newProperty = nameWords.join("");
// if id starts with a number prepend an underscore
if (newProperty.at(0).isDigit())
newProperty.prepend('_');
// If the new id is not valid (e.g. qml keyword match), prepend an underscore
if (!isValidCollectionPropertyName(newProperty))
newProperty.prepend('_');
static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists
QRegularExpressionMatch match = rgx.match(newProperty);
if (match.hasMatch()) { // ends with a number, increment it
QString numStr = match.captured();
int num = numStr.toInt() + 1;
newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num);
} else {
newProperty.append('1');
}
}
return newProperty.toLatin1();
}
void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames)
{
m_collectionPropertyNames.clear();
for (const QString &collectionName : newCollectionNames)
m_collectionPropertyNames.insert(collectionName, {});
update();
}
void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName)
{
ModelNode dataStoreNode = modelNode();
QTC_ASSERT(dataStoreNode.isValid(), return);
if (m_collectionPropertyNames.contains(oldName)) {
const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName);
if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) {
NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty();
if (collectionNode.isValid()) {
VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty(
CollectionEditor::JSONCHILDMODELNAME_PROPERTY);
modelNameProperty.setValue(newName);
m_collectionPropertyNames.remove(oldName);
m_collectionPropertyNames.insert(newName, collectionNode.name());
update();
return;
}
qWarning() << __FUNCTION__ << __LINE__
<< "There is no valid node for the old collection name";
return;
}
qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name")
<< oldPropertyName;
return;
}
qWarning() << __FUNCTION__ << __LINE__
<< QString("There is no old collection name registered with this name \"%1\"").arg(oldName);
}
void DataStoreModelNode::removeCollection(const QString &collectionName)
{
if (m_collectionPropertyNames.contains(collectionName)) {
m_collectionPropertyNames.remove(collectionName);
update();
}
}
void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
const ModelNode &targetNode,
const QString &collectionName)
{
QTC_ASSERT(targetNode.isValid(), return);
if (!CollectionEditor::canAcceptCollectionAsModel(targetNode))
return;
if (!m_collectionPropertyNames.contains(collectionName)) {
qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore"
<< collectionName;
return;
}
PropertyName propertyName = m_collectionPropertyNames.value(collectionName);
const ModelNode dataStore = modelNode();
VariantProperty sourceProperty = dataStore.variantProperty(propertyName);
if (!sourceProperty.exists()) {
qWarning() << __FUNCTION__ << __LINE__
<< "The source property doesn't exist in the DataStore.";
return;
}
BindingProperty modelProperty = targetNode.bindingProperty("model");
QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name()));
view->executeInTransaction("assignCollectionToNode", [&modelProperty, &identifier]() {
modelProperty.setExpression(identifier);
});
}
} // namespace QmlDesigner

View File

@@ -5,6 +5,8 @@
#include <modelnode.h>
#include <QMap>
namespace QmlDesigner {
class Model;
@@ -21,6 +23,12 @@ public:
ModelNode modelNode() const;
void setCollectionNames(const QStringList &newCollectionNames);
void renameCollection(const QString &oldName, const QString &newName);
void removeCollection(const QString &collectionName);
void assignCollectionToNode(AbstractView *view,
const ModelNode &targetNode,
const QString &collectionName);
private:
QString getModelQmlText();
@@ -28,9 +36,11 @@ private:
void reset();
void updateDataStoreProperties();
void updateSingletonFile();
void update();
PropertyName getUniquePropertyName(const QString &collectionName);
ModelPointer m_model;
QStringList m_collectionNames;
QMap<QString, PropertyName> m_collectionPropertyNames;
QString m_dataRelativePath;
};

View File

@@ -27,8 +27,15 @@ QWidget *EventListDelegate::createEditor(QWidget *parent,
{
if (index.column() == EventListModel::shortcutColumn) {
auto *editor = new ShortcutWidget(parent);
connect(editor, &ShortcutWidget::done, this, &EventListDelegate::commitAndClose);
connect(editor, &ShortcutWidget::cancel, this, &EventListDelegate::close);
connect(editor, &ShortcutWidget::done, this, [this, editor] {
auto that = const_cast<EventListDelegate *>(this);
emit that->commitData(editor);
emit that->closeEditor(editor);
});
connect(editor, &ShortcutWidget::cancel, this, [this, editor] {
auto that = const_cast<EventListDelegate *>(this);
emit that->closeEditor(editor);
});
return editor;
} else if (index.column() == EventListModel::connectColumn) {
return nullptr;
@@ -170,18 +177,4 @@ QSize EventListDelegate::sizeHint(const QStyleOptionViewItem &option, const QMod
return QStyledItemDelegate::sizeHint(option, index);
}
void EventListDelegate::commitAndClose()
{
if (auto *editor = qobject_cast<ShortcutWidget *>(sender())) {
emit commitData(editor);
emit closeEditor(editor);
}
}
void EventListDelegate::close()
{
if (auto *editor = qobject_cast<ShortcutWidget *>(sender()))
emit closeEditor(editor);
}
} // namespace QmlDesigner.

View File

@@ -39,9 +39,6 @@ protected:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
void close();
void commitAndClose();
static bool hasConnectionColumn(QObject *parent);
static QRect connectButtonRect(const QStyleOptionViewItem &option);
};

View File

@@ -51,18 +51,4 @@ bool NodeListDelegate::eventFilter(QObject *editor, QEvent *event)
return QStyledItemDelegate::eventFilter(editor, event);
}
void NodeListDelegate::commitAndClose()
{
if (auto *editor = qobject_cast<ShortcutWidget *>(sender())) {
emit commitData(editor);
emit closeEditor(editor);
}
}
void NodeListDelegate::close()
{
if (auto *editor = qobject_cast<ShortcutWidget *>(sender()))
emit closeEditor(editor);
}
} // namespace QmlDesigner.

View File

@@ -16,10 +16,6 @@ public:
protected:
bool eventFilter(QObject *editor, QEvent *event) override;
private:
void close();
void commitAndClose();
};
} // namespace QmlDesigner.

View File

@@ -41,12 +41,12 @@ EasingCurveDialog::EasingCurveDialog(const QList<ModelNode> &frames, QWidget *pa
{
setWindowFlag(Qt::Tool, true);
auto tw = new QTabWidget;
tw->setTabPosition(QTabWidget::East);
tw->addTab(m_splineEditor, "Curve");
tw->addTab(m_text, "Text");
m_tabWidget = new QTabWidget;
m_tabWidget->setTabPosition(QTabWidget::East);
m_tabWidget->addTab(m_splineEditor, "Curve");
m_tabWidget->addTab(m_text, "Text");
connect(tw, &QTabWidget::currentChanged, this, &EasingCurveDialog::tabClicked);
connect(m_tabWidget, &QTabWidget::currentChanged, this, &EasingCurveDialog::tabClicked);
connect(m_text, &QPlainTextEdit::textChanged, this, &EasingCurveDialog::textChanged);
auto labelFont = m_label->font();
@@ -105,7 +105,7 @@ EasingCurveDialog::EasingCurveDialog(const QList<ModelNode> &frames, QWidget *pa
grid->addLayout(vbox, 0, 0);
grid->addWidget(presetBar, 0, 1, Qt::AlignBottom);
grid->addWidget(tw);
grid->addWidget(m_tabWidget);
grid->addWidget(m_presets, 1, 1);
grid->addLayout(m_durationLayout, 2, 0);
grid->addLayout(buttonLayout, 2, 1);
@@ -126,7 +126,6 @@ EasingCurveDialog::EasingCurveDialog(const QList<ModelNode> &frames, QWidget *pa
connect(durationEdit, &QSpinBox::valueChanged, m_splineEditor, &SplineEditor::setDuration);
connect(animateButton, &QPushButton::clicked, m_splineEditor, &SplineEditor::animate);
resize(QSize(1421, 918));
}
@@ -185,7 +184,7 @@ bool EasingCurveDialog::apply()
}
AbstractView *view = m_frames.first().view();
return view->executeInTransaction("EasingCurveDialog::apply", [this](){
return view->executeInTransaction("EasingCurveDialog::apply", [this] {
auto expression = m_splineEditor->easingCurve().toString();
for (const auto &frame : std::as_const(m_frames))
frame.bindingProperty(m_easingCurveProperty).setExpression(expression);
@@ -201,31 +200,24 @@ void EasingCurveDialog::textChanged()
void EasingCurveDialog::tabClicked(int id)
{
if (auto tw = qobject_cast<const QTabWidget *>(sender())) {
int seid = tw->indexOf(m_splineEditor);
const int seid = m_tabWidget->indexOf(m_splineEditor);
if (seid == id) {
for (int i = 0; i < m_durationLayout->count(); ++i) {
auto *item = m_durationLayout->itemAt(i);
if (auto *widget = item->widget())
if (auto *widget = m_durationLayout->itemAt(i)->widget())
widget->show();
}
auto curve = m_splineEditor->easingCurve();
curve.fromString(m_text->toPlainText());
m_splineEditor->setEasingCurve(curve);
} else {
for (int i = 0; i < m_durationLayout->count(); ++i) {
auto *item = m_durationLayout->itemAt(i);
if (auto *widget = item->widget())
if (auto *widget = m_durationLayout->itemAt(i)->widget())
widget->hide();
}
auto curve = m_splineEditor->easingCurve();
m_text->setPlainText(curve.toString());
}
}
}
void EasingCurveDialog::presetTabClicked(int id)
{

View File

@@ -9,9 +9,10 @@
#include <modelnode.h>
QT_BEGIN_NAMESPACE
class QHBoxLayout;
class QLabel;
class QPlainTextEdit;
class QHBoxLayout;
class QTabWidget;
QT_END_NAMESPACE
namespace QmlDesigner {
@@ -33,32 +34,21 @@ public:
private:
bool apply();
void textChanged();
void tabClicked(int id);
void presetTabClicked(int id);
void buttonsClicked(QDialogButtonBox::StandardButton button);
void updateEasingCurve(const EasingCurve &curve);
private:
QTabWidget *m_tabWidget = nullptr;
SplineEditor *m_splineEditor = nullptr;
QPlainTextEdit *m_text = nullptr;
PresetEditor *m_presets = nullptr;
QHBoxLayout *m_durationLayout = nullptr;
QDialogButtonBox *m_buttons = nullptr;
QLabel *m_label = nullptr;
QList<ModelNode> m_frames;
PropertyName m_easingCurveProperty;
};

View File

@@ -62,11 +62,12 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
#include <sqlitelibraryinitializer.h>
#include <qmldesignerbase/qmldesignerbaseplugin.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <sqlite/sqlitelibraryinitializer.h>
#include <utils/algorithm.h>
#include <utils/guard.h>
#include <utils/hostosinfo.h>
#include <utils/mimeconstants.h>
#include <utils/qtcassert.h>
@@ -82,9 +83,10 @@
#include <QTimer>
#include <QWindow>
#include "nanotrace/nanotrace.h"
#include <modelnodecontextmenu_helper.h>
#include <mutex>
static Q_LOGGING_CATEGORY(qmldesignerLog, "qtc.qmldesigner", QtWarningMsg)
using namespace QmlDesigner::Internal;
@@ -162,7 +164,7 @@ public:
SettingsPage settingsPage{externalDependencies};
DesignModeWidget mainWidget;
QtQuickDesignerFactory m_qtQuickDesignerFactory;
bool blockEditorChange = false;
Utils::Guard m_ignoreChanges;
Utils::UniqueObjectPtr<QToolBar> toolBar;
Utils::UniqueObjectPtr<QWidget> statusBar;
QHash<QString, TraceIdentifierData> m_traceIdentifierDataHash;
@@ -497,7 +499,7 @@ void QmlDesignerPlugin::hideDesigner()
void QmlDesignerPlugin::changeEditor()
{
if (d->blockEditorChange)
if (d->m_ignoreChanges.isLocked())
return;
clearDesigner();
@@ -666,18 +668,12 @@ void QmlDesignerPlugin::enforceDelayedInitialize()
DesignDocument *QmlDesignerPlugin::currentDesignDocument() const
{
if (d)
return d->documentManager.currentDesignDocument();
return nullptr;
return d ? d->documentManager.currentDesignDocument() : nullptr;
}
Internal::DesignModeWidget *QmlDesignerPlugin::mainWidget() const
{
if (d)
return &d->mainWidget;
return nullptr;
return d ? &d->mainWidget : nullptr;
}
QWidget *QmlDesignerPlugin::createProjectExplorerWidget(QWidget *parent) const
@@ -687,21 +683,15 @@ QWidget *QmlDesignerPlugin::createProjectExplorerWidget(QWidget *parent) const
void QmlDesignerPlugin::switchToTextModeDeferred()
{
QTimer::singleShot(0, this, [] () {
QTimer::singleShot(0, this, [] {
Core::ModeManager::activateMode(Core::Constants::MODE_EDIT);
});
}
void QmlDesignerPlugin::emitCurrentTextEditorChanged(Core::IEditor *editor)
{
d->blockEditorChange = true;
const std::lock_guard locker(d->m_ignoreChanges);
emit Core::EditorManager::instance()->currentEditorChanged(editor);
d->blockEditorChange = false;
}
void QmlDesignerPlugin::emitAssetChanged(const QString &assetPath)
{
emit assetChanged(assetPath);
}
double QmlDesignerPlugin::formEditorDevicePixelRatio()
@@ -717,7 +707,7 @@ double QmlDesignerPlugin::formEditorDevicePixelRatio()
void QmlDesignerPlugin::contextHelp(const Core::IContext::HelpCallback &callback, const QString &id)
{
emitUsageStatisticsHelpRequested(id);
emitUsageStatistics(Constants::EVENT_HELP_REQUESTED + id);
QmlDesignerPlugin::instance()->viewManager().qmlJSEditorContextHelp(callback);
}
@@ -733,7 +723,7 @@ void QmlDesignerPlugin::emitUsageStatistics(const QString &identifier)
const int currentTime = privateInstance()->timer.elapsed();
const int currentDuration = (currentTime - activeData.time);
if (currentDuration < activeData.maxDuration)
instance()->emitUsageStatisticsUsageDuration(activeData.newIdentifer, currentDuration);
emit instance()->usageStatisticsUsageDuration(activeData.newIdentifer, currentDuration);
privateInstance()->m_activeTraceIdentifierDataHash.remove(identifier);
}
@@ -761,11 +751,6 @@ void QmlDesignerPlugin::emitUsageStatisticsContextAction(const QString &identifi
emitUsageStatistics(Constants::EVENT_ACTION_EXECUTED + identifier);
}
void QmlDesignerPlugin::emitUsageStatisticsHelpRequested(const QString &identifier)
{
emitUsageStatistics(Constants::EVENT_HELP_REQUESTED + identifier);
}
AsynchronousImageCache &QmlDesignerPlugin::imageCache()
{
return m_instance->d->projectManager.asynchronousImageCache();
@@ -776,32 +761,18 @@ void QmlDesignerPlugin::registerPreviewImageProvider(QQmlEngine *engine)
m_instance->d->projectManager.registerPreviewImageProvider(engine);
}
bool isParent(QWidget *parent, QWidget *widget)
{
if (!widget)
return false;
if (widget == parent)
return true;
return isParent(parent, widget->parentWidget());
}
void QmlDesignerPlugin::trackWidgetFocusTime(QWidget *widget, const QString &identifier)
{
connect(qApp,
&QApplication::focusChanged,
widget,
[widget, identifier](QWidget *from, QWidget *to) {
connect(qApp, &QApplication::focusChanged,
widget, [widget, identifier](QWidget *from, QWidget *to) {
static QElapsedTimer widgetUsageTimer;
static QString lastIdentifier;
if (isParent(widget, to)) {
if (widget->isAncestorOf(to)) {
if (!lastIdentifier.isEmpty())
emitUsageStatisticsTime(lastIdentifier, widgetUsageTimer.elapsed());
widgetUsageTimer.restart();
lastIdentifier = identifier;
} else if (isParent(widget, from) && lastIdentifier == identifier) {
} else if (widget->isAncestorOf(from) && lastIdentifier == identifier) {
emitUsageStatisticsTime(identifier, widgetUsageTimer.elapsed());
lastIdentifier.clear();
}
@@ -880,7 +851,6 @@ void QmlDesignerPlugin::closeFeedbackPopup()
void QmlDesignerPlugin::emitUsageStatisticsTime(const QString &identifier, int elapsed)
{
QTC_ASSERT(instance(), return);
emit instance()->usageStatisticsUsageTimer(normalizeIdentifier(identifier), elapsed);
}

View File

@@ -66,15 +66,12 @@ public:
void switchToTextModeDeferred();
void emitCurrentTextEditorChanged(Core::IEditor *editor);
void emitAssetChanged(const QString &assetPath);
static double formEditorDevicePixelRatio();
static void contextHelp(const Core::IContext::HelpCallback &callback, const QString &id);
static void emitUsageStatistics(const QString &identifier);
static void emitUsageStatisticsContextAction(const QString &identifier);
static void emitUsageStatisticsHelpRequested(const QString &identifier);
static void emitUsageStatisticsTime(const QString &identifier, int elapsed);
static void emitUsageStatisticsUsageDuration(const QString &identifier, int elapsed);

View File

@@ -45,6 +45,11 @@
namespace QmlDesigner {
static DesignDocument *currentDesignDocument()
{
return QmlDesignerPlugin::instance()->currentDesignDocument();
}
ShortCutManager::ShortCutManager()
: QObject()
, m_exportAsImageAction(tr("Export as &Image..."))
@@ -58,9 +63,7 @@ ShortCutManager::ShortCutManager()
, m_duplicateAction(tr("&Duplicate"))
, m_selectAllAction(tr("Select &All"))
, m_escapeAction(this)
{
}
{}
void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContext,
const Core::Context &qmlDesignerFormEditorContext,
@@ -327,16 +330,24 @@ void ShortCutManager::selectAll()
void ShortCutManager::connectUndoActions(DesignDocument *designDocument)
{
if (designDocument) {
connect(designDocument, &DesignDocument::undoAvailable, this, &ShortCutManager::undoAvailable);
connect(designDocument, &DesignDocument::redoAvailable, this, &ShortCutManager::redoAvailable);
connect(designDocument, &DesignDocument::undoAvailable, this,
[this, designDocument](bool isAvailable) {
if (currentDesignDocument() == designDocument)
m_undoAction.setEnabled(isAvailable);
});
connect(designDocument, &DesignDocument::redoAvailable, this,
[this, designDocument](bool isAvailable) {
if (currentDesignDocument() == designDocument)
m_redoAction.setEnabled(isAvailable);
});
}
}
void ShortCutManager::disconnectUndoActions(DesignDocument *designDocument)
{
if (currentDesignDocument()) {
disconnect(designDocument, &DesignDocument::undoAvailable, this, &ShortCutManager::undoAvailable);
disconnect(designDocument, &DesignDocument::redoAvailable, this, &ShortCutManager::redoAvailable);
if (designDocument) {
disconnect(designDocument, &DesignDocument::undoAvailable, this, nullptr);
disconnect(designDocument, &DesignDocument::redoAvailable, this, nullptr);
}
}
@@ -351,29 +362,6 @@ void ShortCutManager::updateUndoActions(DesignDocument *designDocument)
}
}
DesignDocument *ShortCutManager::currentDesignDocument() const
{
return QmlDesignerPlugin::instance()->currentDesignDocument();
}
void ShortCutManager::undoAvailable(bool isAvailable)
{
auto documentController = qobject_cast<DesignDocument*>(sender());
if (currentDesignDocument() &&
currentDesignDocument() == documentController) {
m_undoAction.setEnabled(isAvailable);
}
}
void ShortCutManager::redoAvailable(bool isAvailable)
{
auto documentController = qobject_cast<DesignDocument*>(sender());
if (currentDesignDocument() &&
currentDesignDocument() == documentController) {
m_redoAction.setEnabled(isAvailable);
}
}
void ShortCutManager::goIntoComponent()
{
if (currentDesignDocument()

View File

@@ -30,8 +30,6 @@ public:
void connectUndoActions(DesignDocument *designDocument);
void disconnectUndoActions(DesignDocument *designDocument);
void updateUndoActions(DesignDocument *designDocument);
DesignDocument *currentDesignDocument() const;
void updateActions(Core::IEditor* editor);
private:
@@ -43,8 +41,6 @@ private:
void duplicateSelected();
void paste();
void selectAll();
void undoAvailable(bool isAvailable);
void redoAvailable(bool isAvailable);
void goIntoComponent();
private:

View File

@@ -74,7 +74,7 @@ void FileDownloader::start()
QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request);
m_reply = reply;
QNetworkReply::connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
QNetworkReply::connect(reply, &QNetworkReply::readyRead, this, [this, reply] {
bool isDownloadingFile = false;
QString contentType;
if (!reply->hasRawHeader("Content-Type")) {
@@ -115,7 +115,7 @@ void FileDownloader::start()
emit reply->redirectAllowed();
});
QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply] {
if (reply->error()) {
if (reply->error() != QNetworkReply::OperationCanceledError) {
qWarning() << Q_FUNC_INFO << m_url << reply->errorString();
@@ -282,7 +282,7 @@ void FileDownloader::doProbeUrl()
emit reply->redirectAllowed();
});
QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply] {
if (reply->error())
return;
@@ -295,18 +295,14 @@ void FileDownloader::doProbeUrl()
reply->deleteLater();
});
QNetworkReply::connect(reply,
&QNetworkReply::errorOccurred,
this,
[this](QNetworkReply::NetworkError code) {
QNetworkReply::connect(reply, &QNetworkReply::errorOccurred,
this, [this, reply](QNetworkReply::NetworkError code) {
if (QQmlData::wasDeleted(this)) {
qDebug() << Q_FUNC_INFO << "FileDownloader was deleted.";
return;
}
qDebug() << Q_FUNC_INFO << "Network error:" << code
<< qobject_cast<QNetworkReply *>(sender())->errorString();
qDebug() << Q_FUNC_INFO << "Network error:" << code << reply->errorString();
m_available = false;
emit availableChanged();

View File

@@ -216,14 +216,16 @@ void FileExtractor::extract()
m_targetFolder = m_targetPath.toString() + "/" + m_archiveName;
// If the target directory already exists, remove it and its content
QDir targetDir(m_targetFolder);
if (targetDir.exists() && m_clearTargetPathContents)
targetDir.removeRecursively();
if (m_alwaysCreateDir) {
QTC_ASSERT(!m_targetPath.isEmpty(), return );
FilePath targetFilePath = FilePath::fromUserInput(m_targetFolder);
if (targetFilePath.exists() && m_clearTargetPathContents)
targetFilePath.removeRecursively();
// Create a new directory to generate a proper creation date
targetDir.mkdir(m_targetFolder);
}
if (m_alwaysCreateDir)
targetFilePath.createDir();
const auto sourceAndCommand = Unarchiver::sourceAndCommand(m_sourceFile);
QTC_ASSERT(sourceAndCommand, return);
@@ -261,6 +263,8 @@ void FileExtractor::extract()
void QmlDesigner::FileExtractor::removeTempTargetPath()
{
if (m_isTempTargetPath && m_targetPath.exists()) {
QTC_ASSERT(m_targetPath.toString().startsWith(QDir::tempPath()), qDebug() << m_targetPath;
return );
m_targetPath.removeRecursively();
m_isTempTargetPath = false;
}