2023-06-29 15:06:11 +03:00
|
|
|
// Copyright (C) 2023 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
|
|
|
|
|
|
#include "effectmakerwidget.h"
|
|
|
|
|
|
2023-08-25 16:27:01 +03:00
|
|
|
#include "effectmakercontextobject.h"
|
2023-06-29 15:06:11 +03:00
|
|
|
#include "effectmakermodel.h"
|
2023-08-11 12:53:28 +03:00
|
|
|
#include "effectmakernodesmodel.h"
|
2023-06-29 15:06:11 +03:00
|
|
|
#include "effectmakerview.h"
|
2023-12-07 18:32:55 +02:00
|
|
|
#include "effectutils.h"
|
2023-10-18 18:33:42 +03:00
|
|
|
#include "propertyhandler.h"
|
2023-10-09 13:46:08 +03:00
|
|
|
|
2023-11-02 18:09:06 +02:00
|
|
|
//#include "qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h"
|
2023-06-29 15:06:11 +03:00
|
|
|
#include "qmldesignerconstants.h"
|
|
|
|
|
#include "qmldesignerplugin.h"
|
|
|
|
|
#include "theme.h"
|
|
|
|
|
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
|
2023-12-08 12:04:12 +02:00
|
|
|
#include <qmldesigner/documentmanager.h>
|
2023-11-13 17:13:13 +01:00
|
|
|
#include <qmldesigner/qmldesignerconstants.h>
|
|
|
|
|
#include <qmldesigner/qmldesignerplugin.h>
|
2023-06-29 15:06:11 +03:00
|
|
|
#include <studioquickwidget.h>
|
|
|
|
|
|
2023-12-08 12:04:12 +02:00
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
#include <utils/algorithm.h>
|
2023-12-08 12:04:12 +02:00
|
|
|
#include <utils/async.h>
|
2023-06-29 15:06:11 +03:00
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
|
|
|
|
#include <QHBoxLayout>
|
2023-12-12 15:15:15 +02:00
|
|
|
#include <QQmlContext>
|
2023-06-29 15:06:11 +03:00
|
|
|
#include <QQmlEngine>
|
2023-12-12 15:15:15 +02:00
|
|
|
#include <QQuickItem>
|
2023-12-08 12:04:12 +02:00
|
|
|
#include <QTimer>
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2023-09-15 15:36:15 +03:00
|
|
|
namespace EffectMaker {
|
2023-06-29 15:06:11 +03:00
|
|
|
|
|
|
|
|
static QString propertyEditorResourcesPath()
|
|
|
|
|
{
|
|
|
|
|
#ifdef SHARE_QML_PATH
|
|
|
|
|
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
|
|
|
|
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
|
|
|
|
#endif
|
|
|
|
|
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EffectMakerWidget::EffectMakerWidget(EffectMakerView *view)
|
|
|
|
|
: m_effectMakerModel{new EffectMakerModel(this)}
|
2023-08-11 12:53:28 +03:00
|
|
|
, m_effectMakerNodesModel{new EffectMakerNodesModel(this)}
|
2023-06-29 15:06:11 +03:00
|
|
|
, m_effectMakerView(view)
|
2023-08-09 18:15:56 +03:00
|
|
|
, m_quickWidget{new StudioQuickWidget(this)}
|
2023-06-29 15:06:11 +03:00
|
|
|
{
|
|
|
|
|
setWindowTitle(tr("Effect Maker", "Title of effect maker widget"));
|
|
|
|
|
setMinimumWidth(250);
|
|
|
|
|
|
2023-08-09 18:15:56 +03:00
|
|
|
m_quickWidget->quickWidget()->installEventFilter(this);
|
2023-06-29 15:06:11 +03:00
|
|
|
|
|
|
|
|
// create the inner widget
|
2023-09-15 15:36:15 +03:00
|
|
|
m_quickWidget->quickWidget()->setObjectName(QmlDesigner::Constants::OBJECT_NAME_EFFECT_MAKER);
|
2023-08-09 18:15:56 +03:00
|
|
|
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
2023-09-15 15:36:15 +03:00
|
|
|
QmlDesigner::Theme::setupTheme(m_quickWidget->engine());
|
2023-08-09 18:15:56 +03:00
|
|
|
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
2023-12-07 18:32:55 +02:00
|
|
|
m_quickWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common");
|
2023-09-15 15:36:15 +03:00
|
|
|
m_quickWidget->setClearColor(QmlDesigner::Theme::getColor(
|
|
|
|
|
QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
|
2023-06-29 15:06:11 +03:00
|
|
|
|
|
|
|
|
auto layout = new QHBoxLayout(this);
|
|
|
|
|
layout->setContentsMargins({});
|
|
|
|
|
layout->setSpacing(0);
|
2023-08-09 18:15:56 +03:00
|
|
|
layout->addWidget(m_quickWidget.data());
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2023-09-15 15:36:15 +03:00
|
|
|
setStyleSheet(QmlDesigner::Theme::replaceCssColors(
|
2023-06-29 15:06:11 +03:00
|
|
|
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
|
|
|
|
|
2023-09-15 15:36:15 +03:00
|
|
|
QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(this, QmlDesigner::Constants::EVENT_EFFECTMAKER_TIME);
|
2023-06-29 15:06:11 +03:00
|
|
|
|
2023-10-18 18:33:42 +03:00
|
|
|
m_quickWidget->rootContext()->setContextProperty("g_propertyData", &g_propertyData);
|
|
|
|
|
|
2023-12-07 18:32:55 +02:00
|
|
|
QString blurPath = "file:" + EffectUtils::nodesSourcesPath() + "/common/";
|
|
|
|
|
g_propertyData.insert(QString("blur_vs_path"), QString(blurPath + "bluritems.vert.qsb"));
|
|
|
|
|
g_propertyData.insert(QString("blur_fs_path"), QString(blurPath + "bluritems.frag.qsb"));
|
|
|
|
|
|
2023-08-09 18:15:56 +03:00
|
|
|
auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend");
|
2023-08-11 12:53:28 +03:00
|
|
|
map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())},
|
|
|
|
|
{"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())},
|
2023-06-29 15:06:11 +03:00
|
|
|
{"rootView", QVariant::fromValue(this)}});
|
2023-11-13 17:13:13 +01:00
|
|
|
QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(
|
|
|
|
|
this, QmlDesigner::Constants::EVENT_NEWEFFECTMAKER_TIME);
|
2023-12-05 16:05:06 +02:00
|
|
|
|
2023-12-08 12:04:12 +02:00
|
|
|
connect(m_effectMakerModel.data(), &EffectMakerModel::nodesChanged, this, [this]() {
|
2023-12-05 16:05:06 +02:00
|
|
|
m_effectMakerNodesModel->updateCanBeAdded(m_effectMakerModel->uniformNames());
|
|
|
|
|
});
|
2023-12-08 12:04:12 +02:00
|
|
|
|
2023-12-08 14:58:04 +02:00
|
|
|
connect(m_effectMakerModel.data(), &EffectMakerModel::resourcesSaved,
|
2023-12-08 12:04:12 +02:00
|
|
|
this, [this](const QmlDesigner::TypeName &type, const Utils::FilePath &path) {
|
|
|
|
|
if (!m_importScan.timer) {
|
|
|
|
|
m_importScan.timer = new QTimer(this);
|
|
|
|
|
connect(m_importScan.timer, &QTimer::timeout,
|
|
|
|
|
this, &EffectMakerWidget::handleImportScanTimer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_importScan.timer->isActive() && !m_importScan.future.isFinished())
|
|
|
|
|
m_importScan.future.cancel();
|
|
|
|
|
|
|
|
|
|
m_importScan.counter = 0;
|
|
|
|
|
m_importScan.type = type;
|
|
|
|
|
m_importScan.path = path;
|
|
|
|
|
|
|
|
|
|
m_importScan.timer->start(100);
|
|
|
|
|
});
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
|
|
|
|
|
2023-08-25 16:27:01 +03:00
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
bool EffectMakerWidget::eventFilter(QObject *obj, QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(obj)
|
|
|
|
|
Q_UNUSED(event)
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EffectMakerWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(callback)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StudioQuickWidget *EffectMakerWidget::quickWidget() const
|
|
|
|
|
{
|
2023-08-09 18:15:56 +03:00
|
|
|
return m_quickWidget.data();
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPointer<EffectMakerModel> EffectMakerWidget::effectMakerModel() const
|
|
|
|
|
{
|
|
|
|
|
return m_effectMakerModel;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-11 12:53:28 +03:00
|
|
|
QPointer<EffectMakerNodesModel> EffectMakerWidget::effectMakerNodesModel() const
|
|
|
|
|
{
|
|
|
|
|
return m_effectMakerNodesModel;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 13:41:27 +03:00
|
|
|
void EffectMakerWidget::addEffectNode(const QString &nodeQenPath)
|
2023-08-15 15:40:46 +03:00
|
|
|
{
|
2023-08-17 13:41:27 +03:00
|
|
|
m_effectMakerModel->addNode(nodeQenPath);
|
2023-08-15 15:40:46 +03:00
|
|
|
}
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
void EffectMakerWidget::focusSection(int section)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(section)
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-17 13:19:00 +02:00
|
|
|
QRect EffectMakerWidget::screenRect() const
|
|
|
|
|
{
|
|
|
|
|
if (m_quickWidget && m_quickWidget->screen())
|
|
|
|
|
return m_quickWidget->screen()->availableGeometry();
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPoint EffectMakerWidget::globalPos(const QPoint &point) const
|
|
|
|
|
{
|
|
|
|
|
if (m_quickWidget)
|
|
|
|
|
return m_quickWidget->mapToGlobal(point);
|
|
|
|
|
return point;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-24 13:41:55 +03:00
|
|
|
QSize EffectMakerWidget::sizeHint() const
|
|
|
|
|
{
|
|
|
|
|
return {420, 420};
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
QString EffectMakerWidget::qmlSourcesPath()
|
|
|
|
|
{
|
|
|
|
|
#ifdef SHARE_QML_PATH
|
|
|
|
|
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
|
|
|
|
return QLatin1String(SHARE_QML_PATH) + "/effectMakerQmlSources";
|
|
|
|
|
#endif
|
|
|
|
|
return Core::ICore::resourcePath("qmldesigner/effectMakerQmlSources").toString();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 16:27:01 +03:00
|
|
|
void EffectMakerWidget::initView()
|
|
|
|
|
{
|
|
|
|
|
auto ctxObj = new EffectMakerContextObject(m_quickWidget->rootContext());
|
|
|
|
|
m_quickWidget->rootContext()->setContextObject(ctxObj);
|
|
|
|
|
|
|
|
|
|
m_backendModelNode.setup(m_effectMakerView->rootModelNode());
|
2023-10-30 12:15:05 +02:00
|
|
|
m_quickWidget->rootContext()->setContextProperty("anchorBackend", &m_backendAnchorBinding);
|
2023-08-25 16:27:01 +03:00
|
|
|
m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode);
|
2023-10-31 18:07:19 +02:00
|
|
|
m_quickWidget->rootContext()->setContextProperty("activeDragSuffix", "");
|
|
|
|
|
|
2023-11-02 18:09:06 +02:00
|
|
|
//TODO: Fix crash on macos
|
|
|
|
|
// m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails",
|
|
|
|
|
// new QmlDesigner::AssetImageProvider(
|
|
|
|
|
// QmlDesigner::QmlDesignerPlugin::imageCache()));
|
2023-08-25 16:27:01 +03:00
|
|
|
|
|
|
|
|
// init the first load of the QML UI elements
|
|
|
|
|
reloadQmlSource();
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-12 15:15:15 +02:00
|
|
|
void EffectMakerWidget::openComposition(const QString &path)
|
|
|
|
|
{
|
|
|
|
|
m_compositionPath = path;
|
|
|
|
|
|
|
|
|
|
if (effectMakerModel()->hasUnsavedChanges())
|
|
|
|
|
QMetaObject::invokeMethod(quickWidget()->rootObject(), "promptToSaveBeforeOpen");
|
|
|
|
|
else
|
|
|
|
|
doOpenComposition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EffectMakerWidget::doOpenComposition()
|
|
|
|
|
{
|
|
|
|
|
effectMakerModel()->openComposition(m_compositionPath);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-29 15:06:11 +03:00
|
|
|
void EffectMakerWidget::reloadQmlSource()
|
|
|
|
|
{
|
|
|
|
|
const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml";
|
|
|
|
|
QTC_ASSERT(QFileInfo::exists(effectMakerQmlPath), return);
|
2023-08-09 18:15:56 +03:00
|
|
|
m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath));
|
2023-06-29 15:06:11 +03:00
|
|
|
}
|
|
|
|
|
|
2023-12-08 12:04:12 +02:00
|
|
|
void EffectMakerWidget::handleImportScanTimer()
|
|
|
|
|
{
|
|
|
|
|
++m_importScan.counter;
|
|
|
|
|
|
|
|
|
|
if (m_importScan.counter == 1) {
|
|
|
|
|
// Rescan the effect import to update code model
|
|
|
|
|
auto modelManager = QmlJS::ModelManagerInterface::instance();
|
|
|
|
|
if (modelManager) {
|
|
|
|
|
QmlJS::PathsAndLanguages pathToScan;
|
|
|
|
|
pathToScan.maybeInsert(m_importScan.path);
|
|
|
|
|
m_importScan.future = ::Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan,
|
|
|
|
|
modelManager->workingCopy(),
|
|
|
|
|
pathToScan, modelManager, true, true, true);
|
|
|
|
|
}
|
|
|
|
|
} else if (m_importScan.counter < 100) {
|
|
|
|
|
// We have to wait a while to ensure qmljs detects new files and updates its
|
|
|
|
|
// internal model. Then we force amend on rewriter to trigger qmljs snapshot update.
|
|
|
|
|
if (m_importScan.future.isCanceled() || m_importScan.future.isFinished())
|
|
|
|
|
m_importScan.counter = 100; // skip the timeout step
|
|
|
|
|
} else if (m_importScan.counter == 100) {
|
|
|
|
|
// Scanning is taking too long, abort
|
|
|
|
|
m_importScan.future.cancel();
|
|
|
|
|
m_importScan.timer->stop();
|
|
|
|
|
m_importScan.counter = 0;
|
|
|
|
|
} else if (m_importScan.counter == 101) {
|
|
|
|
|
if (m_effectMakerView->model() && m_effectMakerView->model()->rewriterView()) {
|
|
|
|
|
QmlDesigner::QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
|
|
|
|
|
m_effectMakerView->model()->rewriterView()->forceAmend();
|
|
|
|
|
}
|
|
|
|
|
} else if (m_importScan.counter == 102) {
|
|
|
|
|
if (m_effectMakerView->model()) {
|
|
|
|
|
// If type is in use, we have to reset puppet to update 2D view
|
|
|
|
|
if (!m_effectMakerView->allModelNodesOfType(
|
|
|
|
|
m_effectMakerView->model()->metaInfo(m_importScan.type)).isEmpty()) {
|
|
|
|
|
m_effectMakerView->resetPuppet();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (m_importScan.counter >= 103) {
|
|
|
|
|
// Refresh property view by resetting selection if any selected node is of updated type
|
|
|
|
|
if (m_effectMakerView->model() && m_effectMakerView->hasSelectedModelNodes()) {
|
|
|
|
|
const auto nodes = m_effectMakerView->selectedModelNodes();
|
|
|
|
|
QmlDesigner::MetaInfoType metaType
|
|
|
|
|
= m_effectMakerView->model()->metaInfo(m_importScan.type).type();
|
|
|
|
|
bool match = false;
|
|
|
|
|
for (const QmlDesigner::ModelNode &node : nodes) {
|
|
|
|
|
if (node.metaInfo().type() == metaType) {
|
|
|
|
|
match = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (match) {
|
|
|
|
|
m_effectMakerView->clearSelectedModelNodes();
|
|
|
|
|
m_effectMakerView->setSelectedModelNodes(nodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_importScan.timer->stop();
|
|
|
|
|
m_importScan.counter = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-15 15:36:15 +03:00
|
|
|
} // namespace EffectMaker
|
|
|
|
|
|