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

This commit is contained in:
The Qt Project
2024-08-07 12:55:01 +00:00
603 changed files with 41253 additions and 6404 deletions

View File

@@ -289,6 +289,7 @@ public:
MainWindow *m_mainwindow = nullptr;
QTimer m_trimTimer;
QString m_prependAboutInformation;
QStringList m_aboutInformation;
Context m_highPrioAdditionalContexts;
Context m_lowPrioAdditionalContexts{Constants::C_GLOBAL};
@@ -1158,7 +1159,9 @@ void ICore::saveSettings(SaveSettingsReason reason)
*/
QStringList ICore::additionalAboutInformation()
{
return d->m_aboutInformation;
auto aboutInformation = d->m_aboutInformation;
aboutInformation.prepend(d->m_prependAboutInformation);
return aboutInformation;
}
/*!
@@ -1169,6 +1172,14 @@ void ICore::clearAboutInformation()
d->m_aboutInformation.clear();
}
/*!
\internal
*/
void ICore::setPrependAboutInformation(const QString &line)
{
d->m_prependAboutInformation = line;
}
/*!
\internal
*/

View File

@@ -142,6 +142,7 @@ public:
static Utils::FilePath pathRelativeToActiveProject(const Utils::FilePath &path);
static QStringList additionalAboutInformation();
static void clearAboutInformation();
static void setPrependAboutInformation(const QString &line);
static void appendAboutInformation(const QString &line);
static QString aboutInformationCompact();
static QString aboutInformationHtml();

View File

@@ -846,7 +846,8 @@ R"(
QString parentChanged{
R"(
onParentChanged: {
function setupParentLayer()
{
if (_oldParent && _oldParent !== parent) {
_oldParent.layer.enabled = false
_oldParent.layer.effect = null
@@ -860,26 +861,21 @@ R"(
if (visible) {
parent.layer.enabled = true
parent.layer.effect = effectComponent
%6
%4%1%5%3
} else {
parent.layer.enabled = false
parent.layer.effect = null
%8
%4%2
}
%6
%4%1%5%3
parent.update()
}
}
onVisibleChanged: {
if (visible) {
parent.layer.enabled = true
parent.layer.effect = effectComponent
%6
%4%1%5%3
} else {
parent.layer.enabled = false
parent.layer.effect = null
%8
%4%2
}
parent.update()
}
onParentChanged: setupParentLayer()
onVisibleChanged: setupParentLayer()
)"
};

View File

@@ -9,6 +9,7 @@
#include <designermcumanager.h>
#include <documentmanager.h>
#include <import.h>
#include <modelnodeoperations.h>
#include <qmlchangeset.h>
#include <qmldesignerconstants.h>
@@ -16,6 +17,8 @@
#include <coreplugin/icore.h>
#include <QTimer>
namespace EffectComposer {
EffectComposerView::EffectComposerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies)
@@ -106,8 +109,10 @@ QmlDesigner::WidgetInfo EffectComposerView::widgetInfo()
});
}
return createWidgetInfo(m_widget.data(), "EffectComposer",
QmlDesigner::WidgetInfo::LeftPane, 0, tr("Effect Composer [beta]"));
return createWidgetInfo(m_widget.data(),
"EffectComposer",
QmlDesigner::WidgetInfo::LeftPane,
tr("Effect Composer [beta]"));
}
void EffectComposerView::customNotification([[maybe_unused]] const AbstractView *view,
@@ -167,4 +172,48 @@ void EffectComposerView::selectedNodesChanged(const QList<QmlDesigner::ModelNode
m_widget->effectComposerModel()->setHasValidTarget(hasValidTarget);
}
void EffectComposerView::nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode)
{
QList<QmlDesigner::ModelNode> nodes = removedNode.allSubModelNodesAndThisNode();
bool effectRemoved = false;
for (const QmlDesigner::ModelNode &node : nodes) {
QmlDesigner::QmlItemNode qmlNode(node);
if (qmlNode.isEffectItem()) {
effectRemoved = true;
break;
}
}
if (effectRemoved)
QTimer::singleShot(0, this, &EffectComposerView::removeUnusedEffectImports);
}
void EffectComposerView::removeUnusedEffectImports()
{
QTC_ASSERT(model(), return);
const QString effectPrefix = m_componentUtils.composedEffectsTypePrefix();
const QmlDesigner::Imports &imports = model()->imports();
QHash<QString, QmlDesigner::Import> effectImports;
for (const QmlDesigner::Import &import : imports) {
if (import.url().startsWith(effectPrefix)) {
QString type = import.url().split('.').last();
effectImports.insert(type, import);
}
}
const QList<QmlDesigner::ModelNode> allNodes = allModelNodes();
for (const QmlDesigner::ModelNode &node : allNodes) {
if (QmlDesigner::QmlItemNode(node).isEffectItem())
effectImports.remove(node.simplifiedTypeName());
}
if (!effectImports.isEmpty()) {
QmlDesigner::Imports removeImports;
for (const QmlDesigner::Import &import : effectImports)
removeImports.append(import);
model()->changeImports({}, removeImports);
}
}
} // namespace EffectComposer

View File

@@ -29,10 +29,12 @@ public:
void modelAboutToBeDetached(QmlDesigner::Model *model) override;
void selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList,
const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override;
void nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode) override;
private:
void customNotification(const AbstractView *view, const QString &identifier,
const QList<QmlDesigner::ModelNode> &nodeList, const QList<QVariant> &data) override;
void removeUnusedEffectImports();
QPointer<EffectComposerWidget> m_widget;
QString m_currProjectPath;

View File

@@ -45,7 +45,6 @@ WidgetInfo InsightView::widgetInfo()
return createWidgetInfo(m_insightWidget.data(),
"QtInsight",
WidgetInfo::RightPane,
0,
tr("Qt Insight"));
}

View File

@@ -8,13 +8,13 @@ if (APPLE)
set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/QmlDesigner")
endif()
set(BUILD_NOT_DESIGNSTUDIO NOT ${BUILD_NOT_DESIGNSTUDIO})
option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDIO})
add_feature_info("Qml Designer Lite" ${QTC_USE_QML_DESIGNER_LITE} "")
option(USE_PROJECTSTORAGE "Use ProjectStorage" ${QTC_USE_QML_DESIGNER_LITE})
env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF)
option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tracing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING})
add_feature_info("Sqlite tracing" ${ENABLE_PROJECT_STORAGE_TRACING} "")
@@ -34,9 +34,10 @@ add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "")
add_qtc_library(QmlDesignerUtils STATIC
DEPENDS
Qt::Gui Utils Qt::QmlPrivate
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils
PUBLIC_COMPILE_OPTIONS
$<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-unneeded-internal-declaration>
SOURCES
asset.cpp asset.h
designeralgorithm.h
@@ -47,16 +48,14 @@ add_qtc_library(QmlDesignerUtils STATIC
ktximage.cpp ktximage.h
imageutils.cpp imageutils.h
qmldesignerutils_global.h
version.cpp version.h
)
if (TARGET QmlDesignerUtils)
target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>)
target_compile_options(QmlDesignerUtils PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-Wno-unneeded-internal-declaration>)
endif()
extend_qtc_library(QmlDesignerUtils
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON
PUBLIC_COMPILE_OPTIONS
$<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>
)
add_qtc_library(QmlDesignerCore STATIC
@@ -95,10 +94,17 @@ add_qtc_library(QmlDesignerCore STATIC
PUBLIC_INCLUDES
${CMAKE_CURRENT_LIST_DIR}/designercore
${CMAKE_CURRENT_LIST_DIR}/designercore/include
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore
)
extend_qtc_library(QmlDesignerCore
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/designercoreutils
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/designercoreutils
SOURCES
rewritertransaction.cpp rewritertransaction.h
generatedcomponentutils.cpp generatedcomponentutils.h
modelmerger.cpp modelmerger.h
modelutils.cpp modelutils.h
skipiterator.h
stylesheetmerger.cpp stylesheetmerger.h
uniquename.cpp uniquename.h
)
@@ -117,10 +123,11 @@ extend_qtc_library(QmlDesignerCore
extend_qtc_library(QmlDesignerCore
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON
PUBLIC_COMPILE_OPTIONS $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>
)
extend_qtc_library(QmlDesignerCore
CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.5.0 AND Qt6_VERSION VERSION_LESS 6.6.0
CONDITION IS_SUPPORTED_PROJECTSTORAGE_QT
PUBLIC_DEFINES QDS_BUILD_QMLPARSER
)
extend_qtc_library(QmlDesignerCore
@@ -279,9 +286,7 @@ extend_qtc_library(QmlDesignerCore
invalidslideindexexception.h
mathutils.h
modelfwd.h
modelmerger.h
modelnode.h
modelnodepositionstorage.h
module.h
nodeabstractproperty.h
nodeinstance.h
@@ -305,9 +310,9 @@ extend_qtc_library(QmlDesignerCore
qmltimelinekeyframegroup.h
removebasestateexception.h
rewritingexception.h
rewritertransaction.h
signalhandlerproperty.h
stringutils.h
stylesheetmerger.h
synchronousimagecache.h
variantproperty.h
)
@@ -362,8 +367,8 @@ extend_qtc_library(QmlDesignerCore
abstractview.cpp
anchorline.cpp
annotation.cpp
auxiliarypropertystorageview.cpp auxiliarypropertystorageview.h
bindingproperty.cpp
componenttextmodifier.cpp
documentmessage.cpp
import.cpp
internalbindingproperty.cpp
@@ -384,25 +389,46 @@ extend_qtc_library(QmlDesignerCore
internalvariantproperty.h
model.cpp
model_p.h
modelmerger.cpp
modelnode.cpp
modelnodepositionrecalculator.cpp
modelnodepositionrecalculator.h
modelnodepositionstorage.cpp
modelresourcemanagementinterface.h
modelresourcemanagementfwd.h
modelresourcemanagement.cpp modelresourcemanagement.h
modeltotextmerger.cpp
modeltotextmerger.h
modelutils.cpp
modelutils.h
nodeabstractproperty.cpp
nodelistproperty.cpp
nodeproperty.cpp
signalhandlerproperty.cpp
variantproperty.cpp
)
extend_qtc_library(QmlDesignerCore
INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/rewriter
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/rewriter
SOURCES
componenttextmodifier.cpp
modelnodepositionrecalculator.cpp
modelnodepositionrecalculator.h
modelnodepositionstorage.cpp
modelnodepositionstorage.h
modeltotextmerger.cpp
modeltotextmerger.h
plaintexteditmodifier.cpp
propertycontainer.cpp
propertynode.cpp
propertyparser.cpp
rewriteaction.cpp
rewriteaction.h
rewriteactioncompressor.cpp
rewriteactioncompressor.h
rewritertransaction.cpp
rewriterview.cpp
textmodifier.cpp
texttomodelmerger.cpp
texttomodelmerger.h
)
extend_qtc_library(QmlDesignerCore
INCLUDES ${CMAKE_CURRENT_LIST_DIR}/designercore/qmltools
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/qmltools
SOURCES
qml3dnode.cpp
qmlanchors.cpp
qmlchangeset.cpp
@@ -416,18 +442,6 @@ extend_qtc_library(QmlDesignerCore
qmltimeline.cpp
qmltimelinekeyframegroup.cpp
qmlvisualnode.cpp
rewriteaction.cpp
rewriteaction.h
rewriteactioncompressor.cpp
rewriteactioncompressor.h
rewriterview.cpp
signalhandlerproperty.cpp
skipiterator.h
stylesheetmerger.cpp
textmodifier.cpp
texttomodelmerger.cpp
texttomodelmerger.h
variantproperty.cpp
)
extend_qtc_library(QmlDesignerCore
@@ -444,6 +458,7 @@ extend_qtc_library(QmlDesignerCore
SOURCES_PREFIX designercore/projectstorage
PUBLIC_INCLUDES designercore/projectstorage
SOURCES_PROPERTIES SKIP_AUTOGEN ON
DEFINES QML_DOM_MSVC2019_COMPAT # can be removed for Qt 6.8
SOURCES
commontypecache.h
directorypathcompressor.h
@@ -493,7 +508,7 @@ add_qtc_plugin(QmlDesigner
PLUGIN_MANUAL_DEPENDS LicenseChecker ${IDE_VERSION} optional
DEPENDS
QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg QmlDesignerCore Sqlite
Qt::QuickWidgets Qt::CorePrivate Qt::Xml Qt::Svg QmlDesignerCore Sqlite Zip
PUBLIC_DEPENDS
QmlDesignerUtils QmlPuppetCommunication QmlDesignerBase
DEFINES
@@ -516,10 +531,12 @@ add_qtc_plugin(QmlDesigner
${CMAKE_CURRENT_LIST_DIR}/components/propertyeditor
${CMAKE_CURRENT_LIST_DIR}/components/stateseditor
${CMAKE_CURRENT_LIST_DIR}/components/texteditor
${CMAKE_CURRENT_LIST_DIR}/components/designsystem
PUBLIC_INCLUDES
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/designercore #can not be a public dependency -> EXCLUDE_FROM_INSTALL in QmlDesignerCore
${CMAKE_CURRENT_LIST_DIR}/designercore/include #iwidgetplugin.h is used by other plugins
${CMAKE_CURRENT_LIST_DIR}/designercore/designercoreutils
SOURCES
designmodewidget.cpp designmodewidget.h
documentmanager.cpp documentmanager.h
@@ -550,6 +567,7 @@ add_qtc_plugin(QmlDesigner
extend_qtc_plugin(QmlDesigner
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON
PUBLIC_COMPILE_OPTIONS $<$<COMPILE_LANG_AND_ID:CXX,Clang,GNU>:-Wno-error=maybe-uninitialized>
)
function(get_and_add_as_subdirectory name repository git_tag build_dir source_dir source_subdir)
@@ -659,9 +677,11 @@ extend_qtc_plugin(QmlDesigner
edit3dwidget.cpp edit3dwidget.h
edit3dcanvas.cpp edit3dcanvas.h
edit3dactions.cpp edit3dactions.h
edit3dmaterialsaction.cpp edit3dmaterialsaction.h
edit3dtoolbarmenu.cpp edit3dtoolbarmenu.h
backgroundcolorselection.cpp backgroundcolorselection.h
bakelights.cpp bakelights.h
indicatoractionwidget.cpp indicatoractionwidget.h
snapconfiguration.cpp snapconfiguration.h
cameraspeedconfiguration.cpp cameraspeedconfiguration.h
bakelightsdatamodel.cpp bakelightsdatamodel.h
@@ -813,10 +833,12 @@ extend_qtc_plugin(QmlDesigner
propertyeditorvalue.cpp propertyeditorvalue.h
propertyeditorview.cpp propertyeditorview.h
propertyeditorwidget.cpp propertyeditorwidget.h
propertynamevalidator.cpp propertynamevalidator.h
tooltip.cpp tooltip.h
qmlanchorbindingproxy.cpp qmlanchorbindingproxy.h
qmlmodelnodeproxy.cpp qmlmodelnodeproxy.h
quick2propertyeditorview.cpp quick2propertyeditorview.h
propertyeditorutils.cpp propertyeditorutils.h
)
extend_qtc_plugin(QmlDesigner
@@ -836,6 +858,9 @@ extend_qtc_plugin(QmlDesigner
contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h
contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h
contentlibraryusermodel.cpp contentlibraryusermodel.h
usercategory.cpp usercategory.h
useritemcategory.cpp useritemcategory.h
usertexturecategory.cpp usertexturecategory.h
)
extend_qtc_plugin(QmlDesigner
@@ -843,6 +868,7 @@ extend_qtc_plugin(QmlDesigner
SOURCES
materialeditorcontextobject.cpp materialeditorcontextobject.h
materialeditordynamicpropertiesproxymodel.cpp materialeditordynamicpropertiesproxymodel.h
materialeditorimageprovider.cpp materialeditorimageprovider.h
materialeditorqmlbackend.cpp materialeditorqmlbackend.h
materialeditortransaction.cpp materialeditortransaction.h
materialeditorview.cpp materialeditorview.h
@@ -942,7 +968,7 @@ extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX designercore
PUBLIC_INCLUDES designercore
SOURCES
model/basetexteditmodifier.cpp
rewriter/basetexteditmodifier.cpp
)
extend_qtc_plugin(QmlDesigner
@@ -1149,6 +1175,14 @@ extend_qtc_plugin(QmlDesigner
toolbarbackend.h
)
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/designsystem
SOURCES
dsconstants.h
dsthememanager.h dsthememanager.cpp
dsthemegroup.cpp dsthemegroup.h
)
add_qtc_plugin(assetexporterplugin
PLUGIN_CLASS AssetExporterPlugin
CONDITION TARGET QmlDesigner

View File

@@ -6,7 +6,7 @@
#include "exportnotification.h"
#include <designdocument.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <nodemetainfo.h>
#include <qmldesignerplugin.h>
#include <qmlitemnode.h>

View File

@@ -63,7 +63,7 @@ WidgetInfo AssetsLibraryView::widgetInfo()
this};
}
return createWidgetInfo(m_widget.data(), "Assets", WidgetInfo::LeftPane, 0, tr("Assets"));
return createWidgetInfo(m_widget.data(), "Assets", WidgetInfo::LeftPane, tr("Assets"));
}
void AssetsLibraryView::customNotification(const AbstractView * /*view*/,

View File

@@ -413,9 +413,13 @@ void AssetsLibraryWidget::handleExtFilesDrop(const QList<QUrl> &simpleFilePaths,
targetDirPath,
isDropOnRoot);
if (result.status() == AddFilesResult::Failed) {
Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project.")
.arg(simpleFilePathStrings.join(' ')));
QWidget *w = Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project.")
.arg(simpleFilePathStrings.join(' ')));
// Avoid multiple modal dialogs open at the same time
auto mb = qobject_cast<QMessageBox *>(w);
if (mb && !complexFilePathStrings.empty())
mb->exec();
}
}
}
@@ -609,6 +613,9 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog
categoryFileNames.insert(category, fileName);
}
QStringList unsupportedFiles;
QStringList failedOpsFiles;
for (const QString &category : categoryFileNames.uniqueKeys()) {
QStringList fileNames = categoryFileNames.values(category);
AddResourceOperation operation = categoryToOperation.value(category);
@@ -617,9 +624,7 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog
AddFilesResult result = operation(fileNames,
document->fileName().parentDir().toString(), showDialog);
if (result.status() == AddFilesResult::Failed) {
Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project.")
.arg(fileNames.join(' ')));
failedOpsFiles.append(fileNames);
} else {
if (!result.directory().isEmpty()) {
emit directoryCreated(result.directory());
@@ -634,11 +639,24 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog
}
}
} else {
Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project. Unsupported file format.")
.arg(fileNames.join(' ')));
unsupportedFiles.append(fileNames);
}
}
if (!failedOpsFiles.isEmpty()) {
QWidget *w = Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project.")
.arg(failedOpsFiles.join(' ')));
// Avoid multiple modal dialogs open at the same time
auto mb = qobject_cast<QMessageBox *>(w);
if (mb && !unsupportedFiles.isEmpty())
mb->exec();
}
if (!unsupportedFiles.isEmpty()) {
Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"),
tr("Could not add %1 to project. Unsupported file format.")
.arg(unsupportedFiles.join(' ')));
}
}
void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths)

View File

@@ -9,197 +9,284 @@ namespace QmlDesigner {
namespace ComponentCoreConstants {
const char rootCategory[] = "";
inline constexpr char rootCategory[] = "";
const char selectionCategory[] = "Selection";
const char connectionsCategory[] = "Connections";
const char arrangeCategory[] = "Arrange";
const char qmlPreviewCategory[] = "QmlPreview";
const char editCategory[] = "Edit";
const char anchorsCategory[] = "Anchors";
const char positionerCategory[] = "Position";
const char groupCategory[] = "Group";
const char snappingCategory[] = "Snapping";
const char layoutCategory[] = "Layout";
const char flowCategory[] = "Flow";
const char flowEffectCategory[] = "FlowEffect";
const char flowConnectionCategory[] = "FlowConnection";
const char stackedContainerCategory[] = "StackedContainer";
const char genericToolBarCategory[] = "GenericToolBar";
const char eventListCategory[] = "QmlEventList";
inline constexpr char selectionCategory[] = "Selection";
inline constexpr char connectionsCategory[] = "Connections";
inline constexpr char arrangeCategory[] = "Arrange";
inline constexpr char qmlPreviewCategory[] = "QmlPreview";
inline constexpr char editCategory[] = "Edit";
inline constexpr char anchorsCategory[] = "Anchors";
inline constexpr char positionerCategory[] = "Position";
inline constexpr char groupCategory[] = "Group";
inline constexpr char snappingCategory[] = "Snapping";
inline constexpr char layoutCategory[] = "Layout";
inline constexpr char flowCategory[] = "Flow";
inline constexpr char flowEffectCategory[] = "FlowEffect";
inline constexpr char flowConnectionCategory[] = "FlowConnection";
inline constexpr char stackedContainerCategory[] = "StackedContainer";
inline constexpr char genericToolBarCategory[] = "GenericToolBar";
inline constexpr char eventListCategory[] = "QmlEventList";
const char toFrontCommandId[] = "ToFront";
const char toBackCommandId[] = "ToBack";
const char raiseCommandId[] = "Raise";
const char lowerCommandId[] = "Lower";
const char resetZCommandId[] = "ResetZ";
const char reverseCommandId[] = "Reverse";
const char resetSizeCommandId[] = "ResetSize";
const char resetPositionCommandId[] = "ResetPosition";
const char copyFormatCommandId[] = "CopyFormat";
const char applyFormatCommandId[] = "ApplyFormat";
const char visiblityCommandId[] = "ToggleVisiblity";
const char anchorsFillCommandId[] = "AnchorsFill";
const char anchorsResetCommandId[] = "AnchorsReset";
inline constexpr char toFrontCommandId[] = "ToFront";
inline constexpr char toBackCommandId[] = "ToBack";
inline constexpr char raiseCommandId[] = "Raise";
inline constexpr char lowerCommandId[] = "Lower";
inline constexpr char resetZCommandId[] = "ResetZ";
inline constexpr char reverseCommandId[] = "Reverse";
inline constexpr char resetSizeCommandId[] = "ResetSize";
inline constexpr char resetPositionCommandId[] = "ResetPosition";
inline constexpr char copyFormatCommandId[] = "CopyFormat";
inline constexpr char applyFormatCommandId[] = "ApplyFormat";
inline constexpr char visiblityCommandId[] = "ToggleVisiblity";
inline constexpr char anchorsFillCommandId[] = "AnchorsFill";
inline constexpr char anchorsResetCommandId[] = "AnchorsReset";
const char anchorParentTopAndBottomCommandId[] = "AnchorParentTopAndBottom";
const char anchorParentLeftAndRightCommandId[] = "AnchorParentLeftAndRight";
const char anchorParentTopCommandId[] = "AnchorParentTop";
const char anchorParentRightCommandId[] = "AnchorParentRight";
const char anchorParentBottomCommandId[] = "AnchorParentBottom";
const char anchorParentLeftCommandId[] = "AnchorParentLeft";
inline constexpr char anchorParentTopAndBottomCommandId[] = "AnchorParentTopAndBottom";
inline constexpr char anchorParentLeftAndRightCommandId[] = "AnchorParentLeftAndRight";
inline constexpr char anchorParentTopCommandId[] = "AnchorParentTop";
inline constexpr char anchorParentRightCommandId[] = "AnchorParentRight";
inline constexpr char anchorParentBottomCommandId[] = "AnchorParentBottom";
inline constexpr char anchorParentLeftCommandId[] = "AnchorParentLeft";
const char removePositionerCommandId[] = "RemovePositioner";
const char createFlowActionAreaCommandId[] = "CreateFlowActionArea";
const char setFlowStartCommandId[] = "SetFlowStart";
const char selectFlowEffectCommandId[] = "SelectFlowEffect";
const char layoutRowPositionerCommandId[] = "LayoutRowPositioner";
const char layoutColumnPositionerCommandId[] = "LayoutColumnPositioner";
const char layoutGridPositionerCommandId[] = "LayoutGridPositioner";
const char layoutFlowPositionerCommandId[] = "LayoutFlowPositioner";
const char removeLayoutCommandId[] = "RemoveLayout";
const char layoutRowLayoutCommandId[] = "LayoutRowLayout";
const char layoutColumnLayoutCommandId[] = "LayoutColumnLayout";
const char layoutGridLayoutCommandId[] = "LayoutGridLayout";
const char layoutFillWidthCommandId[] = "LayoutFillWidth";
const char layoutFillHeightCommandId[] = "LayoutFillHeight";
const char goIntoComponentCommandId[] = "GoIntoComponent";
const char jumpToCodeCommandId[] = "JumpToCode";
const char mergeTemplateCommandId[] = "MergeTemplate";
const char goToImplementationCommandId[] = "GoToImplementation";
const char makeComponentCommandId[] = "MakeComponent";
const char editMaterialCommandId[] = "EditMaterial";
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedContainer";
const char flowAssignEffectCommandId[] = "AssignFlowEffect";
const char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect";
const char addToGroupItemCommandId[] = "AddToGroupItem";
const char fitRootToScreenCommandId[] = "FitRootToScreen";
const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen";
const char editAnnotationsCommandId[] = "EditAnnotation";
const char addMouseAreaFillCommandId[] = "AddMouseAreaFill";
const char editIn3dViewCommandId[] = "editIn3dView";
inline constexpr char removePositionerCommandId[] = "RemovePositioner";
inline constexpr char createFlowActionAreaCommandId[] = "CreateFlowActionArea";
inline constexpr char setFlowStartCommandId[] = "SetFlowStart";
inline constexpr char selectFlowEffectCommandId[] = "SelectFlowEffect";
inline constexpr char layoutRowPositionerCommandId[] = "LayoutRowPositioner";
inline constexpr char layoutColumnPositionerCommandId[] = "LayoutColumnPositioner";
inline constexpr char layoutGridPositionerCommandId[] = "LayoutGridPositioner";
inline constexpr char layoutFlowPositionerCommandId[] = "LayoutFlowPositioner";
inline constexpr char removeLayoutCommandId[] = "RemoveLayout";
inline constexpr char layoutRowLayoutCommandId[] = "LayoutRowLayout";
inline constexpr char layoutColumnLayoutCommandId[] = "LayoutColumnLayout";
inline constexpr char layoutGridLayoutCommandId[] = "LayoutGridLayout";
inline constexpr char layoutFillWidthCommandId[] = "LayoutFillWidth";
inline constexpr char layoutFillHeightCommandId[] = "LayoutFillHeight";
inline constexpr char goIntoComponentCommandId[] = "GoIntoComponent";
inline constexpr char jumpToCodeCommandId[] = "JumpToCode";
inline constexpr char mergeTemplateCommandId[] = "MergeTemplate";
inline constexpr char goToImplementationCommandId[] = "GoToImplementation";
inline constexpr char makeComponentCommandId[] = "MakeComponent";
inline constexpr char importComponentCommandId[] = "ImportComponent";
inline constexpr char exportComponentCommandId[] = "ExportComponent";
inline constexpr char editMaterialCommandId[] = "EditMaterial";
inline constexpr char addToContentLibraryCommandId[] = "AddToContentLibrary";
inline constexpr char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
inline constexpr char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
inline constexpr char increaseIndexOfStackedContainerCommandId[]
= "IncreaseIndexOfStackedContainer";
inline constexpr char decreaseIndexOfStackedContainerCommandId[]
= "DecreaseIndexOfStackedContainer";
inline constexpr char flowAssignEffectCommandId[] = "AssignFlowEffect";
inline constexpr char flowAssignCustomEffectCommandId[] = "AssignFlowCustomEffect";
inline constexpr char addToGroupItemCommandId[] = "AddToGroupItem";
inline constexpr char fitRootToScreenCommandId[] = "FitRootToScreen";
inline constexpr char fitSelectionToScreenCommandId[] = "FitSelectionToScreen";
inline constexpr char editAnnotationsCommandId[] = "EditAnnotation";
inline constexpr char addMouseAreaFillCommandId[] = "AddMouseAreaFill";
inline constexpr char editIn3dViewCommandId[] = "editIn3dView";
const char openSignalDialogCommandId[] = "OpenSignalDialog";
const char update3DAssetCommandId[] = "Update3DAsset";
inline constexpr char openSignalDialogCommandId[] = "OpenSignalDialog";
inline constexpr 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");
const char editCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit");
const char anchorsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Anchors");
const char positionerCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Positioner");
const char groupCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Group");
const char snappingCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Snapping");
const char layoutCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout");
const char flowCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow");
const char flowEffectCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow Effects");
const char stackedContainerCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stacked Container");
inline constexpr char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Selection");
inline constexpr char connectionsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Connections");
inline constexpr char flowConnectionCategoryDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Connect");
inline constexpr char selectEffectDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Select Effect");
inline constexpr char arrangeCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Arrange");
inline constexpr char editCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit");
inline constexpr char anchorsCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Anchors");
inline constexpr char positionerCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Positioner");
inline constexpr char groupCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Group");
inline constexpr char snappingCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Snapping");
inline constexpr char layoutCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Layout");
inline constexpr char flowCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow");
inline constexpr char flowEffectCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Flow Effects");
inline constexpr char stackedContainerCategoryDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Stacked Container");
const char cutSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Cut");
const char copySelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Copy");
const char pasteSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Paste");
const char deleteSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Delete Selection");
inline constexpr char cutSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Cut");
inline constexpr char copySelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Copy");
inline constexpr char pasteSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Paste");
inline constexpr char deleteSelectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Delete Selection");
const char toFrontDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Bring to Front");
const char toBackDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Send to Back");
inline constexpr char toFrontDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Bring to Front");
inline constexpr char toBackDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Send to Back");
const char raiseDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Bring Forward");
const char lowerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Send Backward");
inline constexpr char raiseDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Bring Forward");
inline constexpr char lowerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Send Backward");
const char undoDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Undo");
const char redoDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Redo");
inline constexpr char undoDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Undo");
inline constexpr char redoDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Redo");
const char visibilityDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Visibility");
inline constexpr char visibilityDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Visibility");
const char resetSizeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset Size");
const char resetPositionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset Position");
const char copyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Copy Formatting");
const char applyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Apply Formatting");
inline constexpr char resetSizeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Reset Size");
inline constexpr char resetPositionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Reset Position");
inline constexpr char copyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Copy Formatting");
inline constexpr char applyFormatDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Apply Formatting");
const char enterComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Component");
const char JumpToCodeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Jump to the Code");
const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Merge with Template");
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component");
const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material");
const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations");
const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area");
const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View");
inline constexpr char enterComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Edit Component");
inline constexpr char JumpToCodeDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Jump to the Code");
inline constexpr char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Merge with Template");
inline constexpr char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Go to Implementation");
inline constexpr char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Create Component");
inline constexpr char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Edit Material");
inline constexpr char addToContentLibraryDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Add to Content Library");
inline constexpr char importComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Import Component");
inline constexpr char exportComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Export Component");
inline constexpr char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Edit Annotations");
inline constexpr char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Add Mouse Area");
inline constexpr char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Edit in 3D View");
const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog");
const char update3DAssetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Update 3D Asset");
inline constexpr char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Open Signal Dialog");
inline constexpr char update3DAssetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Update 3D Asset");
const char setIdDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Id");
inline constexpr char setIdDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Id");
const char resetZDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset z Property");
inline constexpr char resetZDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Reset z Property");
const char reverseDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reverse");
inline constexpr char reverseDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reverse");
const char anchorsFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill Parent");
const char anchorsResetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "No Anchors");
inline constexpr char anchorsFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Fill Parent");
inline constexpr char anchorsResetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"No Anchors");
const char anchorParentTopAndBottomDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Top And Bottom");
const char anchorParentLeftAndRightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Left And Right");
const char anchorParentTopDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Top");
const char anchorParentRightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Right");
const char anchorParentBottomDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Bottom");
const char anchorParentLeftDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Left");
inline constexpr char anchorParentTopAndBottomDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Top And Bottom");
inline constexpr char anchorParentLeftAndRightDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Left And Right");
inline constexpr char anchorParentTopDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Top");
inline constexpr char anchorParentRightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Right");
inline constexpr char anchorParentBottomDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Bottom");
inline constexpr char anchorParentLeftDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Left");
const char layoutColumnPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Column Positioner");
const char layoutRowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Row Positioner");
const char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Grid Positioner");
const char layoutFlowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Flow Positioner");
const char removePositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Positioner");
const char createFlowActionAreaDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Flow Action");
const char setFlowStartDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Set Flow Start");
const char removeLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Remove Layout");
inline constexpr char layoutColumnPositionerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Column Positioner");
inline constexpr char layoutRowPositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Row Positioner");
inline constexpr char layoutGridPositionerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Grid Positioner");
inline constexpr char layoutFlowPositionerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Flow Positioner");
inline constexpr char removePositionerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Remove Positioner");
inline constexpr char createFlowActionAreaDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Create Flow Action");
inline constexpr char setFlowStartDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Set Flow Start");
inline constexpr char removeLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Remove Layout");
const char addToGroupItemDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Group in GroupItem");
const char removeGroupItemDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Remove GroupItem");
inline constexpr char addToGroupItemDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Group in GroupItem");
inline constexpr char removeGroupItemDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Remove GroupItem");
const char addItemToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Component");
const char addTabBarToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Tab Bar");
const char increaseIndexToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Increase Index");
const char decreaseIndexToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Decrease Index");
inline constexpr char addItemToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Add Component");
inline constexpr char addTabBarToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Add Tab Bar");
inline constexpr char increaseIndexToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Increase Index");
inline constexpr char decreaseIndexToStackedContainerDisplayName[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Decrease Index");
const char layoutColumnLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Column Layout");
const char layoutRowLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Row Layout");
const char layoutGridLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Grid Layout");
inline constexpr char layoutColumnLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Column Layout");
inline constexpr char layoutRowLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Row Layout");
inline constexpr char layoutGridLayoutDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Grid Layout");
const char layoutFillWidthDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill Width");
const char layoutFillHeightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill Height");
inline constexpr char layoutFillWidthDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Fill Width");
inline constexpr char layoutFillHeightDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Fill Height");
const char flowAssignEffectDisplayName[] = "Assign FlowEffect ";
const char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect ";
inline constexpr char flowAssignEffectDisplayName[] = "Assign FlowEffect ";
inline constexpr char flowAssignCustomEffectDisplayName[] = "Assign Custom FlowEffect ";
const char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Raise selected component.");
const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower selected component.");
inline constexpr char raiseToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Raise selected component.");
inline constexpr char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Lower selected component.");
const char resetSizeToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset size and use implicit size.");
const char resetPositionTooltip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset position and use implicit position.");
const char copyFormatTooltip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Copy formatting.");
const char applyFormatTooltip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Apply formatting.");
inline constexpr char resetSizeToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Reset size and use implicit size.");
inline constexpr char resetPositionTooltip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Reset position and use implicit position.");
inline constexpr char copyFormatTooltip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Copy formatting.");
inline constexpr char applyFormatTooltip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Apply formatting.");
const char anchorsFillToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill selected component to parent.");
const char anchorsResetToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset anchors for selected component.");
inline constexpr char anchorsFillToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Fill selected component to parent.");
inline constexpr char anchorsResetToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Reset anchors for selected component.");
const char layoutColumnLayoutToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout selected components in column layout.");
const char layoutRowLayoutToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout selected components in row layout.");
const char layoutGridLayoutToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Layout selected components in grid layout.");
inline constexpr char layoutColumnLayoutToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Layout selected components in column layout.");
inline constexpr char layoutRowLayoutToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Layout selected components in row layout.");
inline constexpr char layoutGridLayoutToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Layout selected components in grid layout.");
const char increaseIndexOfStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Increase index of stacked container.");
const char decreaseIndexOfStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Decrease index of stacked container.");
const char addItemToStackedContainerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add component to stacked container.");
const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add flow action.");
inline constexpr char increaseIndexOfStackedContainerToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Increase index of stacked container.");
inline constexpr char decreaseIndexOfStackedContainerToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Decrease index of stacked container.");
inline constexpr char addItemToStackedContainerToolTip[] = QT_TRANSLATE_NOOP(
"QmlDesignerContextMenu", "Add component to stacked container.");
inline constexpr char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Add flow action.");
const char editListModelDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Edit List Model...");
inline constexpr char editListModelDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu",
"Edit List Model...");
namespace Priorities {
enum PrioritiesEnum : int {
Top = 0,
@@ -241,20 +328,31 @@ enum PrioritiesEnum : int {
SignalsDialog,
Refactoring,
GenericToolBar,
Last
Last,
/******** Section *****************************/
AddingAssetsSection = 7000,
Add3DToContentLib,
ImportComponent,
ExportComponent,
};
};
const char addImagesDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Image Files");
const char addFontsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Font Files");
const char addSoundsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Sound Files");
const char addVideosDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Video Files");
const char addShadersDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Shader Files");
const char add3DAssetsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "3D Assets");
const char addQt3DSPresentationsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Qt 3D Studio Presentations");
const char addCustomEffectDialogDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Effect Composer Files");
inline constexpr char addImagesDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Image Files");
inline constexpr char addFontsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Font Files");
inline constexpr char addSoundsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Sound Files");
inline constexpr char addVideosDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Video Files");
inline constexpr char addShadersDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"Shader Files");
inline constexpr char add3DAssetsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources",
"3D Assets");
inline constexpr char addQt3DSPresentationsDisplayString[] = QT_TRANSLATE_NOOP(
"QmlDesignerAddResources", "Qt 3D Studio Presentations");
inline constexpr char addCustomEffectDialogDisplayString[] = QT_TRANSLATE_NOOP(
"QmlDesignerAddResources", "Effect Composer Files");
} //ComponentCoreConstants

View File

@@ -27,7 +27,7 @@
#include <actioneditor.h>
#include <documentmanager.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <viewmanager.h>
#include <qmldesignerplugin.h>
@@ -733,7 +733,7 @@ public:
signalHandler.view()
->emitCustomNotification(EditConnectionNotification,
{signalHandler.parentModelNode()},
{signalHandler.name()});
{signalHandler.name().toByteArray()});
//ActionEditor::invokeEditor(signalHandler, removeSignal);
});
@@ -1978,6 +1978,26 @@ void DesignerActionManager::createDefaultDesignerActions()
&singleSelection,
&singleSelection));
addDesignerAction(new ModelNodeContextMenuAction(
importComponentCommandId,
importComponentDisplayName,
contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
rootCategory,
QKeySequence(),
Priorities::ImportComponent,
&importComponent));
addDesignerAction(new ModelNodeContextMenuAction(
exportComponentCommandId,
exportComponentDisplayName,
contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
rootCategory,
QKeySequence(),
Priorities::ExportComponent,
&exportComponent,
&is3DNode,
&is3DNode));
addDesignerAction(new ModelNodeContextMenuAction(
editMaterialCommandId,
editMaterialDisplayName,
@@ -1999,6 +2019,17 @@ void DesignerActionManager::createDefaultDesignerActions()
[&] (const SelectionContext& context) { mergeWithTemplate(context, m_externalDependencies); },
&SelectionContextFunctors::always));
addDesignerAction(new ModelNodeContextMenuAction(
addToContentLibraryCommandId,
addToContentLibraryDisplayName,
contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
rootCategory,
QKeySequence(),
Priorities::Add3DToContentLib,
&add3DAssetToContentLibrary,
&enableAddToContentLib,
&enableAddToContentLib));
addDesignerAction(new ActionGroup(
"",
genericToolBarCategory,

View File

@@ -3,10 +3,11 @@
#include "groupitemaction.h"
#include "designermcumanager.h"
#include "nodeabstractproperty.h"
#include "nodelistproperty.h"
#include <model/modelutils.h>
#include <modelutils.h>
#include <utils/algorithm.h>
using namespace QmlDesigner;
@@ -81,6 +82,10 @@ inline bool itemsAreGrouped(const SelectionContext &selection)
bool groupingEnabled(const SelectionContext &selection)
{
//StudioComponents.GroupItem is not available in Qt for MCUs
if (DesignerMcuManager::instance().isMCUProject())
return false;
if (selection.singleNodeIsSelected())
return itemsAreGrouped(selection);
else

View File

@@ -4,7 +4,7 @@
#include "modelnodecontextmenu_helper.h"
#include <bindingproperty.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <modelnode.h>
#include <nodemetainfo.h>
#include <nodeproperty.h>

View File

@@ -70,6 +70,23 @@ inline bool isModelOrMaterial(const SelectionContext &selectionState)
return node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DMaterial();
}
inline bool enableAddToContentLib(const SelectionContext &selectionState)
{
ModelNode modelNode = selectionState.currentSingleSelectedNode();
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
bool isInBundle = modelNode.type().startsWith(compUtils.componentBundlesTypePrefix().toLatin1());
bool isNode3D = modelNode.metaInfo().isQtQuick3DNode();
return isNode3D && !isInBundle;
}
inline bool is3DNode(const SelectionContext &selectionState)
{
ModelNode modelNode = selectionState.currentSingleSelectedNode();
return modelNode.metaInfo().isQtQuick3DNode();
}
inline bool hasEditableMaterial(const SelectionContext &selectionState)
{
ModelNode node = selectionState.currentSingleSelectedNode();

View File

@@ -797,6 +797,23 @@ void moveToComponent(const SelectionContext &selectionContext)
selectionContext.view()->model()->rewriterView()->moveToComponent(modelNode);
}
void add3DAssetToContentLibrary(const SelectionContext &selectionContext)
{
ModelNode node = selectionContext.currentSingleSelectedNode();
selectionContext.view()->emitCustomNotification("add_3d_to_content_lib", {node});
}
void importComponent(const SelectionContext &selectionContext)
{
selectionContext.view()->emitCustomNotification("import_bundle_to_project");
}
void exportComponent(const SelectionContext &selectionContext)
{
ModelNode node = selectionContext.currentSingleSelectedNode();
selectionContext.view()->emitCustomNotification("export_item_as_bundle", {node});
}
void goImplementation(const SelectionContext &selectionState)
{
addSignalHandlerOrGotoImplementation(selectionState, false);
@@ -1622,7 +1639,7 @@ void addMouseAreaFill(const SelectionContext &selectionContext)
QmlDesigner::ModelNode mouseAreaNode = selectionContext.view()->createModelNode(
"QtQuick.MouseArea", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion());
#endif
mouseAreaNode.validId();
mouseAreaNode.ensureIdExists();
modelNode.defaultNodeListProperty().reparentHere(mouseAreaNode);
QmlItemNode mouseAreaItemNode(mouseAreaNode);

View File

@@ -96,6 +96,9 @@ void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState
void removeLayout(const SelectionContext &selectionContext);
void removePositioner(const SelectionContext &selectionContext);
void moveToComponent(const SelectionContext &selectionContext);
void add3DAssetToContentLibrary(const SelectionContext &selectionContext);
void importComponent(const SelectionContext &selectionContext);
void exportComponent(const SelectionContext &selectionContext);
PropertyName getIndexPropertyName(const ModelNode &modelNode);
void addItemToStackedContainer(const SelectionContext &selectionContext);
void increaseIndexOfStackedContainer(const SelectionContext &selectionContext);

View File

@@ -13,6 +13,7 @@
#include <qmlprojectmanager/buildsystem/qmlbuildsystem.h>
#include <qmlprojectmanager/qmlprojectconstants.h>
#include <qmlprojectmanager/cmakegen/filetypes.h>
#include <qtsupport/qtkitaspect.h>
@@ -143,41 +144,17 @@ void generateMenuEntry(QObject *parent)
exportMenu->addAction(cmd2, QmlProjectManager::Constants::G_EXPORT_GENERATE);
}
bool skipSuffix(const QString &fileName)
{
const QStringList suffixes = {".pri",
".pro",
".user",
".qrc",
".qds",
"CMakeLists.txt",
".db",
".tmp",
".TMP",
".metainfo",
".qtds",
".db-shm",
".db-wal"};
for (const auto &suffix : suffixes)
if (fileName.endsWith(suffix))
return true;
return false;
}
QStringList getProjectFileList()
{
const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject();
const FilePaths paths = project->files(ProjectExplorer::Project::AllFiles);
const QDir dir(project->projectFilePath().parentDir().toString());
QStringList selectedFileList;
const Utils::FilePath dir(project->projectFilePath().parentDir());
for (const FilePath &path : paths) {
QString relativePath = dir.relativeFilePath(path.toString());
if (!skipSuffix(relativePath))
selectedFileList.append(relativePath);
const Utils::FilePath relativePath = path.relativePathFrom(dir);
if (QmlProjectManager::isResource(relativePath))
selectedFileList.append(relativePath.path());
}
return selectedFileList;

View File

@@ -11,7 +11,6 @@ namespace QmlDesigner::ResourceGenerator {
QMLDESIGNERCOMPONENTS_EXPORT void generateMenuEntry(QObject *parent);
QMLDESIGNERCOMPONENTS_EXPORT QStringList getProjectFileList();
QMLDESIGNERCOMPONENTS_EXPORT bool skipSuffix(const QString &fileName);
QMLDESIGNERCOMPONENTS_EXPORT bool createQrcFile(const Utils::FilePath &qrcFilePath);
QMLDESIGNERCOMPONENTS_EXPORT bool createQmlrcFile(const Utils::FilePath &qmlrcFilePath);

View File

@@ -34,7 +34,8 @@ Theme::Theme(Utils::Theme *originTheme, QObject *parent)
"qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml")
.toString();
QQmlEngine* engine = new QQmlEngine(this);
QQmlEngine *engine = new QQmlEngine(this);
setupTheme(engine);
QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath));
if (component.status() == QQmlComponent::Ready) {
@@ -153,8 +154,7 @@ QPixmap Theme::getPixmap(const QString &id)
QString Theme::getIconUnicode(Theme::Icon i)
{
if (!instance()->m_constants)
return QString();
QTC_ASSERT(instance()->m_constants, return {});
const QMetaObject *m = instance()->metaObject();
const char *enumName = "Icon";
@@ -172,6 +172,7 @@ QString Theme::getIconUnicode(Theme::Icon i)
QString Theme::getIconUnicode(const QString &name)
{
QTC_ASSERT(instance()->m_constants, return {});
return instance()->m_constants->property(name.toStdString().data()).toString();
}

View File

@@ -20,17 +20,19 @@
#include <itemlibraryview.h>
#include <materialbrowserview.h>
#include <materialeditorview.h>
#include <model/auxiliarypropertystorageview.h>
#include <navigatorview.h>
#include <nodeinstanceview.h>
#include <propertyeditorview.h>
#include <qmldesignerplugin.h>
#include <rewriterview.h>
#include <stateseditorview.h>
#include <texteditorview.h>
#include <textureeditorview.h>
#include <qmldesignerplugin.h>
#include <coreplugin/icore.h>
#include <sqlitedatabase.h>
#include <utils/algorithm.h>
#include <advanceddockingsystem/dockwidget.h>
@@ -49,6 +51,7 @@ public:
ViewManagerData(AsynchronousImageCache &imageCache,
ExternalDependenciesInterface &externalDependencies)
: debugView{externalDependencies}
, auxiliaryDataKeyView{auxiliaryDataDatabase, externalDependencies}
, designerActionManagerView{externalDependencies}
, nodeInstanceView(QCoreApplication::arguments().contains("-capture-puppet-stream")
? capturingConnectionManager
@@ -78,6 +81,11 @@ public:
CapturingConnectionManager capturingConnectionManager;
QmlModelState savedState;
Internal::DebugView debugView;
Sqlite::Database auxiliaryDataDatabase{
Utils::PathString{Core::ICore::userResourcePath("auxiliary_data.db").toString()},
Sqlite::JournalMode::Wal,
Sqlite::LockingMode::Normal};
AuxiliaryPropertyStorageView auxiliaryDataKeyView;
DesignerActionManagerView designerActionManagerView;
NodeInstanceView nodeInstanceView;
ContentLibraryView contentLibraryView;
@@ -161,7 +169,7 @@ void ViewManager::attachRewriterView()
});
currentModel()->setRewriterView(view);
view->reactivateTextMofifierChangeSignals();
view->reactivateTextModifierChangeSignals();
view->restoreAuxiliaryData();
}
@@ -171,7 +179,7 @@ void ViewManager::attachRewriterView()
void ViewManager::detachRewriterView()
{
if (RewriterView *view = currentDesignDocument()->rewriterView()) {
view->deactivateTextMofifierChangeSignals();
view->deactivateTextModifierChangeSignals();
currentModel()->setRewriterView(nullptr);
}
}
@@ -201,7 +209,8 @@ QList<AbstractView *> ViewManager::views() const
QList<AbstractView *> ViewManager::standardViews() const
{
#ifndef QTC_USE_QML_DESIGNER_LITE
QList<AbstractView *> list = {&d->edit3DView,
QList<AbstractView *> list = {&d->auxiliaryDataKeyView,
&d->edit3DView,
&d->formEditorView,
&d->textEditorView,
&d->assetsLibraryView,
@@ -416,23 +425,9 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
widgetInfoList.append(view->widgetInfo());
}
Utils::sort(widgetInfoList, [](const WidgetInfo &firstWidgetInfo, const WidgetInfo &secondWidgetInfo) {
return firstWidgetInfo.placementPriority < secondWidgetInfo.placementPriority;
});
return widgetInfoList;
}
QWidget *ViewManager::widget(const QString &uniqueId) const
{
const QList<WidgetInfo> widgetInfoList = widgetInfos();
for (const WidgetInfo &widgetInfo : widgetInfoList) {
if (widgetInfo.uniqueId == uniqueId)
return widgetInfo.widget;
}
return nullptr;
}
void ViewManager::disableWidgets()
{
for (const auto &view : views())

View File

@@ -62,7 +62,6 @@ public:
}
QList<WidgetInfo> widgetInfos() const;
QWidget *widget(const QString & uniqueId) const;
void disableWidgets();
void enableWidgets();

View File

@@ -6,6 +6,7 @@
#include "backendmodel.h"
#include "bindingproperty.h"
#include "connectioneditorutils.h"
#include "connectionview.h"
#include "exception.h"
#include "nodemetainfo.h"
@@ -203,7 +204,7 @@ void BackendModel::addNewBackend()
if (dialog.applied()) {
QStringList importSplit = dialog.importString().split(" ");
if (importSplit.size() != 2) {
qWarning() << Q_FUNC_INFO << "invalid import" << importSplit;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid import" << importSplit;
QTC_ASSERT(false, return);
}
@@ -278,7 +279,7 @@ void BackendModel::updatePropertyName(int rowNumber)
rootModelNode.removeProperty(oldName);
rootModelNode.bindingProperty(newName).setDynamicTypeNameAndExpression(typeName, expression);
} else {
qWarning() << Q_FUNC_INFO << oldName << newName << "failed...";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << oldName << newName << "failed...";
QTC_ASSERT(false, return);
}
});
@@ -290,7 +291,7 @@ void BackendModel::handleDataChanged(const QModelIndex &topLeft, const QModelInd
return;
if (topLeft != bottomRight) {
qWarning() << "BackendModel::handleDataChanged multi edit?";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "multi edit?";
return;
}
@@ -307,7 +308,8 @@ void BackendModel::handleDataChanged(const QModelIndex &topLeft, const QModelInd
updatePropertyName(currentRow);
} break;
default: qWarning() << "BindingModel::handleDataChanged column" << currentColumn;
default:
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "column" << currentColumn;
}
m_lock = false;

View File

@@ -89,7 +89,7 @@ void BindingModel::add()
}
}
} else {
qWarning() << __FUNCTION__ << " Requires exactly one selected node";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Requires exactly one selected node";
}
}
@@ -211,7 +211,7 @@ QHash<int, QByteArray> BindingModel::roleNames() const
std::optional<int> BindingModel::rowForProperty(const AbstractProperty &property) const
{
PropertyName name = property.name();
PropertyNameView name = property.name();
int internalId = property.parentModelNode().internalId();
for (int i = 0; i < rowCount(); ++i) {

View File

@@ -42,7 +42,7 @@ void BindingModelItem::updateProperty(const BindingProperty &property)
{
setData(property.parentModelNode().internalId(), InternalIdRole);
setData(idOrTypeName(property.parentModelNode()), TargetNameRole);
setData(property.name(), TargetPropertyNameRole);
setData(property.name().toByteArray(), TargetPropertyNameRole);
// TODO: Make this safe when the new codemodel allows it.
if (auto expression = property.expression(); !expression.isEmpty()) {

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectioneditorevaluator.h"
#include "connectioneditorutils.h"
#include "qmljs/parser/qmljsast_p.h"
#include "qmljs/qmljsdocument.h"
@@ -254,7 +255,7 @@ protected:
void throwRecursionDepthError() override
{
checkValidityAndReturn(false, "Recursion depth problem");
qDebug() << Q_FUNC_INFO << this;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error";
}
void checkAndResetVariable()
@@ -477,7 +478,7 @@ protected:
void throwRecursionDepthError() override
{
setFailed();
qDebug() << Q_FUNC_INFO << this;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error";
}
void checkAndResetCal()
@@ -617,7 +618,7 @@ protected:
void throwRecursionDepthError() override
{
m_failed = true;
qDebug() << Q_FUNC_INFO << this;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error";
}
private:
@@ -1082,7 +1083,7 @@ void ConnectionEditorEvaluator::endVisit(QmlJS::AST::StatementList * /*statement
void ConnectionEditorEvaluator::throwRecursionDepthError()
{
d->checkValidityAndReturn(false, "Recursion depth problem");
qDebug() << Q_FUNC_INFO << this;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Recursion depth error";
}
bool ConnectionEditorEvaluatorPrivate::checkValidityAndReturn(bool valid, const QString &parseError)
@@ -1091,7 +1092,7 @@ bool ConnectionEditorEvaluatorPrivate::checkValidityAndReturn(bool valid, const
if (m_checkStatus != Status::Failed) {
setStatus(Status::Failed);
m_errorString = parseError;
qDebug() << Q_FUNC_INFO << "Error: " << parseError;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "Parse error" << parseError;
}
}

View File

@@ -24,6 +24,8 @@
namespace QmlDesigner {
Q_LOGGING_CATEGORY(ConnectionEditorLog, "qtc.qtquickdesigner.connectioneditor", QtWarningMsg)
void callLater(const std::function<void()> &fun)
{
QTimer::singleShot(0, fun);
@@ -78,7 +80,7 @@ NodeMetaInfo dynamicTypeNameToNodeMetaInfo(const TypeName &typeName, Model *mode
else if (typeName == "var" || typeName == "variant")
return model->metaInfo("QML.variant");
else
qWarning() << __FUNCTION__ << " type " << typeName << "not found";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "type" << typeName << "not found";
return {};
}
@@ -173,7 +175,7 @@ void convertPropertyType(const T &property, const QVariant &value)
if (!node.isValid())
return;
PropertyName name = property.name();
PropertyNameView name = property.name();
TypeName type = property.dynamicTypeName();
node.removeProperty(name);
@@ -203,7 +205,8 @@ bool isBindingExpression(const QVariant &value)
if (value.metaType().id() != QMetaType::QString)
return false;
QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+");
QRegularExpression regexp("^[a-zA-Z_]\\w*\\.{1}([a-z_]\\w*\\.?)+");
// QRegularExpression regexp("^[a-z_]\\w*|^[A-Z]\\w*\\.{1}([a-z_]\\w*\\.?)+");
QRegularExpressionMatch match = regexp.match(value.toString());
return match.hasMatch();
}
@@ -301,13 +304,16 @@ std::vector<PropertyMetaInfo> propertiesFromSingleton(const QString &name, Abstr
QList<AbstractProperty> dynamicPropertiesFromNode(const ModelNode &node)
{
auto isDynamic = [](const AbstractProperty &p) { return p.isDynamic(); };
auto isDynamic = [](const AbstractProperty &p) {
return p.isDynamic() || p.isSignalDeclarationProperty();
};
auto byName = [](const AbstractProperty &a, const AbstractProperty &b) {
return a.name() < b.name();
};
QList<AbstractProperty> dynamicProperties = Utils::filtered(node.properties(), isDynamic);
Utils::sort(dynamicProperties, byName);
return dynamicProperties;
}
@@ -326,7 +332,7 @@ QStringList availableTargetProperties(const BindingProperty &bindingProperty)
{
const ModelNode modelNode = bindingProperty.parentModelNode();
if (!modelNode.isValid()) {
qWarning() << __FUNCTION__ << " invalid model node";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid model node";
return {};
}
@@ -368,7 +374,7 @@ QStringList availableSourceProperties(const QString &id,
} else if (auto metaInfo = targetProperty.parentModelNode().metaInfo(); metaInfo.isValid()) {
targetType = metaInfo.property(targetProperty.name()).propertyType();
} else
qWarning() << __FUNCTION__ << " no meta info for target node";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "no meta info for target node";
QStringList possibleProperties;
if (!modelNode.isValid()) {
@@ -380,7 +386,7 @@ QStringList availableSourceProperties(const QString &id,
}
return possibleProperties;
}
qWarning() << __FUNCTION__ << " invalid model node: " << id;
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid model node:" << id;
return {};
}
@@ -406,7 +412,7 @@ QStringList availableSourceProperties(const QString &id,
possibleProperties.push_back(QString::fromUtf8(property.name()));
}
} else {
qWarning() << __FUNCTION__ << " no meta info for source node";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "no meta info for source node";
}
return possibleProperties;

View File

@@ -7,11 +7,14 @@
#include "propertymetainfo.h"
#include <QList>
#include <QLoggingCategory>
#include <QString>
#include <QVariant>
namespace QmlDesigner {
Q_DECLARE_LOGGING_CATEGORY(ConnectionEditorLog)
class AbstractView;
class AbstractProperty;
class BindingProperty;

View File

@@ -2,13 +2,14 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "connectionmodel.h"
#include "connectioneditorutils.h"
#include "connectionview.h"
#include "utils/algorithm.h"
#include <bindingproperty.h>
#include <connectioneditorevaluator.h>
#include <exception.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <modelnodeoperations.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
@@ -201,7 +202,7 @@ void ConnectionModel::updateSignalName(int rowNumber)
SignalHandlerProperty newSignalHandlerProperty = connectionNode.signalHandlerProperty(newName);
updateCustomData(idItem, newSignalHandlerProperty);
} else {
qWarning() << "BindingModel::updatePropertyName invalid property name";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid property name";
}
}
@@ -251,14 +252,14 @@ void ConnectionModel::updateTargetNode(int rowNumber)
});
} else {
qWarning() << "BindingModel::updatePropertyName invalid target id";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid target id";
}
}
void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty)
{
item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole);
item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole);
item->setData(signalHandlerProperty.name().toByteArray(), UserRoles::TargetPropertyNameRole);
item->setData(signalHandlerProperty.parentModelNode()
.bindingProperty("target")
.resolveToModelNode()
@@ -532,7 +533,7 @@ ConnectionModelBackendDelegate *ConnectionModel::delegate()
void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
if (topLeft != bottomRight) {
qWarning() << "ConnectionModel::handleDataChanged multi edit?";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "multi edit?";
return;
}
@@ -551,8 +552,8 @@ void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModel
case SourceRow: {
updateSource(currentRow);
} break;
default: qWarning() << "ConnectionModel::handleDataChanged column" << currentColumn;
default:
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "column" << currentColumn;
}
m_lock = false;
@@ -1694,9 +1695,9 @@ QVariant ConditionListModel::data(const QModelIndex &index, int role) const
return m_tokens.at(index.row()).value;
}
qWarning() << Q_FUNC_INFO << "invalid role";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid role";
} else {
qWarning() << Q_FUNC_INFO << "invalid index";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "invalid index";
}
return QVariant();

View File

@@ -303,7 +303,7 @@ WidgetInfo ConnectionView::widgetInfo()
return createWidgetInfo(d->connectionViewQuickWidget.get(),
QLatin1String("ConnectionView"),
WidgetInfo::LeftPane,
0,
tr("Connections"));
}

View File

@@ -55,7 +55,7 @@ void DynamicPropertiesItem::updateProperty(const AbstractProperty &property)
{
setData(property.parentModelNode().internalId(), InternalIdRole);
setData(idOrTypeName(property.parentModelNode()), TargetNameRole);
setData(property.name(), PropertyNameRole);
setData(property.name().toByteArray(), PropertyNameRole);
setData(property.dynamicTypeName(), PropertyTypeRole);
if (property.isVariantProperty()) {

View File

@@ -68,7 +68,7 @@ void DynamicPropertiesModel::add()
showErrorMessage(e.description());
}
} else {
qWarning() << "DynamicPropertiesModel::add not one node selected";
qCWarning(ConnectionEditorLog) << __FUNCTION__ << "not one node selected";
}
}
@@ -136,7 +136,7 @@ void DynamicPropertiesModel::setCurrentProperty(const AbstractProperty &property
setCurrentIndex(*index);
}
void DynamicPropertiesModel::setCurrent(int internalId, const PropertyName &name)
void DynamicPropertiesModel::setCurrent(int internalId, PropertyNameView name)
{
if (internalId < 0)
return;
@@ -195,7 +195,7 @@ AbstractProperty DynamicPropertiesModel::propertyForRow(int row) const
return {};
}
std::optional<int> DynamicPropertiesModel::findRow(int nodeId, const PropertyName &name) const
std::optional<int> DynamicPropertiesModel::findRow(int nodeId, PropertyNameView name) const
{
for (int i = 0; i < rowCount(); ++i) {
if (auto *item = itemForRow(i)) {
@@ -243,7 +243,7 @@ void DynamicPropertiesModel::addModelNode(const ModelNode &node)
void DynamicPropertiesModel::addProperty(const AbstractProperty &property)
{
const PropertyName name = property.name();
const PropertyNameView name = property.name();
for (int i = 0; i < rowCount(); ++i) {
if (auto *item = itemForRow(i)) {
if (item->propertyName() > name) {
@@ -282,7 +282,7 @@ void DynamicPropertiesModel::commitPropertyType(int row, const TypeName &type)
}
}
void DynamicPropertiesModel::commitPropertyName(int row, const PropertyName &name)
void DynamicPropertiesModel::commitPropertyName(int row, PropertyNameView name)
{
AbstractProperty property = propertyForRow(row);
if (!property.isValid())
@@ -346,7 +346,7 @@ void DynamicPropertiesModel::dispatchPropertyChanges(const AbstractProperty &abs
QmlPropertyChanges changes(abstractProperty.parentModelNode());
if (changes.target().isValid()) {
const ModelNode target = changes.target();
const PropertyName propertyName = abstractProperty.name();
const PropertyNameView propertyName = abstractProperty.name();
const AbstractProperty targetProperty = target.variantProperty(propertyName);
if (target.hasProperty(propertyName) && targetProperty.isDynamic())
updateItem(targetProperty);

View File

@@ -46,13 +46,13 @@ public:
void reset(const QList<ModelNode> &modelNodes = {});
void setCurrentIndex(int i);
void setCurrentProperty(const AbstractProperty &property);
void setCurrent(int internalId, const PropertyName &name);
void setCurrent(int internalId, PropertyNameView name);
void updateItem(const AbstractProperty &property);
void removeItem(const AbstractProperty &property);
void commitPropertyType(int row, const TypeName &type);
void commitPropertyName(int row, const PropertyName &name);
void commitPropertyName(int row, PropertyNameView name);
void commitPropertyValue(int row, const QVariant &value);
void dispatchPropertyChanges(const AbstractProperty &abstractProperty);
@@ -61,7 +61,7 @@ protected:
QHash<int, QByteArray> roleNames() const override;
private:
std::optional<int> findRow(int nodeId, const PropertyName &name) const;
std::optional<int> findRow(int nodeId, PropertyNameView name) const;
DynamicPropertiesItem *itemForRow(int row) const;
DynamicPropertiesItem *itemForProperty(const AbstractProperty &property) const;
ModelNode modelNodeForItem(DynamicPropertiesItem *item);

View File

@@ -7,7 +7,7 @@
#include <bindingproperty.h>
#include <designeralgorithm.h>
#include <exception.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
@@ -519,47 +519,51 @@ const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredPropertyName
const std::vector<PropertyName> PropertyTreeModel::getDynamicProperties(
const ModelNode &modelNode) const
{
QList<PropertyName> list = Utils::transform(modelNode.dynamicProperties(),
[](const AbstractProperty &property) {
return property.name();
});
auto dynamicProperties = modelNode.dynamicProperties();
auto dynamicPropertyNames = Utils::transform<PropertyNameViews>(
dynamicProperties, [](const AbstractProperty &property) { return property.name(); });
QList<PropertyName> filtered
= Utils::filtered(list, [this, modelNode](const PropertyName &propertyName) {
PropertyName propertyType = modelNode.property(propertyName).dynamicTypeName();
switch (m_type) {
case AllTypes:
return true;
case NumberType:
return propertyType == "float" || propertyType == "double"
|| propertyType == "int";
case StringType:
return propertyType == "string";
case UrlType:
return propertyType == "url";
case ColorType:
return propertyType == "color";
case BoolType:
return propertyType == "bool";
default:
break;
}
return true;
});
auto filtered = Utils::filtered(dynamicPropertyNames, [this, modelNode](PropertyNameView propertyName) {
TypeName propertyType = modelNode.property(propertyName).dynamicTypeName();
switch (m_type) {
case AllTypes:
return true;
case NumberType:
return propertyType == "float" || propertyType == "double" || propertyType == "int";
case StringType:
return propertyType == "string";
case UrlType:
return propertyType == "url";
case ColorType:
return propertyType == "color";
case BoolType:
return propertyType == "bool";
default:
break;
}
return true;
});
return Utils::sorted(std::vector<PropertyName>(filtered.begin(), filtered.end()));
auto sorted = Utils::sorted(filtered);
return Utils::transform<std::vector<PropertyName>>(sorted, [](PropertyNameView propertyName) {
return propertyName.toByteArray();
});
}
const std::vector<PropertyName> PropertyTreeModel::getDynamicSignals(const ModelNode &modelNode) const
{
QList<PropertyName> list = Utils::transform(modelNode.dynamicProperties(),
[](const AbstractProperty &property) {
if (property.isSignalDeclarationProperty())
return property.name();
auto list = Utils::transform<std::vector<PropertyName>>(
modelNode.dynamicProperties(), [](const AbstractProperty &property) -> PropertyName {
if (property.isSignalDeclarationProperty())
return property.name().toByteArray();
return PropertyName(property.name() + "Changed");
});
return Utils::sorted(std::vector<PropertyName>(list.begin(), list.end()));
return property.name().toByteArray() + "Changed";
});
std::sort(list.begin(), list.end());
return list;
}
const std::vector<PropertyName> PropertyTreeModel::sortedAndFilteredPropertyNames(

View File

@@ -11,7 +11,9 @@
#include <qmldesignerplugin.h>
#include <rewritingexception.h>
#include <modelutils.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <utils/async.h>
#include <QJsonDocument>
#include <QJsonObject>
@@ -21,10 +23,11 @@ using namespace Utils;
namespace QmlDesigner {
static constexpr int normalImportDelay = 200;
ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent)
: QObject(parent)
{
m_importTimer.setInterval(200);
connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer);
}
@@ -49,19 +52,21 @@ QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir,
if (!bundleImportPath.exists() && !bundleImportPath.createDir())
return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString());
bool doScan = false;
bool doReset = false;
FilePath qmldirPath = bundleImportPath.pathAppended("qmldir");
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module ");
qmldirContent.append(module);
qmldirContent.append('\n');
doScan = true;
}
FilePath qmlSourceFile = bundleImportPath.pathAppended(qmlFile);
const bool qmlFileExists = qmlSourceFile.exists();
const QString qmlType = qmlSourceFile.baseName();
if (m_pendingTypes.contains(type) && !m_pendingTypes.value(type))
if (m_pendingImports.contains(type) && !m_pendingImports[type].isImport)
return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(QLatin1String(type));
if (!qmldirContent.contains(qmlFile)) {
@@ -70,6 +75,7 @@ QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir,
qmldirContent.append(qmlFile);
qmldirContent.append('\n');
qmldirPath.writeFileContents(qmldirContent.toUtf8());
doReset = true;
}
QStringList allFiles;
@@ -104,16 +110,22 @@ QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir,
if (writeAssetRefs)
writeAssetRefMap(bundleImportPath, assetRefMap);
m_fullReset = !qmlFileExists;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (!model)
return "Model not available, cannot add import statement or update code model";
ImportData data;
data.isImport = true;
data.type = type;
if (doScan)
data.pathToScan = bundleImportPath;
else
data.fullReset = doReset;
Import import = Import::createLibraryImport(module, "1.0");
if (!model->hasImport(import)) {
if (model->possibleImports().contains(import)) {
m_pendingImport.clear();
try {
model->changeImports({import}, {});
} catch (const RewritingException &) {
@@ -123,12 +135,12 @@ QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir,
} else {
// If import is not yet possible, import statement needs to be added asynchronously to
// avoid errors, as code model update takes a while.
m_pendingImport = module;
data.importToAdd = module;
}
}
m_pendingTypes.insert(type, true);
m_pendingImports.insert(type, data);
m_importTimerCount = 0;
m_importTimer.start();
m_importTimer.start(normalImportDelay);
return {};
}
@@ -137,21 +149,20 @@ void ContentLibraryBundleImporter::handleImportTimer()
{
auto handleFailure = [this] {
m_importTimer.stop();
m_fullReset = false;
m_pendingImport.clear();
m_importTimerCount = 0;
disconnect(m_libInfoConnection);
// Emit dummy finished signals for all pending types
const QList<TypeName> pendingTypes = m_pendingTypes.keys();
const QList<TypeName> pendingTypes = m_pendingImports.keys();
for (const TypeName &pendingType : pendingTypes) {
m_pendingTypes.remove(pendingType);
if (m_pendingTypes.value(pendingType))
ImportData data = m_pendingImports.take(pendingType);
if (data.isImport)
emit importFinished({}, m_bundleId);
else
emit unimportFinished({}, m_bundleId);
m_bundleId.clear();
}
m_bundleId.clear();
};
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -161,55 +172,125 @@ void ContentLibraryBundleImporter::handleImportTimer()
return;
}
if (m_fullReset) {
// Force code model reset to notice changes to existing module
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (modelManager)
modelManager->resetCodeModel();
m_fullReset = false;
return;
}
QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
if (!m_pendingImport.isEmpty()) {
try {
Import import = Import::createLibraryImport(m_pendingImport, "1.0");
if (model->possibleImports().contains(import)) {
model->changeImports({import}, {});
m_pendingImport.clear();
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (modelManager) {
const QList<TypeName> keys = m_pendingImports.keys();
int scanDone = 0;
bool refreshImports = false;
for (const TypeName &type : keys) {
ImportData &data = m_pendingImports[type];
if (data.state == ImportData::Starting) {
if (data.pathToScan.isEmpty()) {
data.state = ImportData::FullReset;
} else {
// A new bundle module was added, so we can scan it without doing full code
// model reset, which is faster
QmlJS::PathsAndLanguages pathToScan;
pathToScan.maybeInsert(data.pathToScan);
data.future = Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan,
modelManager->workingCopy(),
pathToScan, modelManager,
true, true, true);
data.state = ImportData::WaitingForImportScan;
}
} else if (data.state == ImportData::WaitingForImportScan) {
// Import scan is asynchronous, so we need to wait for it to be finished
if ((data.future.isCanceled() || data.future.isFinished()))
data.state = ImportData::RefreshImports;
} else if (data.state >= ImportData::RefreshImports) {
// Do not mode to next stage until all pending scans are done
++scanDone;
if (data.state == ImportData::RefreshImports)
refreshImports = true;
}
} catch (const RewritingException &) {
// Import adding is unlikely to succeed later, either, so just bail out
handleFailure();
}
return;
}
if (scanDone != m_pendingImports.size()) {
return;
} else {
if (refreshImports) {
// The new import is now available in code model, so reset the possible imports
QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
model->rewriterView()->forceAmend();
// Detect when the code model has the new material(s) fully available
const QList<TypeName> pendingTypes = m_pendingTypes.keys();
for (const TypeName &pendingType : pendingTypes) {
NodeMetaInfo metaInfo = model->metaInfo(pendingType);
const bool isImport = m_pendingTypes.value(pendingType);
const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty();
if (isImport == typeComplete) {
m_pendingTypes.remove(pendingType);
if (isImport)
for (const TypeName &type : keys) {
ImportData &data = m_pendingImports[type];
if (data.state == ImportData::RefreshImports) {
if (!data.importToAdd.isEmpty()) {
try {
RewriterTransaction transaction = model->rewriterView()->beginRewriterTransaction(
QByteArrayLiteral(__FUNCTION__));
bool success = ModelUtils::addImportWithCheck(data.importToAdd, model);
if (!success)
handleFailure();
transaction.commit();
} catch (const RewritingException &) {
handleFailure();
}
}
}
data.state = ImportData::FullReset;
}
return;
}
bool fullReset = false;
for (const TypeName &type : keys) {
ImportData &data = m_pendingImports[type];
if (data.state == ImportData::FullReset) {
if (data.fullReset)
fullReset = true;
data.state = ImportData::Finalize;
}
}
if (fullReset) {
// Force code model reset to notice changes to existing module
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (modelManager) {
modelManager->resetCodeModel();
disconnect(m_libInfoConnection);
m_libInfoConnection = connect(modelManager, &QmlJS::ModelManagerInterface::libraryInfoUpdated,
this, [this]() {
// This signal comes for each library in code model, so we need to compress
// it until no more notifications come
m_importTimer.start(1000);
}, Qt::QueuedConnection);
// Stop the import timer for a bit to allow full reset to complete
m_importTimer.stop();
}
return;
}
for (const TypeName &type : keys) {
ImportData &data = m_pendingImports[type];
if (data.state == ImportData::Finalize) {
// Reset delay just in case full reset was done earlier
m_importTimer.start(normalImportDelay);
disconnect(m_libInfoConnection);
model->rewriterView()->forceAmend();
// Verify that code model has the new material fully available (or removed for unimport)
NodeMetaInfo metaInfo = model->metaInfo(type);
const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty();
if (data.isImport == typeComplete) {
m_pendingImports.remove(type);
if (data.isImport)
#ifdef QDS_USE_PROJECTSTORAGE
emit importFinished(pendingType, m_bundleId);
emit importFinished(type, m_bundleId);
#else
emit importFinished(metaInfo, m_bundleId);
emit importFinished(metaInfo, m_bundleId);
#endif
else
emit unimportFinished(metaInfo, m_bundleId);
m_bundleId.clear();
else
emit unimportFinished(metaInfo, m_bundleId);
}
}
}
}
}
if (m_pendingTypes.isEmpty()) {
if (m_pendingImports.isEmpty()) {
m_bundleId.clear();
m_importTimer.stop();
m_importTimerCount = 0;
disconnect(m_libInfoConnection);
}
}
@@ -267,7 +348,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, co
QByteArray newContent;
QString qmlType = qmlFilePath.baseName();
if (m_pendingTypes.contains(type) && m_pendingTypes.value(type)) {
if (m_pendingImports.contains(type) && m_pendingImports[type].isImport) {
return QStringLiteral("Unable to unimport while importing the same type: '%1'")
.arg(QString::fromLatin1(type));
}
@@ -286,8 +367,6 @@ QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, co
}
}
m_pendingTypes.insert(type, false);
QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath);
bool writeAssetRefs = false;
const auto keys = assetRefMap.keys();
@@ -326,9 +405,14 @@ QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, co
}
}
m_fullReset = true;
ImportData data;
data.isImport = false;
data.type = type;
data.fullReset = true;
m_pendingImports.insert(type, data);
m_importTimerCount = 0;
m_importTimer.start();
m_importTimer.start(normalImportDelay);
return {};
}

View File

@@ -7,6 +7,7 @@
#include <utils/filepath.h>
#include <QFuture>
#include <QTimer>
#include <QVariantHash>
@@ -46,10 +47,27 @@ private:
QTimer m_importTimer;
int m_importTimerCount = 0;
QString m_pendingImport;
QString m_bundleId;
bool m_fullReset = false;
QHash<TypeName, bool> m_pendingTypes; // <type, isImport>
struct ImportData
{
enum State {
Starting,
WaitingForImportScan,
RefreshImports,
FullReset,
Finalize
};
bool isImport = true; // false = unimport
TypeName type;
Utils::FilePath pathToScan; // If set, do importScan
QFuture<void> future;
QString importToAdd; // If set, add import to model
bool fullReset = false; // If true, reset the entire code model.
State state = Starting;
};
QHash<TypeName, ImportData> m_pendingImports;
QMetaObject::Connection m_libInfoConnection;
};
} // namespace QmlDesigner

View File

@@ -93,11 +93,9 @@ QHash<int, QByteArray> ContentLibraryEffectsModel::roleNames() const
return roles;
}
void ContentLibraryEffectsModel::loadBundle()
void ContentLibraryEffectsModel::loadBundle(bool force)
{
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
if (m_probeBundleDir || (m_bundleExists && m_bundleId == compUtils.effectsBundleId()))
if (m_probeBundleDir && !force)
return;
// clean up
@@ -148,6 +146,7 @@ void ContentLibraryEffectsModel::loadBundle()
m_bundleObj = bundleJsonDoc.object();
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
QString bundleType = compUtils.effectsBundleType();
m_bundleId = compUtils.effectsBundleId();
@@ -171,17 +170,14 @@ void ContentLibraryEffectsModel::loadBundle()
TypeName type = QLatin1String("%1.%2")
.arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files);
auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files, m_bundleId);
category->addBundleItem(bundleItem);
}
m_bundleCategories.append(category);
}
m_bundleSharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
for (const QJsonValueConstRef &file : sharedFilesArr)
m_bundleSharedFiles.append(file.toString());
m_bundleSharedFiles = m_bundleObj.value("sharedFiles").toVariant().toStringList();
m_bundlePath = bundleDir.path();
m_bundleExists = true;

View File

@@ -28,7 +28,7 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QHash<int, QByteArray> roleNames() const override;
void loadBundle();
void loadBundle(bool force = false);
void setSearchText(const QString &searchText);
void updateImportedState(const QStringList &importedItems);

View File

@@ -7,21 +7,18 @@
namespace QmlDesigner {
namespace Internal {
ContentLibraryIconProvider::ContentLibraryIconProvider()
: QQuickImageProvider(Pixmap)
{
}
QPixmap ContentLibraryIconProvider::requestPixmap(const QString &id,
QSize *size,
[[maybe_unused]] const QSize &requestedSize)
{
QString realPath = Core::ICore::resourcePath("qmldesigner/contentLibraryImages/" + id).toString();
QString imagePath = Core::ICore::resourcePath("qmldesigner/contentLibraryImages/" + id).toFSPathString();
QPixmap pixmap{realPath};
QPixmap pixmap{imagePath};
if (size) {
size->setWidth(pixmap.width());
@@ -37,6 +34,4 @@ QPixmap ContentLibraryIconProvider::requestPixmap(const QString &id,
return pixmap;
}
} // namespace Internal
} // namespace QmlDesigner

View File

@@ -5,7 +5,7 @@
#include <QQuickImageProvider>
namespace QmlDesigner::Internal {
namespace QmlDesigner {
class ContentLibraryIconProvider : public QQuickImageProvider
{
@@ -14,4 +14,4 @@ public:
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override;
};
} // namespace QmlDesigner::Internal
} // namespace QmlDesigner

View File

@@ -10,11 +10,11 @@ ContentLibraryItem::ContentLibraryItem(QObject *parent,
const QString &qml,
const TypeName &type,
const QUrl &icon,
const QStringList &files)
: QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
const QStringList &files,
const QString &bundleId)
: QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files),
m_bundleId(bundleId)
{
m_allFiles = m_files;
m_allFiles.push_back(m_qml);
}
bool ContentLibraryItem::filter(const QString &searchText)
@@ -68,9 +68,9 @@ bool ContentLibraryItem::imported() const
return m_imported;
}
QStringList ContentLibraryItem::allFiles() const
QString ContentLibraryItem::bundleId() const
{
return m_allFiles;
return m_bundleId;
}
} // namespace QmlDesigner

View File

@@ -16,10 +16,9 @@ class ContentLibraryItem : public QObject
Q_PROPERTY(QString bundleItemName MEMBER m_name CONSTANT)
Q_PROPERTY(QUrl bundleItemIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(QStringList bundleItemFiles READ allFiles CONSTANT)
Q_PROPERTY(bool bundleItemVisible MEMBER m_visible NOTIFY itemVisibleChanged)
Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY itemImportedChanged)
Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
Q_PROPERTY(QString bundleId READ bundleId CONSTANT)
public:
ContentLibraryItem(QObject *parent,
@@ -27,7 +26,8 @@ public:
const QString &qml,
const TypeName &type,
const QUrl &icon,
const QStringList &files);
const QStringList &files,
const QString &bundleId);
bool filter(const QString &searchText);
@@ -37,9 +37,10 @@ public:
QStringList files() const;
bool visible() const;
QString bundleId() const;
bool setImported(bool imported);
bool imported() const;
QStringList allFiles() const;
signals:
void itemVisibleChanged();
@@ -55,8 +56,7 @@ private:
bool m_visible = true;
bool m_imported = false;
QStringList m_allFiles;
const QString m_itemType = "item";
const QString m_bundleId;
};
} // namespace QmlDesigner

View File

@@ -3,8 +3,6 @@
#include "contentlibrarymaterial.h"
#include <QFileInfo>
namespace QmlDesigner {
ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
@@ -13,10 +11,9 @@ ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
const TypeName &type,
const QUrl &icon,
const QStringList &files,
const QString &downloadPath,
const QString &baseWebUrl)
const QString &bundleId)
: QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
, m_downloadPath(downloadPath), m_baseWebUrl(baseWebUrl)
, m_bundleId(bundleId)
{
m_allFiles = m_files;
m_allFiles.push_back(m_qml);
@@ -78,25 +75,14 @@ bool ContentLibraryMaterial::imported() const
return m_imported;
}
bool ContentLibraryMaterial::isDownloaded() const
{
QString fullPath = qmlFilePath();
return QFileInfo(fullPath).isFile();
}
QString ContentLibraryMaterial::qmlFilePath() const
{
return m_downloadPath + "/" + m_qml;
}
QString ContentLibraryMaterial::dirPath() const
{
return m_downloadPath;
}
QStringList ContentLibraryMaterial::allFiles() const
{
return m_allFiles;
}
QString ContentLibraryMaterial::bundleId() const
{
return m_bundleId;
}
} // namespace QmlDesigner

View File

@@ -18,10 +18,8 @@ class ContentLibraryMaterial : public QObject
Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged)
Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY materialImportedChanged)
Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT)
Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT)
Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT)
Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
Q_PROPERTY(QString bundleId READ bundleId CONSTANT)
public:
ContentLibraryMaterial(QObject *parent,
@@ -30,26 +28,23 @@ public:
const TypeName &type,
const QUrl &icon,
const QStringList &files,
const QString &downloadPath,
const QString &baseWebUrl = {});
const QString &bundleId);
bool filter(const QString &searchText);
Q_INVOKABLE bool isDownloaded() const;
QString name() const;
QUrl icon() const;
QString qml() const;
TypeName type() const;
QStringList files() const;
bool visible() const;
QString qmlFilePath() const;
bool setImported(bool imported);
bool imported() const;
QString dirPath() const;
QStringList allFiles() const;
QString bundleId() const;
signals:
void materialVisibleChanged();
void materialImportedChanged();
@@ -64,10 +59,8 @@ private:
bool m_visible = true;
bool m_imported = false;
QString m_downloadPath;
QString m_baseWebUrl;
QStringList m_allFiles;
const QString m_itemType = "material";
const QString m_bundleId;
};
} // namespace QmlDesigner

View File

@@ -19,7 +19,6 @@
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
@@ -33,7 +32,7 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget
: QAbstractListModel(parent)
, m_widget(parent)
{
m_downloadPath = Paths::bundlesPathSetting() + "/Materials";
m_bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/Materials");
m_baseUrl = QmlDesignerPlugin::settings()
.value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL)
@@ -45,9 +44,8 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget
void ContentLibraryMaterialsModel::loadBundle()
{
QDir bundleDir{m_downloadPath};
if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir))
loadMaterialBundle(bundleDir);
if (fetchBundleJsonFile() && fetchBundleIcons())
loadMaterialBundle();
}
int ContentLibraryMaterialsModel::rowCount(const QModelIndex &) const
@@ -114,13 +112,14 @@ QHash<int, QByteArray> ContentLibraryMaterialsModel::roleNames() const
return roles;
}
bool ContentLibraryMaterialsModel::fetchBundleIcons(const QDir &bundleDir)
bool ContentLibraryMaterialsModel::fetchBundleIcons()
{
QString iconsPath = bundleDir.filePath("icons");
Utils::FilePath iconsFilePath = m_bundlePath.pathAppended("icons");
QDir iconsDir(iconsPath);
if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0)
if (iconsFilePath.exists() && iconsFilePath.dirEntries(QDir::Files | QDir::Dirs
| QDir::NoDotAndDotDot).length() > 0) {
return true;
}
QString zipFileUrl = m_baseUrl + "/icons.zip";
@@ -130,20 +129,20 @@ bool ContentLibraryMaterialsModel::fetchBundleIcons(const QDir &bundleDir)
downloader->setDownloadEnabled(true);
QObject::connect(downloader, &FileDownloader::finishedChanged, this,
[this, downloader, bundleDir] {
[this, downloader] {
FileExtractor *extractor = new FileExtractor(this);
extractor->setArchiveName(downloader->completeBaseName());
extractor->setSourceFile(downloader->outputFile());
extractor->setTargetPath(bundleDir.absolutePath());
extractor->setTargetPath(m_bundlePath.toFSPathString());
extractor->setAlwaysCreateDir(false);
extractor->setClearTargetPathContents(false);
QObject::connect(extractor, &FileExtractor::finishedChanged, this,
[this, downloader, bundleDir, extractor] {
[this, downloader, extractor] {
downloader->deleteLater();
extractor->deleteLater();
loadMaterialBundle(bundleDir);
loadMaterialBundle();
});
extractor->extract();
@@ -153,25 +152,25 @@ bool ContentLibraryMaterialsModel::fetchBundleIcons(const QDir &bundleDir)
return false;
}
bool ContentLibraryMaterialsModel::fetchBundleMetadata(const QDir &bundleDir)
bool ContentLibraryMaterialsModel::fetchBundleJsonFile()
{
QString matBundlePath = bundleDir.filePath("material_bundle.json");
Utils::FilePath jsonFilePath = m_bundlePath.pathAppended("material_bundle.json");
QFileInfo fi(matBundlePath);
if (fi.exists() && fi.size() > 0)
if (jsonFilePath.exists() && jsonFilePath.fileSize() > 0)
return true;
QString metaFileUrl = m_baseUrl + "/material_bundle.json";
QString bundleJsonUrl = m_baseUrl + "/material_bundle.json";
FileDownloader *downloader = new FileDownloader(this);
downloader->setUrl(metaFileUrl);
downloader->setUrl(bundleJsonUrl);
downloader->setProbeUrl(false);
downloader->setDownloadEnabled(true);
downloader->setTargetFilePath(matBundlePath);
downloader->setTargetFilePath(jsonFilePath.toFSPathString());
QObject::connect(downloader, &FileDownloader::finishedChanged, this,
[this, downloader, bundleDir] {
if (fetchBundleIcons(bundleDir))
loadMaterialBundle(bundleDir);
[this, downloader] {
if (fetchBundleIcons())
loadMaterialBundle();
downloader->deleteLater();
});
@@ -179,7 +178,7 @@ bool ContentLibraryMaterialsModel::fetchBundleMetadata(const QDir &bundleDir)
return false;
}
void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, const QStringList &)
void ContentLibraryMaterialsModel::downloadSharedFiles()
{
QString metaFileUrl = m_baseUrl + "/shared_files.zip";
FileDownloader *downloader = new FileDownloader(this);
@@ -188,11 +187,11 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co
downloader->setDownloadEnabled(true);
QObject::connect(downloader, &FileDownloader::finishedChanged, this,
[this, downloader, targetDir] {
[this, downloader] {
FileExtractor *extractor = new FileExtractor(this);
extractor->setArchiveName(downloader->completeBaseName());
extractor->setSourceFile(downloader->outputFile());
extractor->setTargetPath(targetDir.absolutePath());
extractor->setTargetPath(m_bundlePath.toFSPathString());
extractor->setAlwaysCreateDir(false);
extractor->setClearTargetPathContents(false);
@@ -212,11 +211,11 @@ QString ContentLibraryMaterialsModel::bundleId() const
return m_bundleId;
}
void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir)
void ContentLibraryMaterialsModel::loadMaterialBundle(bool forceReload)
{
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
if (m_bundleExists && m_bundleId == compUtils.materialsBundleId())
if (!forceReload && m_bundleExists && m_bundleId == compUtils.materialsBundleId())
return;
// clean up
@@ -227,18 +226,18 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir)
m_bundleObj = {};
m_bundleId.clear();
QString bundlePath = bundleDir.filePath("material_bundle.json");
Utils::FilePath jsonFilePath = m_bundlePath.pathAppended("material_bundle.json");
QFile bundleFile(bundlePath);
if (!bundleFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open material_bundle.json");
Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
if (!jsonContents.has_value()) {
qWarning() << __FUNCTION__ << jsonContents.error();
resetModel();
return;
}
QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll());
QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
if (bundleJsonDoc.isNull()) {
qWarning("Invalid material_bundle.json file");
qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
resetModel();
return;
}
@@ -258,38 +257,38 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir)
for (const QString &matName : matsNames) {
const QJsonObject matObj = matsObj.value(matName).toObject();
if (matObj.contains("minQtVersion")) {
const Version minQtVersion = Version::fromString(
matObj.value("minQtVersion").toString());
if (minQtVersion > m_quick3dVersion)
continue;
}
QStringList files;
const QJsonArray assetsArr = matObj.value("files").toArray();
for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString()));
QUrl icon = m_bundlePath.pathAppended(matObj.value("icon").toString()).toUrl();
QString qml = matObj.value("qml").toString();
TypeName type = QLatin1String("%1.%2")
.arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
TypeName type = QLatin1String("%1.%2").arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files,
m_downloadPath, m_baseUrl);
m_bundleId);
category->addBundleMaterial(bundleMat);
}
m_bundleCategories.append(category);
}
m_bundleSharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
for (const QJsonValueConstRef &file : sharedFilesArr)
m_bundleSharedFiles.append(file.toString());
QStringList missingSharedFiles;
for (const QString &s : std::as_const(m_bundleSharedFiles)) {
if (!QFileInfo::exists(bundleDir.filePath(s)))
missingSharedFiles.push_back(s);
m_bundleSharedFiles = m_bundleObj.value("sharedFiles").toVariant().toStringList();
for (const QString &file : std::as_const(m_bundleSharedFiles)) {
if (!m_bundlePath.pathAppended(file).exists()) {
downloadSharedFiles();
break;
}
}
if (missingSharedFiles.length() > 0)
downloadSharedFiles(bundleDir, missingSharedFiles);
m_bundleExists = true;
updateIsEmpty();
resetModel();
@@ -297,7 +296,7 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir)
bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
{
return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3;
return m_widget->hasQuick3DImport() && m_quick3dVersion >= Version{6, 3};
}
bool ContentLibraryMaterialsModel::matBundleExists() const
@@ -305,6 +304,11 @@ bool ContentLibraryMaterialsModel::matBundleExists() const
return m_bundleExists;
}
QString ContentLibraryMaterialsModel::bundlePath() const
{
return m_bundlePath.toFSPathString();
}
void ContentLibraryMaterialsModel::setSearchText(const QString &searchText)
{
QString lowerSearchText = searchText.toLower();
@@ -338,8 +342,11 @@ void ContentLibraryMaterialsModel::setQuick3DImportVersion(int major, int minor)
{
bool oldRequiredImport = hasRequiredQuick3DImport();
m_quick3dMajorVersion = major;
m_quick3dMinorVersion = minor;
const Version newVersion{major, minor};
if (m_quick3dVersion != newVersion) {
m_quick3dVersion = newVersion;
loadMaterialBundle(true);
}
bool newRequiredImport = hasRequiredQuick3DImport();
@@ -364,8 +371,8 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat,
void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat)
{
QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(),
mat->files() + m_bundleSharedFiles);
QString err = m_widget->importer()->importComponent(m_bundlePath.toFSPathString(), mat->type(),
mat->qml(), mat->files() + m_bundleSharedFiles);
if (err.isEmpty())
m_widget->setImporterRunning(true);
@@ -383,4 +390,9 @@ void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat
qWarning() << __FUNCTION__ << err;
}
bool ContentLibraryMaterialsModel::isMaterialDownloaded(ContentLibraryMaterial *mat) const
{
return m_bundlePath.pathAppended(mat->qml()).exists();
}
} // namespace QmlDesigner

View File

@@ -3,11 +3,12 @@
#pragma once
#include <utils/filepath.h>
#include <utils/version.h>
#include <QAbstractListModel>
#include <QJsonObject>
QT_FORWARD_DECLARE_CLASS(QDir)
namespace QmlDesigner {
class ContentLibraryMaterial;
@@ -21,6 +22,8 @@ class ContentLibraryMaterialsModel : public QAbstractListModel
Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
Q_PROPERTY(QString baseWebUrl MEMBER m_baseUrl CONSTANT)
Q_PROPERTY(QString bundlePath READ bundlePath CONSTANT)
public:
ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr);
@@ -37,6 +40,8 @@ public:
bool hasRequiredQuick3DImport() const;
bool matBundleExists() const;
QString bundlePath() const;
void resetModel();
void updateIsEmpty();
void loadBundle();
@@ -44,6 +49,7 @@ public:
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat);
Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat);
Q_INVOKABLE bool isMaterialDownloaded(QmlDesigner::ContentLibraryMaterial *mat) const;
QString bundleId() const;
@@ -55,11 +61,11 @@ signals:
void matBundleExistsChanged();
private:
void loadMaterialBundle(const QDir &matBundleDir);
bool fetchBundleIcons(const QDir &bundleDir);
bool fetchBundleMetadata(const QDir &bundleDir);
void loadMaterialBundle(bool forceReload = false);
bool fetchBundleIcons();
bool fetchBundleJsonFile();
bool isValidIndex(int idx) const;
void downloadSharedFiles(const QDir &targetDir, const QStringList &files);
void downloadSharedFiles();
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
@@ -71,10 +77,8 @@ private:
bool m_isEmpty = true;
bool m_bundleExists = false;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
QString m_downloadPath;
Version m_quick3dVersion;
Utils::FilePath m_bundlePath;
QString m_baseUrl;
};

View File

@@ -78,17 +78,17 @@ QString ContentLibraryTexture::resolveToolTipText()
if (m_suffix.isEmpty())
return m_baseName; // empty suffix means we have just the icon and no other data
QString fileName = m_baseName + m_suffix;
QString texFileName = fileName();
QString imageInfo;
if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) {
imageInfo = ImageUtils::imageInfoString(m_dimensions, m_sizeInBytes);
} else {
QString fullDownloadPath = m_dirPath + '/' + fileName;
QString fullDownloadPath = m_dirPath + '/' + texFileName;
imageInfo = ImageUtils::imageInfoString(fullDownloadPath);
}
return QString("%1\n%2").arg(fileName, imageInfo);
return QString("%1\n%2").arg(texFileName, imageInfo);
}
bool ContentLibraryTexture::isDownloaded() const
@@ -98,7 +98,7 @@ bool ContentLibraryTexture::isDownloaded() const
QString ContentLibraryTexture::texturePath() const
{
return m_dirPath + '/' + m_baseName + m_suffix;
return m_dirPath + '/' + fileName();
}
void ContentLibraryTexture::setDownloaded()
@@ -135,6 +135,11 @@ QString ContentLibraryTexture::textureKey() const
return m_textureKey;
}
QString ContentLibraryTexture::fileName() const
{
return m_baseName + m_suffix;
}
void ContentLibraryTexture::setHasUpdate(bool value)
{
if (m_hasUpdate != value) {

View File

@@ -24,7 +24,7 @@ class ContentLibraryTexture : public QObject
Q_PROPERTY(bool textureHasUpdate WRITE setHasUpdate READ hasUpdate NOTIFY hasUpdateChanged)
Q_PROPERTY(bool textureIsNew MEMBER m_isNew CONSTANT)
Q_PROPERTY(QString textureKey MEMBER m_textureKey CONSTANT)
Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
Q_PROPERTY(QString bundleId MEMBER m_bundleId CONSTANT)
public:
ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &dirPath,
@@ -42,6 +42,7 @@ public:
QString texturePath() const;
QString parentDirPath() const;
QString textureKey() const;
QString fileName() const;
void setHasUpdate(bool value);
bool hasUpdate() const;
@@ -74,7 +75,7 @@ private:
bool m_visible = true;
bool m_hasUpdate = false;
bool m_isNew = false;
const QString m_itemType = "texture";
const QString m_bundleId = "UserTextures";
};
} // namespace QmlDesigner

View File

@@ -2,11 +2,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "contentlibraryusermodel.h"
#include "useritemcategory.h"
#include "usertexturecategory.h"
#include "contentlibrarybundleimporter.h"
#include "contentlibraryitem.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialscategory.h"
#include "contentlibrarytexture.h"
#include "contentlibrarywidget.h"
@@ -14,7 +14,6 @@
#include <imageutils.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <uniquename.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
@@ -30,7 +29,7 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent)
: QAbstractListModel(parent)
, m_widget(parent)
{
m_userCategories = {tr("Materials"), tr("Textures"), tr("3D"), /*tr("Effects"), tr("2D components")*/}; // TODO
createCategories();
}
int ContentLibraryUserModel::rowCount(const QModelIndex &) const
@@ -43,218 +42,147 @@ QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const
QTC_ASSERT(index.isValid() && index.row() < m_userCategories.size(), return {});
QTC_ASSERT(roleNames().contains(role), return {});
if (role == NameRole)
return m_userCategories.at(index.row());
UserCategory *currCat = m_userCategories.at(index.row());
if (role == ItemsRole) {
if (index.row() == MaterialsSectionIdx)
return QVariant::fromValue(m_userMaterials);
if (index.row() == TexturesSectionIdx)
return QVariant::fromValue(m_userTextures);
if (index.row() == Items3DSectionIdx)
return QVariant::fromValue(m_user3DItems);
if (index.row() == EffectsSectionIdx)
return QVariant::fromValue(m_userEffects);
}
if (role == TitleRole)
return currCat->title();
if (role == NoMatchRole) {
if (index.row() == MaterialsSectionIdx)
return m_noMatchMaterials;
if (index.row() == TexturesSectionIdx)
return m_noMatchTextures;
if (index.row() == Items3DSectionIdx)
return m_noMatch3D;
if (index.row() == EffectsSectionIdx)
return m_noMatchEffects;
}
if (role == ItemsRole)
return QVariant::fromValue(currCat->items());
if (role == VisibleRole) {
if (index.row() == MaterialsSectionIdx)
return !m_userMaterials.isEmpty();
if (index.row() == TexturesSectionIdx)
return !m_userTextures.isEmpty();
if (index.row() == Items3DSectionIdx)
return !m_user3DItems.isEmpty();
if (index.row() == EffectsSectionIdx)
return !m_userEffects.isEmpty();
}
if (role == NoMatchRole)
return currCat->noMatch();
if (role == EmptyRole)
return currCat->isEmpty();
return {};
}
bool ContentLibraryUserModel::isValidIndex(int idx) const
void ContentLibraryUserModel::createCategories()
{
return idx > -1 && idx < rowCount();
QTC_ASSERT(m_userCategories.isEmpty(), return);
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
auto userBundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User");
auto catMaterial = new UserItemCategory{tr("Materials"),
userBundlePath.pathAppended("materials"),
compUtils.userMaterialsBundleId()};
auto catTexture = new UserTextureCategory{tr("Textures"), userBundlePath.pathAppended("textures")};
auto cat3D = new UserItemCategory{tr("3D"), userBundlePath.pathAppended("3d"),
compUtils.user3DBundleId()};
m_userCategories << catMaterial << catTexture << cat3D;
}
void ContentLibraryUserModel::updateNoMatchMaterials()
{
m_noMatchMaterials = Utils::allOf(m_userMaterials, [&](ContentLibraryMaterial *item) {
return !item->visible();
});
}
void ContentLibraryUserModel::updateNoMatchTextures()
{
m_noMatchTextures = Utils::allOf(m_userTextures, [&](ContentLibraryTexture *item) {
return !item->visible();
});
}
void ContentLibraryUserModel::updateNoMatch3D()
{
m_noMatch3D = Utils::allOf(m_user3DItems, [&](ContentLibraryItem *item) {
return !item->visible();
});
}
void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml,
const QUrl &icon, const QStringList &files)
void ContentLibraryUserModel::addItem(const QString &bundleId, const QString &name,
const QString &qml, const QUrl &icon, const QStringList &files)
{
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
QString typePrefix = compUtils.userMaterialsBundleType();
QString typePrefix = compUtils.userBundleType(bundleId);
TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files,
Paths::bundlesPathSetting().append("/User/materials"));
m_userMaterials.append(libMat);
SectionIndex sectionIndex = bundleIdToSectionIndex(bundleId);
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
UserCategory *cat = m_userCategories[sectionIndex];
cat->addItem(new ContentLibraryItem(cat, name, qml, type, icon, files, bundleId));
updateIsEmpty();
}
void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml,
const QUrl &icon, const QStringList &files)
void ContentLibraryUserModel::refreshSection(const QString &bundleId)
{
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
QString typePrefix = compUtils.user3DBundleType();
TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
m_user3DItems.append(new ContentLibraryItem(this, name, qml, type, icon, files));
SectionIndex sectionIdx = bundleIdToSectionIndex(bundleId);
emit dataChanged(index(sectionIdx), index(sectionIdx), {ItemsRole, EmptyRole});
updateIsEmpty();
}
void ContentLibraryUserModel::refresh3DSection()
void ContentLibraryUserModel::addTextures(const Utils::FilePaths &paths)
{
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
auto texCat = qobject_cast<UserTextureCategory *>(m_userCategories[TexturesSectionIdx]);
QTC_ASSERT(texCat, return);
texCat->addItems(paths);
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx), {ItemsRole, EmptyRole});
updateIsEmpty();
}
void ContentLibraryUserModel::addTextures(const QStringList &paths)
void ContentLibraryUserModel::removeTextures(const QStringList &fileNames)
{
QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"};
bundleDir.mkpath(".");
bundleDir.mkdir("icons");
// note: this method doesn't refresh the model after textures removal
for (const QString &path : paths) {
QFileInfo fileInfo(path);
QString suffix = '.' + fileInfo.suffix();
auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png"));
QPair<QSize, qint64> info = ImageUtils::imageInfo(path);
QString dirPath = fileInfo.path();
QSize imgDims = info.first;
qint64 imgFileSize = info.second;
auto texCat = qobject_cast<UserTextureCategory *>(m_userCategories[TexturesSectionIdx]);
QTC_ASSERT(texCat, return);
auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
m_userTextures.append(tex);
const QObjectList items = texCat->items();
for (QObject *item : items) {
ContentLibraryTexture *castedItem = qobject_cast<ContentLibraryTexture *>(item);
QTC_ASSERT(castedItem, continue);
if (fileNames.contains(castedItem->fileName()))
removeTexture(castedItem, false);
}
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
}
void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem)
{
QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(),
bundleItem->qml(),
bundleItem->files() + m_bundle3DSharedFiles);
if (err.isEmpty())
m_widget->setImporterRunning(true);
else
qWarning() << __FUNCTION__ << err;
}
void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex)
void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex, bool refresh)
{
// remove resources
Utils::FilePath::fromString(tex->texturePath()).removeFile();
Utils::FilePath::fromString(tex->iconPath()).removeFile();
// remove from model
m_userTextures.removeOne(tex);
tex->deleteLater();
m_userCategories[TexturesSectionIdx]->removeItem(tex);
// update model
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
if (refresh) {
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
updateIsEmpty();
}
}
void ContentLibraryUserModel::removeFromContentLib(QObject *item)
{
if (auto mat = qobject_cast<ContentLibraryMaterial *>(item))
removeMaterialFromContentLib(mat);
else if (auto itm = qobject_cast<ContentLibraryItem *>(item))
remove3DFromContentLib(itm);
auto castedItem = qobject_cast<ContentLibraryItem *>(item);
QTC_ASSERT(castedItem, return);
removeItem(castedItem);
}
void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *item)
void ContentLibraryUserModel::removeItemByName(const QString &qmlFileName, const QString &bundleId)
{
auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
ContentLibraryItem *itemToRemove = nullptr;
const QObjectList items = m_userCategories[bundleIdToSectionIndex(bundleId)]->items();
QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray();
for (QObject *item : items) {
ContentLibraryItem *castedItem = qobject_cast<ContentLibraryItem *>(item);
QTC_ASSERT(castedItem, return);
// remove qml and icon files
Utils::FilePath::fromString(item->qmlFilePath()).removeFile();
Utils::FilePath::fromUrl(item->icon()).removeFile();
// remove from the bundle json file
for (int i = 0; i < itemsArr.size(); ++i) {
if (itemsArr.at(i).toObject().value("qml") == item->qml()) {
itemsArr.removeAt(i);
if (castedItem->qml() == qmlFileName) {
itemToRemove = castedItem;
break;
}
}
m_bundleObjMaterial.insert("items", itemsArr);
auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
.writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson());
if (!result)
qWarning() << __FUNCTION__ << result.error();
// delete dependency files if they are only used by the deleted material
QStringList allFiles;
for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr))
allFiles.append(itemRef.toObject().value("files").toVariant().toStringList());
const QStringList itemFiles = item->files();
for (const QString &file : itemFiles) {
if (allFiles.count(file) == 0) // only used by the deleted item
bundlePath.pathAppended(file).removeFile();
}
// remove from model
m_userMaterials.removeOne(item);
item->deleteLater();
// update model
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
}
void ContentLibraryUserModel::remove3DFromContentLibByName(const QString &qmlFileName)
{
ContentLibraryItem *itemToRemove = Utils::findOr(m_user3DItems, nullptr,
[&qmlFileName](ContentLibraryItem *item) {
return item->qml() == qmlFileName;
});
if (itemToRemove)
remove3DFromContentLib(itemToRemove);
removeItem(itemToRemove);
}
void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item)
void ContentLibraryUserModel::removeItem(ContentLibraryItem *item)
{
QJsonArray itemsArr = m_bundleObj3D.value("items").toArray();
UserItemCategory *itemCat = qobject_cast<UserItemCategory *>(item->parent());
QTC_ASSERT(itemCat, return);
Utils::FilePath bundlePath = itemCat->bundlePath();
QJsonObject &bundleObj = itemCat->bundleObjRef();
QJsonArray itemsArr = bundleObj.value("items").toArray();
// remove qml and icon files
m_bundlePath3D.pathAppended(item->qml()).removeFile();
bundlePath.pathAppended(item->qml()).removeFile();
Utils::FilePath::fromUrl(item->icon()).removeFile();
// remove from the bundle json file
@@ -264,10 +192,10 @@ void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item)
break;
}
}
m_bundleObj3D.insert("items", itemsArr);
bundleObj.insert("items", itemsArr);
auto result = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME)
.writeFileContents(QJsonDocument(m_bundleObj3D).toJson());
auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
.writeFileContents(QJsonDocument(bundleObj).toJson());
if (!result)
qWarning() << __FUNCTION__ << result.error();
@@ -279,276 +207,60 @@ void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item)
const QStringList itemFiles = item->files();
for (const QString &file : itemFiles) {
if (allFiles.count(file) == 0) // only used by the deleted item
m_bundlePath3D.pathAppended(file).removeFile();
bundlePath.pathAppended(file).removeFile();
}
// remove from model
m_user3DItems.removeOne(item);
item->deleteLater();
itemCat->removeItem(item);
// update model
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
SectionIndex sectionIdx = bundleIdToSectionIndex(item->bundleId());
emit dataChanged(index(sectionIdx), index(sectionIdx), {ItemsRole, EmptyRole});
updateIsEmpty();
}
/**
* @brief Gets unique Qml component and icon file material names from a given name
* @param defaultName input name
* @return <Qml, icon> file names
*/
QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNames(const QString &defaultName) const
ContentLibraryUserModel::SectionIndex ContentLibraryUserModel::bundleIdToSectionIndex(
const QString &bundleId) const
{
return getUniqueLibItemNames(defaultName, m_bundleObjMaterial);
}
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
/**
* @brief Gets unique Qml component and icon file 3d item names from a given name
* @param defaultName input name
* @return <Qml, icon> file names
*/
QPair<QString, QString> ContentLibraryUserModel::getUniqueLib3DNames(const QString &defaultName) const
{
return getUniqueLibItemNames(defaultName, m_bundleObj3D);
}
if (bundleId == compUtils.userMaterialsBundleId())
return MaterialsSectionIdx;
QPair<QString, QString> ContentLibraryUserModel::getUniqueLibItemNames(const QString &defaultName,
const QJsonObject &bundleObj) const
{
QTC_ASSERT(!bundleObj.isEmpty(), return {});
if (bundleId == compUtils.user3DBundleId())
return Items3DSectionIdx;
const QJsonArray itemsArr = bundleObj.value("items").toArray();
if (bundleId == compUtils.userEffectsBundleId())
return EffectsSectionIdx;
QStringList itemQmls, itemIcons;
for (const QJsonValueConstRef &itemRef : itemsArr) {
const QJsonObject &obj = itemRef.toObject();
itemQmls.append(obj.value("qml").toString().chopped(4)); // remove .qml
itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName());
}
QString baseQml = UniqueName::generateId(defaultName);
baseQml[0] = baseQml.at(0).toUpper();
baseQml.prepend("My");
QString uniqueQml = UniqueName::generate(baseQml, [&] (const QString &name) {
return itemQmls.contains(name);
});
QString uniqueIcon = UniqueName::generate(defaultName, [&] (const QString &name) {
return itemIcons.contains(name);
});
return {uniqueQml + ".qml", uniqueIcon + ".png"};
qWarning() << __FUNCTION__ << "Invalid section index for bundleId:" << bundleId;
return {};
}
QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const
{
static const QHash<int, QByteArray> roles {
{NameRole, "categoryName"},
{VisibleRole, "categoryVisible"},
{TitleRole, "categoryTitle"},
{EmptyRole, "categoryEmpty"},
{ItemsRole, "categoryItems"},
{NoMatchRole, "categoryNoMatch"}
};
return roles;
}
QJsonObject &ContentLibraryUserModel::bundleJsonMaterialObjectRef()
QJsonObject &ContentLibraryUserModel::bundleObjectRef(const QString &bundleId)
{
return m_bundleObjMaterial;
auto secIdx = bundleIdToSectionIndex(bundleId);
return qobject_cast<UserItemCategory *>(m_userCategories[secIdx])->bundleObjRef();
}
QJsonObject &ContentLibraryUserModel::bundleJson3DObjectRef()
void ContentLibraryUserModel::loadBundles(bool force)
{
return m_bundleObj3D;
}
for (UserCategory *cat : std::as_const(m_userCategories))
cat->loadBundle(force);
void ContentLibraryUserModel::loadBundles()
{
loadMaterialBundle();
load3DBundle();
loadTextureBundle();
}
void ContentLibraryUserModel::loadMaterialBundle()
{
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId())
return;
// clean up
qDeleteAll(m_userMaterials);
m_userMaterials.clear();
m_matBundleExists = false;
m_noMatchMaterials = true;
m_bundleObjMaterial = {};
m_bundleIdMaterial.clear();
m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials");
m_bundlePathMaterial.ensureWritableDir();
m_bundlePathMaterial.pathAppended("icons").ensureWritableDir();
auto jsonFilePath = m_bundlePathMaterial.pathAppended(Constants::BUNDLE_JSON_FILENAME);
if (!jsonFilePath.exists()) {
QString jsonContent = "{\n";
jsonContent += " \"id\": \"UserMaterials\",\n";
jsonContent += " \"items\": []\n";
jsonContent += "}";
Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent.toLatin1());
if (!res.has_value()) {
qWarning() << __FUNCTION__ << res.error();
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
return;
}
}
Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
if (!jsonContents.has_value()) {
qWarning() << __FUNCTION__ << jsonContents.error();
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
return;
}
QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
if (bundleJsonDoc.isNull()) {
qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
return;
}
m_bundleIdMaterial = compUtils.userMaterialsBundleId();
m_bundleObjMaterial = bundleJsonDoc.object();
m_bundleObjMaterial["id"] = m_bundleIdMaterial;
// parse items
QString typePrefix = compUtils.userMaterialsBundleType();
const QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray();
for (const QJsonValueConstRef &itemRef : itemsArr) {
const QJsonObject itemObj = itemRef.toObject();
QString name = itemObj.value("name").toString();
QString qml = itemObj.value("qml").toString();
TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
QUrl icon = m_bundlePathMaterial.pathAppended(itemObj.value("icon").toString()).toUrl();
QStringList files;
const QJsonArray assetsArr = itemObj.value("files").toArray();
for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
m_userMaterials.append(new ContentLibraryMaterial(this, name, qml, type, icon, files,
m_bundlePathMaterial.path(), ""));
}
m_bundleMaterialSharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObjMaterial.value("sharedFiles").toArray();
for (const QJsonValueConstRef &file : sharedFilesArr)
m_bundleMaterialSharedFiles.append(file.toString());
m_matBundleExists = true;
updateNoMatchMaterials();
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
}
void ContentLibraryUserModel::load3DBundle()
{
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
if (m_bundle3DExists && m_bundleId3D == compUtils.user3DBundleId())
return;
// clean up
qDeleteAll(m_user3DItems);
m_user3DItems.clear();
m_bundle3DExists = false;
m_noMatch3D = true;
m_bundleObj3D = {};
m_bundleId3D.clear();
m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d");
m_bundlePath3D.ensureWritableDir();
m_bundlePath3D.pathAppended("icons").ensureWritableDir();
auto jsonFilePath = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME);
if (!jsonFilePath.exists()) {
QByteArray jsonContent = "{\n";
jsonContent += " \"id\": \"User3D\",\n";
jsonContent += " \"items\": []\n";
jsonContent += "}";
Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent);
if (!res.has_value()) {
qWarning() << __FUNCTION__ << res.error();
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
return;
}
}
Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
if (!jsonContents.has_value()) {
qWarning() << __FUNCTION__ << jsonContents.error();
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
return;
}
QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
if (bundleJsonDoc.isNull()) {
qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
return;
}
m_bundleId3D = compUtils.user3DBundleId();
m_bundleObj3D = bundleJsonDoc.object();
m_bundleObj3D["id"] = m_bundleId3D;
// parse items
QString typePrefix = compUtils.user3DBundleType();
const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray();
for (const QJsonValueConstRef &itemRef : itemsArr) {
const QJsonObject itemObj = itemRef.toObject();
QString name = itemObj.value("name").toString();
QString qml = itemObj.value("qml").toString();
TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl();
QStringList files;
const QJsonArray assetsArr = itemObj.value("files").toArray();
for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
m_user3DItems.append(new ContentLibraryItem(nullptr, name, qml, type, icon, files));
}
m_bundle3DSharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObj3D.value("sharedFiles").toArray();
for (const QJsonValueConstRef &file : sharedFilesArr)
m_bundle3DSharedFiles.append(file.toString());
m_bundle3DExists = true;
updateNoMatch3D();
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
}
void ContentLibraryUserModel::loadTextureBundle()
{
if (!m_userTextures.isEmpty())
return;
QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"};
bundleDir.mkpath(".");
bundleDir.mkdir("icons");
const QFileInfoList fileInfos = bundleDir.entryInfoList(QDir::Files);
for (const QFileInfo &fileInfo : fileInfos) {
QString suffix = '.' + fileInfo.suffix();
auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png"));
QPair<QSize, qint64> info = ImageUtils::imageInfo(fileInfo.path());
QString dirPath = fileInfo.path();
QSize imgDims = info.first;
qint64 imgFileSize = info.second;
auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
m_userTextures.append(tex);
}
updateNoMatchTextures();
emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
resetModel();
updateIsEmpty();
}
bool ContentLibraryUserModel::hasRequiredQuick3DImport() const
@@ -556,9 +268,17 @@ bool ContentLibraryUserModel::hasRequiredQuick3DImport() const
return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3;
}
bool ContentLibraryUserModel::matBundleExists() const
{
return m_matBundleExists;
void ContentLibraryUserModel::updateIsEmpty() {
bool newIsEmpty = Utils::allOf(std::as_const(m_userCategories), [](UserCategory *cat) {
return cat->isEmpty();
});
if (m_isEmpty == newIsEmpty)
return;
m_isEmpty = newIsEmpty;
emit isEmptyChanged();
}
void ContentLibraryUserModel::setSearchText(const QString &searchText)
@@ -570,39 +290,46 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText)
m_searchText = lowerSearchText;
for (ContentLibraryMaterial *item : std::as_const(m_userMaterials))
item->filter(m_searchText);
for (UserCategory *cat : std::as_const(m_userCategories))
cat->filter(m_searchText);
for (ContentLibraryTexture *item : std::as_const(m_userTextures))
item->filter(m_searchText);
for (ContentLibraryItem *item : std::as_const(m_user3DItems))
item->filter(m_searchText);
updateNoMatchMaterials();
updateNoMatchTextures();
updateNoMatch3D();
resetModel();
}
void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems)
void ContentLibraryUserModel::updateImportedState(const QStringList &importedItems,
const QString &bundleId)
{
SectionIndex secIdx = bundleIdToSectionIndex(bundleId);
UserItemCategory *cat = qobject_cast<UserItemCategory *>(m_userCategories.at(secIdx));
const QObjectList items = cat->items();
bool changed = false;
for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials))
changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4)));
for (QObject *item : items) {
ContentLibraryItem *castedItem = qobject_cast<ContentLibraryItem *>(item);
changed |= castedItem->setImported(importedItems.contains(castedItem->qml().chopped(4)));
}
if (changed)
emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
emit dataChanged(index(secIdx), index(secIdx), {ItemsRole});
}
void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems)
bool ContentLibraryUserModel::jsonPropertyExists(const QString &propName, const QString &propValue,
const QString &bundleId) const
{
bool changed = false;
for (ContentLibraryItem *item : std::as_const(m_user3DItems))
changed |= item->setImported(importedItems.contains(item->qml().chopped(4)));
SectionIndex secIdx = bundleIdToSectionIndex(bundleId);
UserItemCategory *cat = qobject_cast<UserItemCategory *>(m_userCategories.at(secIdx));
QTC_ASSERT(cat, return false);
if (changed)
emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
QJsonObject &bundleObj = cat->bundleObjRef();
const QJsonArray itemsArr = bundleObj.value("items").toArray();
for (const QJsonValueConstRef &itemRef : itemsArr) {
const QJsonObject &obj = itemRef.toObject();
if (obj.value(propName).toString() == propValue)
return true;
}
return false;
}
void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor)
@@ -626,33 +353,22 @@ void ContentLibraryUserModel::resetModel()
endResetModel();
}
void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool add)
void ContentLibraryUserModel::applyToSelected(ContentLibraryItem *mat, bool add)
{
emit applyToSelectedTriggered(mat, add);
}
void ContentLibraryUserModel::addToProject(QObject *item)
void ContentLibraryUserModel::addToProject(ContentLibraryItem *item)
{
QString bundleDir;
TypeName type;
QString qmlFile;
QStringList files;
if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) {
bundleDir = mat->dirPath();
type = mat->type();
qmlFile = mat->qml();
files = mat->files() + m_bundleMaterialSharedFiles;
} else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) {
bundleDir = m_bundlePath3D.toString();
type = itm->type();
qmlFile = itm->qml();
files = itm->files() + m_bundle3DSharedFiles;
} else {
qWarning() << __FUNCTION__ << "Unsupported Item";
return;
}
UserItemCategory *itemCat = qobject_cast<UserItemCategory *>(item->parent());
QTC_ASSERT(itemCat, return);
QString err = m_widget->importer()->importComponent(bundleDir, type, qmlFile, files);
QString bundlePath = itemCat->bundlePath().toFSPathString();
TypeName type = item->type();
QString qmlFile = item->qml();
QStringList files = item->files() + itemCat->sharedFiles();
QString err = m_widget->importer()->importComponent(bundlePath, type, qmlFile, files);
if (err.isEmpty())
m_widget->setImporterRunning(true);
@@ -662,20 +378,8 @@ void ContentLibraryUserModel::addToProject(QObject *item)
void ContentLibraryUserModel::removeFromProject(QObject *item)
{
TypeName type;
QString qml;
if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) {
type = mat->type();
qml = mat->qml();
} else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) {
type = itm->type();
qml = itm->qml();
} else {
qWarning() << __FUNCTION__ << "Unsupported Item";
return;
}
QString err = m_widget->importer()->unimportComponent(type, qml);
auto castedItem = qobject_cast<ContentLibraryItem *>(item);
QString err = m_widget->importer()->unimportComponent(castedItem->type(), castedItem->qml());
if (err.isEmpty())
m_widget->setImporterRunning(true);

View File

@@ -3,6 +3,8 @@
#pragma once
#include "usercategory.h"
#include <utils/filepath.h>
#include <QAbstractListModel>
@@ -13,7 +15,6 @@ QT_FORWARD_DECLARE_CLASS(QUrl)
namespace QmlDesigner {
class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryTexture;
class ContentLibraryWidget;
class NodeMetaInfo;
@@ -22,13 +23,8 @@ class ContentLibraryUserModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
Q_PROPERTY(QList<ContentLibraryMaterial *> userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged)
Q_PROPERTY(QList<ContentLibraryTexture *> userTextures MEMBER m_userTextures NOTIFY userTexturesChanged)
Q_PROPERTY(QList<ContentLibraryItem *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged)
Q_PROPERTY(QList<ContentLibraryItem *> userEffects MEMBER m_userEffects NOTIFY userEffectsChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
public:
ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr);
@@ -38,98 +34,68 @@ public:
QHash<int, QByteArray> roleNames() const override;
void setSearchText(const QString &searchText);
void updateMaterialsImportedState(const QStringList &importedItems);
void update3DImportedState(const QStringList &importedItems);
void updateImportedState(const QStringList &importedItems, const QString &bundleId);
QPair<QString, QString> getUniqueLibMaterialNames(const QString &defaultName = "Material") const;
QPair<QString, QString> getUniqueLib3DNames(const QString &defaultName = "Item") const;
bool jsonPropertyExists(const QString &propName, const QString &propValue,
const QString &bundleId) const;
void setQuick3DImportVersion(int major, int minor);
bool hasRequiredQuick3DImport() const;
bool matBundleExists() const;
void updateIsEmpty();
void resetModel();
void updateNoMatchMaterials();
void updateNoMatchTextures();
void updateNoMatch3D();
void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
void refresh3DSection();
void addTextures(const QStringList &paths);
void addItem(const QString &bundleId, const QString &name, const QString &qml,const QUrl &icon,
const QStringList &files);
void refreshSection(const QString &bundleId);
void addTextures(const Utils::FilePaths &paths);
void removeTextures(const QStringList &fileNames);
void add3DInstance(ContentLibraryItem *bundleItem);
void remove3DFromContentLibByName(const QString &qmlFileName);
void removeItemByName(const QString &qmlFileName, const QString &bundleId);
void setBundleObj(const QJsonObject &newBundleObj);
QJsonObject &bundleJsonMaterialObjectRef();
QJsonObject &bundleJson3DObjectRef();
QJsonObject &bundleObjectRef(const QString &bundleId);
void loadBundles();
void loadBundles(bool force = false);
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
Q_INVOKABLE void addToProject(QObject *item);
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryItem *mat, bool add = false);
Q_INVOKABLE void addToProject(ContentLibraryItem *item);
Q_INVOKABLE void removeFromProject(QObject *item);
Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex);
Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex, bool refresh = true);
Q_INVOKABLE void removeFromContentLib(QObject *item);
signals:
void hasRequiredQuick3DImportChanged();
void userMaterialsChanged();
void userTexturesChanged();
void user3DItemsChanged();
void userEffectsChanged();
void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
void matBundleExistsChanged();
void bundle3DExistsChanged();
void isEmptyChanged();
void applyToSelectedTriggered(QmlDesigner::ContentLibraryItem *mat, bool add = false);
private:
// section indices must match the order in initModel()
enum SectionIndex { MaterialsSectionIdx = 0,
TexturesSectionIdx,
Items3DSectionIdx,
EffectsSectionIdx };
void createCategories();
void loadMaterialBundle();
void load3DBundle();
void loadTextureBundle();
bool isValidIndex(int idx) const;
void removeMaterialFromContentLib(ContentLibraryMaterial *mat);
void remove3DFromContentLib(ContentLibraryItem *item);
QPair<QString, QString> getUniqueLibItemNames(const QString &defaultName,
const QJsonObject &bundleObj) const;
void removeItem(ContentLibraryItem *item);
SectionIndex bundleIdToSectionIndex(const QString &bundleId) const;
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
QString m_bundleIdMaterial;
QString m_bundleId3D;
QStringList m_bundleMaterialSharedFiles;
QStringList m_bundle3DSharedFiles;
Utils::FilePath m_bundlePathMaterial;
Utils::FilePath m_bundlePath3D;
QList<ContentLibraryMaterial *> m_userMaterials;
QList<ContentLibraryTexture *> m_userTextures;
QList<ContentLibraryItem *> m_userEffects;
QList<ContentLibraryItem *> m_user3DItems;
QStringList m_userCategories;
QList<UserCategory *> m_userCategories;
QJsonObject m_bundleObjMaterial;
QJsonObject m_bundleObj3D;
bool m_noMatchMaterials = true;
bool m_noMatchTextures = true;
bool m_noMatch3D = true;
bool m_noMatchEffects = true;
bool m_matBundleExists = false;
bool m_bundle3DExists = false;
bool m_isEmpty = true;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole, NoMatchRole };
enum Roles { TitleRole = Qt::UserRole + 1, ItemsRole, EmptyRole, NoMatchRole };
};
} // namespace QmlDesigner

View File

@@ -8,10 +8,16 @@
#include <createtexture.h>
#include <nodemetainfo.h>
#include <utils/filepath.h>
#include <QObject>
#include <QPointer>
class ZipWriter;
QT_FORWARD_DECLARE_CLASS(QImage)
QT_FORWARD_DECLARE_CLASS(QPixmap)
QT_FORWARD_DECLARE_CLASS(QTemporaryDir)
namespace QmlDesigner {
@@ -21,6 +27,27 @@ class ContentLibraryTexture;
class ContentLibraryWidget;
class Model;
struct AssetPath
{
QString basePath;
QString relativePath;
Utils::FilePath absFilPath() const
{
return Utils::FilePath::fromString(basePath).pathAppended(relativePath);
}
bool operator==(const AssetPath &other) const
{
return basePath == other.basePath && relativePath == other.relativePath;
}
friend size_t qHash(const AssetPath &asset)
{
return ::qHash(asset.relativePath);
}
};
class ContentLibraryView : public AbstractView
{
Q_OBJECT
@@ -48,6 +75,9 @@ public:
void auxiliaryDataChanged(const ModelNode &node,
AuxiliaryDataKeyView type,
const QVariant &data) override;
void modelNodePreviewPixmapChanged(const ModelNode &node,
const QPixmap &pixmap,
const QByteArray &requestId) override;
private:
void connectImporter();
@@ -55,14 +85,22 @@ private:
bool isItemBundle(const QString &bundleId) const;
void active3DSceneChanged(qint32 sceneId);
void updateBundlesQuick3DVersion();
void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap);
void addLibAssets(const QStringList &paths);
void addLib3DComponent(const ModelNode &node);
void addLib3DItem(const ModelNode &node);
void genAndSaveIcon(const QString &qmlPath, const QString &iconPath);
QStringList writeLibItemQml(const ModelNode &node, const QString &qml);
QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds,
int depth = 0);
void exportLib3DComponent(const ModelNode &node);
void addLibItem(const ModelNode &node, const QPixmap &iconPixmap = {});
void exportLibItem(const ModelNode &node, const QPixmap &iconPixmap = {});
void importBundleToContentLib();
void importBundleToProject();
void getImageFromCache(const QString &qmlPath,
std::function<void(const QImage &image)> successCallback);
QSet<AssetPath> getBundleComponentDependencies(const ModelNode &node) const;
QString getExportPath(const ModelNode &node) const;
QString getImportPath() const;
QString nodeNameToComponentFileName(const QString &name) const;
QPair<QString, QSet<AssetPath>> modelNodeToQmlString(const ModelNode &node, int depth = 0);
void addIconAndCloseZip(const auto &image);
void saveIconToBundle(const auto &image);
#ifdef QDS_USE_PROJECTSTORAGE
void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {});
@@ -89,6 +127,15 @@ private:
bool m_hasQuick3DImport = false;
qint32 m_sceneId = -1;
CreateTexture m_createTexture;
Utils::FilePath m_iconSavePath;
QString m_generatedFolderName;
QString m_bundleId;
std::unique_ptr<ZipWriter> m_zipWriter;
std::unique_ptr<QTemporaryDir> m_tempDir;
static constexpr char BUNDLE_VERSION[] = "1.0";
static constexpr char ADD_ITEM_REQ_ID[] = "AddItemReqId";
static constexpr char EXPORT_ITEM_REQ_ID[] = "ExportItemReqId";
};
} // namespace QmlDesigner

View File

@@ -5,12 +5,12 @@
#include "contentlibrarybundleimporter.h"
#include "contentlibraryeffectsmodel.h"
#include "contentlibraryiconprovider.h"
#include "contentlibraryitem.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialsmodel.h"
#include "contentlibrarytexture.h"
#include "contentlibrarytexturesmodel.h"
#include "contentlibraryiconprovider.h"
#include "contentlibraryusermodel.h"
#include <coreplugin/icore.h>
@@ -84,7 +84,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
} else if (m_materialToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPosition().toPoint() - m_dragStartPoint).manhattanLength() > 20
&& m_materialToDrag->isDownloaded()) {
&& m_materialsModel->isMaterialDownloaded(m_materialToDrag)) {
QByteArray data;
QMimeData *mimeData = new QMimeData;
QDataStream stream(&data, QIODevice::WriteOnly);
@@ -123,7 +123,8 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
}
ContentLibraryWidget::ContentLibraryWidget()
: m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this))
: m_iconProvider(Utils::makeUniqueObjectPtr<ContentLibraryIconProvider>())
, m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this))
, m_materialsModel(new ContentLibraryMaterialsModel(this))
, m_texturesModel(new ContentLibraryTexturesModel("Textures", this))
, m_environmentsModel(new ContentLibraryTexturesModel("Environments", this))
@@ -138,8 +139,7 @@ ContentLibraryWidget::ContentLibraryWidget()
m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_CONTENT_LIBRARY);
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_quickWidget->engine()->addImageProvider(QStringLiteral("contentlibrary"),
new Internal::ContentLibraryIconProvider);
m_quickWidget->engine()->addImageProvider("contentlibrary", m_iconProvider.get());
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
@@ -181,6 +181,10 @@ ContentLibraryWidget::ContentLibraryWidget()
createImporter();
}
ContentLibraryWidget::~ContentLibraryWidget()
{
}
void ContentLibraryWidget::createImporter()
{
m_importer = new ContentLibraryBundleImporter();
@@ -212,6 +216,11 @@ void ContentLibraryWidget::createImporter()
});
}
ContentLibraryIconProvider *ContentLibraryWidget::iconProvider() const
{
return m_iconProvider.get();
}
void ContentLibraryWidget::updateImportedState(const QString &bundleId)
{
if (!m_importer)
@@ -230,10 +239,8 @@ void ContentLibraryWidget::updateImportedState(const QString &bundleId)
m_materialsModel->updateImportedState(importedItems);
else if (bundleId == compUtils.effectsBundleId())
m_effectsModel->updateImportedState(importedItems);
else if (bundleId == compUtils.userMaterialsBundleId())
m_userModel->updateMaterialsImportedState(importedItems);
else if (bundleId == compUtils.user3DBundleId())
m_userModel->update3DImportedState(importedItems);
else
m_userModel->updateImportedState(importedItems, bundleId);
}
ContentLibraryBundleImporter *ContentLibraryWidget::importer() const

View File

@@ -24,13 +24,13 @@ namespace QmlDesigner {
class ContentLibraryBundleImporter;
class ContentLibraryEffectsModel;
class ContentLibraryIconProvider;
class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryMaterialsModel;
class ContentLibraryTexture;
class ContentLibraryTexturesModel;
class ContentLibraryUserModel;
class NodeMetaInfo;
class ContentLibraryWidget : public QFrame
{
@@ -48,6 +48,7 @@ class ContentLibraryWidget : public QFrame
public:
ContentLibraryWidget();
~ContentLibraryWidget();
QList<QToolButton *> createToolBarWidgets();
@@ -94,6 +95,7 @@ public:
QSize sizeHint() const override;
ContentLibraryBundleImporter *importer() const;
ContentLibraryIconProvider *iconProvider() const;
signals:
void bundleItemDragStarted(QmlDesigner::ContentLibraryItem *item);
@@ -108,6 +110,7 @@ signals:
void isQt6ProjectChanged();
void importerRunningChanged();
void hasModelSelectionChanged();
void importBundle();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -128,6 +131,7 @@ private:
void populateTextureBundleModels();
void createImporter();
Utils::UniqueObjectPtr<ContentLibraryIconProvider> m_iconProvider;
Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget;
QPointer<ContentLibraryMaterialsModel> m_materialsModel;
QPointer<ContentLibraryTexturesModel> m_texturesModel;

View File

@@ -0,0 +1,74 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "usercategory.h"
namespace QmlDesigner {
UserCategory::UserCategory(const QString &title, const Utils::FilePath &bundlePath)
: m_title(title)
, m_bundlePath(bundlePath)
{
}
QString UserCategory::title() const
{
return m_title;
}
bool UserCategory::isEmpty() const
{
return m_isEmpty;
}
void UserCategory::setIsEmpty(bool val)
{
if (m_isEmpty == val)
return;
m_isEmpty = val;
emit isEmptyChanged();
}
bool UserCategory::noMatch() const
{
return m_noMatch;
}
void UserCategory::setNoMatch(bool val)
{
if (m_noMatch == val)
return;
m_noMatch = val;
emit noMatchChanged();
}
void UserCategory::addItem(QObject *item)
{
m_items.append(item);
emit itemsChanged();
setIsEmpty(false);
}
void UserCategory::removeItem(QObject *item)
{
m_items.removeOne(item);
item->deleteLater();
emit itemsChanged();
setIsEmpty(m_items.isEmpty());
}
Utils::FilePath UserCategory::bundlePath() const
{
return m_bundlePath;
}
QObjectList UserCategory::items() const
{
return m_items;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,50 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <utils/filepath.h>
#include <QObject>
namespace QmlDesigner {
class UserCategory : public QObject
{
Q_OBJECT
public:
UserCategory(const QString &title, const Utils::FilePath &bundlePath);
QString title() const;
QObjectList items() const;
bool isEmpty() const;
void setIsEmpty(bool val);
bool noMatch() const;
void setNoMatch(bool val);
virtual void loadBundle(bool force = false) = 0;
virtual void filter(const QString &searchText) = 0;
void addItem(QObject *item);
void removeItem(QObject *item);
Utils::FilePath bundlePath() const;
signals:
void itemsChanged();
void isEmptyChanged();
void noMatchChanged();
protected:
QString m_title;
Utils::FilePath m_bundlePath;
QObjectList m_items;
bool m_isEmpty = true;
bool m_noMatch = true;
bool m_bundleLoaded = false;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,120 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "useritemcategory.h"
#include "contentlibraryitem.h"
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <QJsonArray>
#include <QJsonDocument>
namespace QmlDesigner {
UserItemCategory::UserItemCategory(const QString &title, const Utils::FilePath &bundlePath,
const QString &bundleId)
: UserCategory(title, bundlePath)
, m_bundleId(bundleId)
{
}
void UserItemCategory::loadBundle(bool force)
{
if (m_bundleLoaded && !force)
return;
// clean up
qDeleteAll(m_items);
m_items.clear();
m_bundleLoaded = false;
m_noMatch = false;
m_bundleObj = {};
m_bundlePath.ensureWritableDir();
m_bundlePath.pathAppended("icons").ensureWritableDir();
auto jsonFilePath = m_bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME);
if (!jsonFilePath.exists()) {
QString jsonContent = "{\n";
jsonContent += " \"id\": \"" + m_bundleId + "\",\n";
jsonContent += " \"items\": []\n";
jsonContent += "}";
Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent.toLatin1());
if (!res.has_value()) {
qWarning() << __FUNCTION__ << res.error();
setIsEmpty(true);
emit itemsChanged();
return;
}
}
Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
if (!jsonContents.has_value()) {
qWarning() << __FUNCTION__ << jsonContents.error();
setIsEmpty(true);
emit itemsChanged();
return;
}
QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
if (bundleJsonDoc.isNull()) {
qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
setIsEmpty(true);
emit itemsChanged();
return;
}
m_bundleObj = bundleJsonDoc.object();
m_bundleObj["id"] = m_bundleId;
// parse items
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
QString typePrefix = compUtils.userBundleType(m_bundleId);
const QJsonArray itemsArr = m_bundleObj.value("items").toArray();
for (const QJsonValueConstRef &itemRef : itemsArr) {
const QJsonObject itemObj = itemRef.toObject();
QString name = itemObj.value("name").toString();
QString qml = itemObj.value("qml").toString();
TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
QUrl icon = m_bundlePath.pathAppended(itemObj.value("icon").toString()).toUrl();
QStringList files = itemObj.value("files").toVariant().toStringList();
m_items.append(new ContentLibraryItem(this, name, qml, type, icon, files, m_bundleId));
}
m_sharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
for (const QJsonValueConstRef &file : sharedFilesArr)
m_sharedFiles.append(file.toString());
m_bundleLoaded = true;
setIsEmpty(m_items.isEmpty());
emit itemsChanged();
}
void UserItemCategory::filter(const QString &searchText)
{
bool noMatch = true;
for (QObject *item : std::as_const(m_items)) {
ContentLibraryItem *castedItem = qobject_cast<ContentLibraryItem *>(item);
bool itemVisible = castedItem->filter(searchText);
if (itemVisible)
noMatch = false;
}
setNoMatch(noMatch);
}
QStringList UserItemCategory::sharedFiles() const
{
return m_sharedFiles;
}
QJsonObject &UserItemCategory::bundleObjRef()
{
return m_bundleObj;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "usercategory.h"
#include <QJsonObject>
namespace QmlDesigner {
class ContentLibraryItem;
class UserItemCategory : public UserCategory
{
Q_OBJECT
public:
UserItemCategory(const QString &title, const Utils::FilePath &bundlePath, const QString &bundleId);
void loadBundle(bool force) override;
void filter(const QString &searchText) override;
QStringList sharedFiles() const;
QJsonObject &bundleObjRef();
private:
QString m_bundleId;
QJsonObject m_bundleObj;
QStringList m_sharedFiles;
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,66 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "usertexturecategory.h"
#include "contentlibrarytexture.h"
#include <designerpaths.h>
#include <imageutils.h>
namespace QmlDesigner {
UserTextureCategory::UserTextureCategory(const QString &title, const Utils::FilePath &bundlePath)
: UserCategory(title, bundlePath)
{
}
void UserTextureCategory::loadBundle(bool force)
{
if (m_bundleLoaded && !force)
return;
// clean up
qDeleteAll(m_items);
m_items.clear();
m_bundlePath.ensureWritableDir();
m_bundlePath.pathAppended("icons").ensureWritableDir();
addItems(m_bundlePath.dirEntries(QDir::Files));
m_bundleLoaded = true;
}
void UserTextureCategory::filter(const QString &searchText)
{
bool noMatch = true;
for (QObject *item : std::as_const(m_items)) {
ContentLibraryTexture *castedItem = qobject_cast<ContentLibraryTexture *>(item);
bool itemVisible = castedItem->filter(searchText);
if (itemVisible)
noMatch = false;
}
setNoMatch(noMatch);
}
void UserTextureCategory::addItems(const Utils::FilePaths &paths)
{
for (const Utils::FilePath &filePath : paths) {
QString suffix = '.' + filePath.suffix();
auto iconFileInfo = filePath.parentDir().pathAppended("icons/" + filePath.baseName() + ".png")
.toFileInfo();
QPair<QSize, qint64> info = ImageUtils::imageInfo(filePath.path());
QString dirPath = filePath.parentDir().toFSPathString();
QSize imgDims = info.first;
qint64 imgFileSize = info.second;
auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
m_items.append(tex);
}
setIsEmpty(m_items.isEmpty());
emit itemsChanged();
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,25 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "usercategory.h"
namespace QmlDesigner {
class ContentLibraryTexture;
class UserTextureCategory : public UserCategory
{
Q_OBJECT
public:
UserTextureCategory(const QString &title, const Utils::FilePath &bundlePath);
void loadBundle(bool force) override;
void filter(const QString &searchText) override;
void addItems(const Utils::FilePaths &paths);
};
} // namespace QmlDesigner

View File

@@ -16,15 +16,75 @@
#include <coreplugin/messagebox.h>
#include <QRegularExpression>
#include <QStack>
#include <QTimer>
#include <QUrl>
namespace QmlDesigner {
using namespace Qt::StringLiterals;
static QString nameFromId(const QString &id, const QString &defaultName)
{
if (id.isEmpty())
return defaultName;
QString newName = id;
static const QRegularExpression sideUnderscores{R"((?:^_+)|(?:_+$))"};
static const QRegularExpression underscores{R"((?:_+))"};
static const QRegularExpression camelCases{R"((?:[A-Z](?=[a-z]))|(?:(?<=[a-z])[A-Z]))"};
newName.remove(sideUnderscores);
// Insert underscore to camel case edges
QRegularExpressionMatchIterator caseMatch = camelCases.globalMatch(newName);
QStack<int> camelCaseIndexes;
while (caseMatch.hasNext())
camelCaseIndexes.push(caseMatch.next().capturedStart());
while (!camelCaseIndexes.isEmpty())
newName.insert(camelCaseIndexes.pop(), '_');
// Replace underscored joints with space
newName.replace(underscores, " ");
newName = newName.trimmed();
if (newName.isEmpty())
return defaultName;
newName[0] = newName[0].toUpper();
return newName;
}
CreateTexture::CreateTexture(AbstractView *view)
: m_view{view}
{}
ModelNode CreateTexture::execute()
{
ModelNode matLib = Utils3D::materialLibraryNode(m_view);
if (!matLib.isValid())
return {};
ModelNode newTextureNode;
m_view->executeInTransaction(__FUNCTION__, [&]() {
#ifdef QDS_USE_PROJECTSTORAGE
newTextureNode = m_view->createModelNode("Texture");
#else
NodeMetaInfo metaInfo = m_view->model()->qtQuick3DTextureMetaInfo();
newTextureNode = m_view->createModelNode("QtQuick3D.Texture",
metaInfo.majorVersion(),
metaInfo.minorVersion());
#endif
newTextureNode.ensureIdExists();
VariantProperty textureName = newTextureNode.variantProperty("objectName");
textureName.setValue(nameFromId(newTextureNode.id(), "Texture"_L1));
matLib.defaultNodeListProperty().reparentHere(newTextureNode);
});
return newTextureNode;
}
ModelNode CreateTexture::execute(const QString &filePath, AddTextureMode mode, int sceneId)
{
Asset asset(filePath);
@@ -94,8 +154,12 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat
#endif
newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName()));
VariantProperty textureName = newTexNode.variantProperty("objectName");
textureName.setValue(nameFromId(newTexNode.id(), "Texture"_L1));
VariantProperty sourceProp = newTexNode.variantProperty("source");
sourceProp.setValue(QUrl(textureSource));
matLib.defaultNodeListProperty().reparentHere(newTexNode);
}

View File

@@ -23,7 +23,10 @@ class CreateTexture : public QObject
public:
CreateTexture(AbstractView *view);
ModelNode execute(const QString &filePath, AddTextureMode mode = AddTextureMode::Texture, int sceneId = -1);
ModelNode execute();
ModelNode execute(const QString &filePath,
AddTextureMode mode = AddTextureMode::Texture,
int sceneId = -1);
ModelNode resolveSceneEnv(int sceneId);
void assignTextureAsLightProbe(const ModelNode &texture, int sceneId);

View File

@@ -49,7 +49,7 @@ bool CurveEditorView::hasWidget() const
WidgetInfo CurveEditorView::widgetInfo()
{
return createWidgetInfo(m_editor, "CurveEditorId", WidgetInfo::BottomPane, 0, tr("Curves"));
return createWidgetInfo(m_editor, "CurveEditorId", WidgetInfo::BottomPane, tr("Curves"));
}
void CurveEditorView::modelAttached(Model *model)

View File

@@ -7,7 +7,7 @@
#include <qmldesignerplugin.h>
#include <bindingproperty.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
@@ -244,6 +244,9 @@ QTextStream &operator<<(QTextStream &stream, AuxiliaryDataType type)
case AuxiliaryDataType::Temporary:
stream << "Temporary";
break;
case AuxiliaryDataType::Persistent:
stream << "Persistent";
break;
}
return stream;
@@ -436,7 +439,6 @@ WidgetInfo DebugView::widgetInfo()
return createWidgetInfo(m_debugViewWidget.data(),
QStringLiteral("DebugView"),
WidgetInfo::LeftPane,
0,
tr("Debug View"));
}

View File

@@ -0,0 +1,36 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "nodeinstanceglobal.h"
#include <QVariant>
namespace QmlDesigner
{
using ThemeId = ushort;
enum class GroupType { Colors, Flags, Numbers, Strings };
class ThemeProperty
{
public:
bool isValid() const { return !name.trimmed().isEmpty() && value.isValid(); }
PropertyName name;
QVariant value;
bool isBinding = false;
};
constexpr const char *GroupId(const GroupType type) {
if (type == GroupType::Colors) return "colors";
if (type == GroupType::Flags) return "flags";
if (type == GroupType::Numbers) return "numbers";
if (type == GroupType::Strings) return "strings";
return "unknown";
}
}

View File

@@ -0,0 +1,187 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dsthemegroup.h"
#include <model.h>
#include <nodeproperty.h>
#include <variantproperty.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
#include <QVariant>
namespace QmlDesigner {
using namespace std;
namespace {
Q_LOGGING_CATEGORY(dsLog, "qtc.designer.designSystem", QtInfoMsg)
std::optional<TypeName> groupTypeName(GroupType type)
{
switch (type) {
case QmlDesigner::GroupType::Colors: return "color"; break;
case QmlDesigner::GroupType::Flags: return "bool"; break;
case QmlDesigner::GroupType::Numbers: return "real"; break;
case QmlDesigner::GroupType::Strings: return "string"; break;
}
return {};
}
QDebug &operator<<(QDebug &s, const ThemeProperty &p)
{
s << "{Name:" << p.name << ", Value:" << p.value << ", isBinding:" << p.isBinding << "}";
return s;
}
}
DSThemeGroup::DSThemeGroup(GroupType type):
m_type(type)
{}
DSThemeGroup::~DSThemeGroup() {}
bool DSThemeGroup::addProperty(ThemeId theme, const ThemeProperty &prop)
{
if (!prop.isValid()) {
qCDebug(dsLog) << "Add property failed. Invalid property." << prop;
return false;
}
if (!m_values.contains(prop.name))
m_values[prop.name] = {};
auto &tValues = m_values.at(prop.name);
if (tValues.contains(theme)) {
qCDebug(dsLog) << "Add property failed. Duplicate property name." << prop;
return false;
}
tValues.emplace(std::piecewise_construct,
std::forward_as_tuple(theme),
std::forward_as_tuple(prop.value, prop.isBinding));
return true;
}
std::optional<ThemeProperty> DSThemeGroup::propertyValue(ThemeId theme, const PropertyName &name) const
{
if (!m_values.contains(name))
return {};
const auto &tValues = m_values.at(name);
const auto itr = tValues.find(theme);
if (itr != tValues.end()) {
auto &[value, isBindind] = itr->second;
return ThemeProperty{name, value, isBindind};
}
return {};
}
void DSThemeGroup::updateProperty(ThemeId theme, PropertyName newName, const ThemeProperty &prop)
{
if (!m_values.contains(prop.name)) {
qCDebug(dsLog) << "Property update failure. Can't find property" << prop;
return;
}
if (!ThemeProperty{newName, prop.value, prop.isBinding}.isValid()) {
qCDebug(dsLog) << "Property update failure. Invalid property" << prop << newName;
return;
}
if (newName != prop.name && m_values.contains(newName)) {
qCDebug(dsLog) << "Property update failure. Property name update already exists" << newName
<< prop;
return;
}
auto &tValues = m_values.at(prop.name);
const auto itr = tValues.find(theme);
if (itr == tValues.end()) {
qCDebug(dsLog) << "Property update failure. No property for the theme" << theme << prop;
return;
}
auto &entry = tValues.at(theme);
entry.value = prop.value;
entry.isBinding = prop.isBinding;
if (newName != prop.name) {
m_values[newName] = std::move(tValues);
m_values.erase(prop.name);
}
}
void DSThemeGroup::removeProperty(const PropertyName &name)
{
m_values.erase(name);
}
size_t DSThemeGroup::count(ThemeId theme) const
{
return std::accumulate(m_values.cbegin(),
m_values.cend(),
0ull,
[theme](size_t c, const GroupProperties::value_type &p) {
return c + (p.second.contains(theme) ? 1 : 0);
});
}
size_t DSThemeGroup::count() const
{
return m_values.size();
}
void DSThemeGroup::removeTheme(ThemeId theme)
{
for (auto itr = m_values.begin(); itr != m_values.end();) {
itr->second.erase(theme);
itr = itr->second.size() == 0 ? m_values.erase(itr) : std::next(itr);
}
}
void DSThemeGroup::duplicateValues(ThemeId from, ThemeId to)
{
for (auto itr = m_values.begin(); itr != m_values.end(); ++itr) {
auto &[propName, values] = *itr;
auto fromValueItr = values.find(from);
if (fromValueItr != values.end())
values[to] = fromValueItr->second;
}
}
void DSThemeGroup::decorate(ThemeId theme, ModelNode themeNode)
{
if (!count(theme))
return; // No props for this theme in this group.
const auto groupName = GroupId(m_type);
const auto typeName = groupTypeName(m_type);
auto groupNode = themeNode.model()->createModelNode("QtObject");
auto groupProperty = themeNode.nodeProperty(groupName);
if (!groupProperty || !typeName || !groupNode) {
qCDebug(dsLog) << "Adding group node failed." << groupName << theme;
return;
}
for (auto itr = m_values.begin(); itr != m_values.end(); ++itr) {
auto &[propName, values] = *itr;
auto themeValue = values.find(theme);
if (themeValue != values.end()) {
auto &propData = themeValue->second;
if (propData.isBinding) {
auto bindingProp = groupNode.bindingProperty(propName);
if (bindingProp)
bindingProp.setDynamicTypeNameAndExpression(*typeName, propData.value.toString());
} else {
auto nodeProp = groupNode.variantProperty(propName);
if (nodeProp)
nodeProp.setDynamicTypeNameAndValue(*typeName, propData.value);
}
}
}
groupProperty.setDynamicTypeNameAndsetModelNode("QtObject", groupNode);
}
}

View File

@@ -0,0 +1,59 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "qmldesignercomponents_global.h"
#include "dsconstants.h"
#include "nodeinstanceglobal.h"
#include <modelnode.h>
#include <map>
#include <optional>
namespace QmlDesigner {
class QMLDESIGNERCOMPONENTS_EXPORT DSThemeGroup
{
struct PropertyData
{
PropertyData() = default;
template<typename Variant>
PropertyData(Variant &&value, bool isBinding)
: value{std::forward<Variant>(value)}
, isBinding{isBinding}
{}
QVariant value;
bool isBinding = false;
};
using ThemeValues = std::map<ThemeId, PropertyData>;
using GroupProperties = std::map<PropertyName, ThemeValues>;
public:
DSThemeGroup(GroupType type);
~DSThemeGroup();
bool addProperty(ThemeId, const ThemeProperty &prop);
std::optional<ThemeProperty> propertyValue(ThemeId theme, const PropertyName &name) const;
void updateProperty(ThemeId theme, PropertyName newName, const ThemeProperty &prop);
void removeProperty(const PropertyName &name);
GroupType type() const { return m_type; }
size_t count(ThemeId theme) const;
size_t count() const;
bool isEmpty() const;
void removeTheme(ThemeId theme);
void duplicateValues(ThemeId from, ThemeId to);
void decorate(ThemeId theme, ModelNode themeNode);
private:
const GroupType m_type;
GroupProperties m_values;
};
}

View File

@@ -0,0 +1,179 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dsthememanager.h"
#include "dsconstants.h"
#include "dsthemegroup.h"
#include <nodeproperty.h>
#include <model.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
#include <QVariant>
namespace {
Q_LOGGING_CATEGORY(dsLog, "qtc.designer.designSystem", QtInfoMsg)
}
namespace QmlDesigner {
DSThemeManager::DSThemeManager() {}
DSThemeManager::~DSThemeManager() {}
std::optional<ThemeId> DSThemeManager::addTheme(const ThemeName &themeName)
{
if (themeName.trimmed().isEmpty() || themeId(themeName)) {
qCDebug(dsLog) << "Can not add new Theme. Duplicate theme name";
return {};
}
const ThemeId newThemeId = m_themes.empty() ? 1 : m_themes.rbegin()->first + 1;
m_themes.insert({newThemeId, themeName});
// Copy the new theme properties from an old theme(first one).
if (m_themes.size() > 1)
duplicateTheme(m_themes.begin()->first, newThemeId);
return newThemeId;
}
std::optional<ThemeId> DSThemeManager::themeId(const ThemeName &themeName) const
{
for (auto &[id, name] : m_themes) {
if (themeName == name)
return id;
}
return {};
}
size_t DSThemeManager::themeCount() const
{
return m_themes.size();
}
void DSThemeManager::removeTheme(ThemeId id)
{
if (!m_themes.contains(id))
return;
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->removeTheme(id);
m_themes.erase(id);
}
void DSThemeManager::duplicateTheme(ThemeId from, ThemeId to)
{
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->duplicateValues(from, to);
}
std::optional<ThemeProperty> DSThemeManager::property(ThemeId themeId,
GroupType gType,
const PropertyName &name) const
{
if (m_themes.contains(themeId)) {
auto groupItr = m_groups.find(gType);
if (groupItr != m_groups.end())
return groupItr->second->propertyValue(themeId, name);
}
qCDebug(dsLog) << "Error fetching property: {" << themeId << GroupId(gType) << name << "}";
return {};
}
bool DSThemeManager::addProperty(GroupType gType, const ThemeProperty &p)
{
if (!m_themes.size()) {
qCDebug(dsLog) << "Can not add proprty. Themes empty";
return false;
}
// A property is added to all themes.
DSThemeGroup *dsGroup = propertyGroup(gType);
QTC_ASSERT(dsGroup, return false);
bool success = true;
for (auto itr = m_themes.begin(); itr != m_themes.end(); ++itr)
success &= dsGroup->addProperty(itr->first, p);
return success;
}
void DSThemeManager::removeProperty(GroupType gType, const PropertyName &name)
{
// A property is removed from all themes.
DSThemeGroup *dsGroup = propertyGroup(gType);
QTC_ASSERT(dsGroup, return);
dsGroup->removeProperty(name);
}
void DSThemeManager::updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p)
{
updateProperty(id, gType, p, p.name);
}
void DSThemeManager::updateProperty(ThemeId id,
GroupType gType,
const ThemeProperty &p,
const PropertyName &newName)
{
if (!m_themes.contains(id))
return;
DSThemeGroup *dsGroup = propertyGroup(gType);
QTC_ASSERT(dsGroup, return);
dsGroup->updateProperty(id, newName, p);
}
void DSThemeManager::decorate(ModelNode rootNode) const
{
if (!m_themes.size())
return;
auto p = rootNode.bindingProperty("currentTheme");
p.setDynamicTypeNameAndExpression("QtObject", QString::fromLatin1(m_themes.begin()->second));
addGroupAliases(rootNode);
auto model = rootNode.model();
for (auto itr = m_themes.begin(); itr != m_themes.end(); ++itr) {
auto themeNode = model->createModelNode("QtObject");
auto themeProperty = model->rootModelNode().nodeProperty(itr->second);
themeProperty.setDynamicTypeNameAndsetModelNode("QtObject", themeNode);
// Add property groups
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->decorate(itr->first, themeNode);
}
}
DSThemeGroup *DSThemeManager::propertyGroup(GroupType type)
{
auto itr = m_groups.find(type);
if (itr == m_groups.end())
itr = m_groups.insert({type, std::make_unique<DSThemeGroup>(type)}).first;
return itr->second.get();
}
void DSThemeManager::addGroupAliases(ModelNode rootNode) const
{
QSet<PropertyName> groupNames;
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) {
DSThemeGroup *group = groupItr->second.get();
const PropertyName groupName = GroupId(group->type());
if (group->count())
groupNames.insert(groupName);
}
for (const auto &name : groupNames) {
auto p = rootNode.bindingProperty(name);
auto binding = QString("currentTheme.%1").arg(QString::fromLatin1(name));
p.setDynamicTypeNameAndExpression("QtObject", binding);
}
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "qmldesignercomponents_global.h"
#include "dsconstants.h"
#include "dsthemegroup.h"
#include <modelnode.h>
namespace QmlDesigner {
using ThemeName = PropertyName;
class DSTheme;
class QMLDESIGNERCOMPONENTS_EXPORT DSThemeManager
{
public:
DSThemeManager();
~DSThemeManager();
DSThemeManager(const DSThemeManager&) = delete;
DSThemeManager& operator=(const DSThemeManager&) = delete;
DSThemeManager(DSThemeManager&&) = default;
DSThemeManager& operator=(DSThemeManager&&) = default;
std::optional<ThemeId> addTheme(const ThemeName &themeName);
std::optional<ThemeId> themeId(const ThemeName &themeName) const;
void removeTheme(ThemeId id);
size_t themeCount() const;
void duplicateTheme(ThemeId from, ThemeId to);
bool addProperty(GroupType gType, const ThemeProperty &p);
std::optional<ThemeProperty> property(ThemeId themeId,
GroupType gType,
const PropertyName &name) const;
void removeProperty(GroupType gType, const PropertyName &p);
void updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p);
void updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p, const PropertyName &newName);
void decorate(ModelNode rootNode) const;
private:
DSThemeGroup *propertyGroup(GroupType type);
void addGroupAliases(ModelNode rootNode) const;
private:
std::map<ThemeId, ThemeName> m_themes;
std::map<GroupType, std::unique_ptr<DSThemeGroup>> m_groups;
};
}

View File

@@ -9,7 +9,7 @@
#include <bakelightsdatamodel.h>
#include <bindingproperty.h>
#include <documentmanager.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <modelnode.h>
#include <nodeabstractproperty.h>
#include <nodeinstanceview.h>

View File

@@ -15,7 +15,7 @@
#include "qmlobjectnode.h"
#include "variantproperty.h"
#include <model/modelutils.h>
#include <modelutils.h>
#include <utils3d.h>
@@ -234,7 +234,7 @@ bool BakeLightsDataModel::reset()
PropertyName dotName = mi.name() + '.';
for (const AbstractProperty &prop : props) {
if (prop.name().startsWith(dotName)) {
PropertyName subName = prop.name().mid(dotName.size());
PropertyNameView subName = prop.name().mid(dotName.size());
if (subName == "bakedLightmap") {
ModelNode blm = prop.toBindingProperty().resolveToModelNode();
if (blm.isValid()) {
@@ -269,7 +269,7 @@ bool BakeLightsDataModel::reset()
PropertyName dotName = mi.name() + '.';
for (const AbstractProperty &prop : props) {
if (prop.name().startsWith(dotName)) {
PropertyName subName = prop.name().mid(dotName.size());
PropertyNameView subName = prop.name().mid(dotName.size());
if (subName == "bakeMode") {
if (prop.isVariantProperty()) {
QString bakeModeStr = prop.toVariantProperty().value()

View File

@@ -2,13 +2,9 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "cameraspeedconfiguration.h"
#include "designersettings.h"
#include "edit3dview.h"
#include "edit3dviewconfig.h"
#include <coreplugin/icore.h>
#include <utils/environment.h>
#include <QCursor>
@@ -193,4 +189,9 @@ bool CameraSpeedConfiguration::eventFilter(QObject *obj, QEvent *event)
return QObject::eventFilter(obj, event);
}
bool CameraSpeedConfiguration::isQDSTrusted() const
{
return Edit3DView::isQDSTrusted();
}
} // namespace QmlDesigner

View File

@@ -39,6 +39,7 @@ public:
Q_INVOKABLE void restoreCursor();
Q_INVOKABLE void holdCursorInPlace();
Q_INVOKABLE int devicePixelRatio();
Q_INVOKABLE bool isQDSTrusted() const;
void cancel();
void apply();
@@ -58,6 +59,7 @@ signals:
void speedChanged();
void multiplierChanged();
void totalSpeedChanged();
void accessibilityOpened();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;

View File

@@ -3,9 +3,8 @@
#include "edit3dactions.h"
#include "bakelights.h"
#include "edit3dview.h"
#include "nodemetainfo.h"
#include "indicatoractionwidget.h"
#include "qmldesignerconstants.h"
#include "seekerslider.h"
@@ -34,10 +33,25 @@ void Edit3DActionTemplate::actionTriggered(bool b)
m_action(m_selectionContext);
}
Edit3DWidgetActionTemplate::Edit3DWidgetActionTemplate(QWidgetAction *widget)
Edit3DWidgetActionTemplate::Edit3DWidgetActionTemplate(QWidgetAction *widget,
SelectionContextOperation action)
: PureActionInterface(widget)
, m_action(action)
{
QObject::connect(widget, &QAction::triggered, widget, [this](bool value) {
actionTriggered(value);
});
}
void Edit3DWidgetActionTemplate::setSelectionContext(const SelectionContext &selectionContext)
{
m_selectionContext = selectionContext;
}
void Edit3DWidgetActionTemplate::actionTriggered([[maybe_unused]] bool b)
{
if (m_action)
m_action(m_selectionContext);
}
Edit3DAction::Edit3DAction(const QByteArray &menuId,
@@ -154,4 +168,34 @@ bool Edit3DBakeLightsAction::isEnabled(const SelectionContext &) const
&& !Utils3D::activeView3dId(m_view).isEmpty();
}
Edit3DIndicatorButtonAction::Edit3DIndicatorButtonAction(const QByteArray &menuId,
View3DActionType type,
const QString &description,
const QIcon &icon,
SelectionContextOperation customAction,
Edit3DView *view)
: Edit3DAction(menuId,
type,
view,
new Edit3DWidgetActionTemplate(new IndicatorButtonAction(description, icon),
customAction))
{
m_buttonAction = qobject_cast<IndicatorButtonAction *>(action());
}
void Edit3DIndicatorButtonAction::setIndicator(bool indicator)
{
m_buttonAction->setIndicator(indicator);
}
bool Edit3DIndicatorButtonAction::isVisible(const SelectionContext &) const
{
return m_buttonAction->isVisible();
}
bool Edit3DIndicatorButtonAction::isEnabled(const SelectionContext &) const
{
return m_buttonAction->isEnabled();
}
} // namespace QmlDesigner

View File

@@ -17,6 +17,7 @@ namespace QmlDesigner {
using SelectionContextOperation = std::function<void(const SelectionContext &)>;
class Edit3DView;
class SeekerSliderAction;
class IndicatorButtonAction;
class Edit3DActionTemplate : public DefaultAction
{
@@ -40,8 +41,13 @@ class Edit3DWidgetActionTemplate : public PureActionInterface
Q_DISABLE_COPY(Edit3DWidgetActionTemplate)
public:
explicit Edit3DWidgetActionTemplate(QWidgetAction *widget);
virtual void setSelectionContext(const SelectionContext &) {}
explicit Edit3DWidgetActionTemplate(QWidgetAction *widget, SelectionContextOperation action = {});
void setSelectionContext(const SelectionContext &selectionContext) override;
virtual void actionTriggered(bool b);
SelectionContextOperation m_action;
SelectionContext m_selectionContext;
};
class Edit3DAction : public AbstractAction
@@ -108,6 +114,27 @@ private:
SeekerSliderAction *m_seeker = nullptr;
};
class Edit3DIndicatorButtonAction : public Edit3DAction
{
public:
Edit3DIndicatorButtonAction(const QByteArray &menuId,
View3DActionType type,
const QString &description,
const QIcon &icon,
SelectionContextOperation customAction,
Edit3DView *view);
IndicatorButtonAction *buttonAction();
void setIndicator(bool indicator);
protected:
bool isVisible(const SelectionContext &) const override;
bool isEnabled(const SelectionContext &) const override;
private:
IndicatorButtonAction *m_buttonAction = nullptr;
};
class Edit3DBakeLightsAction : public Edit3DAction
{
public:

View File

@@ -81,21 +81,13 @@ QWidget *Edit3DCanvas::busyIndicator() const
return m_busyIndicator;
}
#ifdef Q_OS_MACOS
extern "C" bool AXIsProcessTrusted();
#endif
void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
{
if (m_flyMode == enabled)
return;
#ifdef Q_OS_MACOS
if (!AXIsProcessTrusted())
m_isTrusted = false;
#endif
m_flyMode = enabled;
m_isQDSTrusted = Edit3DView::isQDSTrusted();
if (enabled) {
m_flyModeStartTime = QDateTime::currentMSecsSinceEpoch();
@@ -199,7 +191,7 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
// We notify explicit camera rotation need for puppet rather than rely in mouse events,
// as mouse isn't grabbed on puppet side and can't handle fast movements that go out of
// edit camera mouse area. This also simplifies split view handling.
QPointF diff = m_isTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos());
QPointF diff = m_isQDSTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos());
if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) {
m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove,
@@ -212,7 +204,7 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
m_flyModeFirstUpdate = false;
}
if (m_isTrusted)
if (m_isQDSTrusted)
QCursor::setPos(m_hiddenCursorPos);
else
m_lastCursorPos = e->globalPos();

View File

@@ -53,7 +53,7 @@ private:
qint32 m_activeScene = -1;
QElapsedTimer m_usageTimer;
qreal m_opacity = 1.0;
bool m_isTrusted = true;
bool m_isQDSTrusted = true;
QWidget *m_busyIndicator = nullptr;
bool m_flyMode = false;
QPoint m_flyModeStartCursorPos;

View File

@@ -0,0 +1,195 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "edit3dmaterialsaction.h"
#include <bindingproperty.h>
#include <designmodewidget.h>
#include <model.h>
#include <modelnode.h>
#include <modelutils.h>
#include <qmldesignerplugin.h>
#include <qmleditormenu.h>
#include <variantproperty.h>
#include <utils/qtcassert.h>
using namespace Qt::StringLiterals;
namespace QmlDesigner {
static QString getMaterialName(const ModelNode &material, bool forceIncludeId = false)
{
QString materialName = material.variantProperty("objectName").value().toString();
if (materialName.isEmpty() || forceIncludeId)
materialName.append(QString("[%1]").arg(material.id()));
return materialName;
}
struct MaterialNameLessThan
{
bool operator()(const ModelNode &a, const ModelNode &b)
{
const QString aName = getMaterialName(a, true);
const QString bName = getMaterialName(b, true);
return aName.compare(bName, Qt::CaseSensitive) < 0;
};
};
static QList<ModelNode> getMaterials(const ModelNode &node)
{
BindingProperty matsProp = node.bindingProperty("materials");
if (!matsProp.exists())
return {};
Model *model = node.model();
QList<ModelNode> materials;
if (model->hasId(matsProp.expression()))
materials.append(model->modelNodeForId(matsProp.expression()));
else
materials = matsProp.resolveToModelNodeList();
return materials;
}
static QList<ModelNode> getSortedMaterials(const ModelNode &node)
{
QList<ModelNode> materials = getMaterials(node);
std::sort(materials.begin(), materials.end(), MaterialNameLessThan{});
return materials;
}
static void removeMaterialFromNode(const ModelNode &node, const QString &materialId, int nthMaterial)
{
BindingProperty matsProp = node.bindingProperty("materials");
if (!matsProp.exists())
return;
const QString materialsExpression = matsProp.expression();
Model *model = node.model();
if (matsProp.isList()) {
matsProp.removeModelNodeFromArray(model->modelNodeForId(materialId));
QStringList nodeMaterials = ModelUtils::expressionToList(materialsExpression);
int indexToBeRemoved = -1;
do
indexToBeRemoved = nodeMaterials.indexOf(materialId, indexToBeRemoved + 1);
while (nthMaterial-- && indexToBeRemoved != -1);
if (indexToBeRemoved != -1)
nodeMaterials.removeAt(indexToBeRemoved);
if (nodeMaterials.isEmpty())
matsProp.parentModelNode().removeProperty(matsProp.name());
else if (nodeMaterials.size() == 1)
matsProp.setExpression(nodeMaterials.first());
else
matsProp.setExpression('[' + nodeMaterials.join(',') + ']');
} else if (materialsExpression == materialId) {
matsProp.parentModelNode().removeProperty(matsProp.name());
}
}
static QList<ModelNode> commonMaterialsOfNodes(const QList<ModelNode> &selectedNodes)
{
if (selectedNodes.isEmpty())
return {};
if (selectedNodes.size() == 1)
return getMaterials(selectedNodes.first());
QList<ModelNode> commonMaterials = getSortedMaterials(selectedNodes.first());
for (const ModelNode &node : Utils::span(selectedNodes).subspan(1)) {
const QList<ModelNode> materials = getSortedMaterials(node);
QList<ModelNode> materialIntersection;
std::set_intersection(commonMaterials.begin(),
commonMaterials.end(),
materials.begin(),
materials.end(),
std::back_inserter(materialIntersection),
MaterialNameLessThan{});
std::swap(commonMaterials, materialIntersection);
if (commonMaterials.isEmpty())
return {};
}
return commonMaterials;
}
Edit3DMaterialsAction::Edit3DMaterialsAction(const QIcon &icon, QObject *parent)
: QAction(icon, tr("Materials"), parent)
{
this->setMenu(new QmlEditorMenu("Materials"));
connect(this, &QObject::destroyed, this->menu(), &QObject::deleteLater);
}
void Edit3DMaterialsAction::updateMenu(const QList<ModelNode> &selecedNodes)
{
QMenu *menu = this->menu();
QTC_ASSERT(menu, return);
m_selectedNodes = selecedNodes;
menu->clear();
const QList<ModelNode> materials = commonMaterialsOfNodes(m_selectedNodes);
QHash<ModelNode, int> nthMaterialMap; // <material, n times repeated>
for (const ModelNode &material : materials) {
int nthMaterialWithTheSameId = nthMaterialMap.value(material, -1) + 1;
nthMaterialMap.insert(material, nthMaterialWithTheSameId);
QAction *materialAction = createMaterialAction(material, menu, nthMaterialWithTheSameId);
if (materialAction)
menu->addAction(materialAction);
}
setVisible(!menu->actions().isEmpty());
setEnabled(isVisible());
}
void Edit3DMaterialsAction::removeMaterial(const QString &materialId, int nthMaterial)
{
if (m_selectedNodes.isEmpty())
return;
AbstractView *nodesView = m_selectedNodes.first().view();
nodesView->executeInTransaction(__FUNCTION__, [&] {
for (ModelNode &node : m_selectedNodes)
removeMaterialFromNode(node, materialId, nthMaterial);
});
}
QAction *Edit3DMaterialsAction::createMaterialAction(const ModelNode &material,
QMenu *parentMenu,
int nthMaterial)
{
const QString materialId = material.id();
if (materialId.isEmpty())
return nullptr;
QString materialName = getMaterialName(material);
QAction *action = new QAction(materialName, parentMenu);
QMenu *menu = new QmlEditorMenu(materialName, parentMenu);
connect(action, &QObject::destroyed, menu, &QObject::deleteLater);
QAction *removeMaterialAction = new QAction(tr("Remove"), menu);
connect(removeMaterialAction,
&QAction::triggered,
menu,
std::bind(&Edit3DMaterialsAction::removeMaterial, this, materialId, nthMaterial));
QAction *editMaterialAction = new QAction(tr("Edit"), menu);
connect(editMaterialAction, &QAction::triggered, menu, [material] {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialEditor", true);
if (auto materialView = material.view())
materialView->emitCustomNotification("select_material", {material});
});
menu->addAction(editMaterialAction);
menu->addAction(removeMaterialAction);
action->setMenu(menu);
return action;
}
}; // namespace QmlDesigner

View File

@@ -0,0 +1,31 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <selectioncontext.h>
#include <QAction>
#include <QList>
namespace QmlDesigner {
class ModelNode;
class Edit3DMaterialsAction : public QAction
{
Q_OBJECT
public:
Edit3DMaterialsAction(const QIcon &icon, QObject *parent);
void updateMenu(const QList<ModelNode> &selecedNodes);
private slots:
void removeMaterial(const QString &materialId, int nthMaterial);
private:
QAction *createMaterialAction(const ModelNode &material, QMenu *parentMenu, int nthMaterial);
QList<ModelNode> m_selectedNodes;
};
} // namespace QmlDesigner

View File

@@ -26,7 +26,7 @@
#include "variantproperty.h"
#include <auxiliarydataproperties.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <utils3d.h>
#include <coreplugin/icore.h>
@@ -41,7 +41,6 @@
#include <utils/qtcassert.h>
#include <utils/stylehelper.h>
#include <utils/utilsicons.h>
#include <QToolButton>
namespace QmlDesigner {
@@ -86,7 +85,6 @@ WidgetInfo Edit3DView::widgetInfo()
return createWidgetInfo(m_edit3DWidget.data(),
"Editor3D",
WidgetInfo::CentralPane,
0,
tr("3D"),
tr("3D view"),
DesignerWidgetFlags::IgnoreErrors);
@@ -1306,22 +1304,25 @@ void Edit3DView::createEdit3DActions()
this, [this] {
setCameraSpeedAuxData(m_cameraSpeedConfiguration->speed(),
m_cameraSpeedConfiguration->multiplier());
});
});
connect(m_cameraSpeedConfiguration.data(), &CameraSpeedConfiguration::accessibilityOpened,
this, [this] {
m_cameraSpeedConfigAction->setIndicator(false);
});
}
m_cameraSpeedConfiguration->showConfigDialog(resolveToolbarPopupPos(m_cameraSpeedConfigAction.get()));
};
m_cameraSpeedConfigAction = std::make_unique<Edit3DAction>(
m_cameraSpeedConfigAction = std::make_unique<Edit3DIndicatorButtonAction>(
QmlDesigner::Constants::EDIT3D_CAMERA_SPEED_CONFIG,
View3DActionType::Empty,
QCoreApplication::translate("CameraSpeedConfigAction", "Open camera speed configuration dialog"),
QKeySequence(),
false,
false,
QCoreApplication::translate("CameraSpeedConfigAction",
"Open camera speed configuration dialog"),
toolbarIcon(DesignerIcons::CameraSpeedConfigIcon),
this,
cameraSpeedConfigTrigger);
cameraSpeedConfigTrigger,
this);
m_cameraSpeedConfigAction->setIndicator(!isQDSTrusted());
m_leftActions << m_selectionModeAction.get();
m_leftActions << nullptr; // Null indicates separator

View File

@@ -18,6 +18,10 @@
#include <QVector>
#include <QVector3D>
#ifdef Q_OS_MACOS
extern "C" bool AXIsProcessTrusted();
#endif
QT_BEGIN_NAMESPACE
class QAction;
class QInputEvent;
@@ -41,6 +45,15 @@ public:
bool showWireframe = false;
};
static bool isQDSTrusted()
{
#ifdef Q_OS_MACOS
return AXIsProcessTrusted();
#else
return true;
#endif
}
Edit3DView(ExternalDependenciesInterface &externalDependencies);
WidgetInfo widgetInfo() override;
@@ -180,7 +193,7 @@ private:
std::unique_ptr<Edit3DAction> m_backgroundColorMenuAction;
std::unique_ptr<Edit3DAction> m_snapToggleAction;
std::unique_ptr<Edit3DAction> m_snapConfigAction;
std::unique_ptr<Edit3DAction> m_cameraSpeedConfigAction;
std::unique_ptr<Edit3DIndicatorButtonAction> m_cameraSpeedConfigAction;
std::unique_ptr<Edit3DBakeLightsAction> m_bakeLightsAction;
int particlemode;

View File

@@ -5,6 +5,7 @@
#include "edit3dactions.h"
#include "edit3dcanvas.h"
#include "edit3dmaterialsaction.h"
#include "edit3dtoolbarmenu.h"
#include "edit3dview.h"
@@ -35,7 +36,7 @@
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <utils/asset.h>
#include <utils/fileutils.h>
@@ -205,13 +206,8 @@ void Edit3DWidget::createContextMenu()
DocumentManager::goIntoComponent(m_view->singleSelectedModelNode());
});
m_editMaterialAction = m_contextMenu->addAction(
contextIcon(DesignerIcons::MaterialIcon),
tr("Edit Material"), [&] {
SelectionContext selCtx(m_view);
selCtx.setTargetNode(m_contextMenuTarget);
ModelNodeOperations::editMaterial(selCtx);
});
m_materialsAction = new Edit3DMaterialsAction(contextIcon(DesignerIcons::MaterialIcon), this);
m_contextMenu->addAction(m_materialsAction);
m_contextMenu->addSeparator();
@@ -374,7 +370,19 @@ void Edit3DWidget::createContextMenu()
m_addToContentLibAction = m_contextMenu->addAction(
contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
tr("Add to Content Library"), [&] {
view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget});
view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget}); // To ContentLibrary
});
m_importBundleAction = m_contextMenu->addAction(
contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
tr("Import Component"), [&] {
view()->emitCustomNotification("import_bundle_to_project"); // To ContentLibrary
});
m_exportBundleAction = m_contextMenu->addAction(
contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
tr("Export Component"), [&] {
view()->emitCustomNotification("export_item_as_bundle", {m_contextMenuTarget}); // To ContentLibrary
});
m_contextMenu->addSeparator();
@@ -644,7 +652,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
m_createSubMenu->setEnabled(!isSceneLocked());
m_editComponentAction->setEnabled(isSingleComponent);
m_editMaterialAction->setEnabled(isModel);
m_materialsAction->setEnabled(isModel);
m_duplicateAction->setEnabled(selectionExcludingRoot);
m_copyAction->setEnabled(selectionExcludingRoot);
m_pasteAction->setEnabled(isPasteAvailable());
@@ -657,6 +665,8 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible());
m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled());
m_addToContentLibAction->setEnabled(isNode && !isInBundle);
m_exportBundleAction->setEnabled(isNode);
m_materialsAction->updateMenu(view()->selectedModelNodes());
if (m_view) {
int idx = m_view->activeSplit();

View File

@@ -21,6 +21,7 @@ namespace QmlDesigner {
class Edit3DView;
class Edit3DCanvas;
class ToolBox;
class Edit3DMaterialsAction;
struct ItemLibraryDetails {
QString name;
@@ -89,7 +90,6 @@ private:
QPointer<QMenu> m_contextMenu;
QPointer<QAction> m_bakeLightsAction;
QPointer<QAction> m_editComponentAction;
QPointer<QAction> m_editMaterialAction;
QPointer<QAction> m_duplicateAction;
QPointer<QAction> m_copyAction;
QPointer<QAction> m_pasteAction;
@@ -100,7 +100,10 @@ private:
QPointer<QAction> m_selectParentAction;
QPointer<QAction> m_toggleGroupAction;
QPointer<QAction> m_wireFrameAction;
QPointer<QAction> m_importBundleAction;
QPointer<QAction> m_exportBundleAction;
QPointer<QAction> m_addToContentLibAction;
QPointer<Edit3DMaterialsAction> m_materialsAction;
QHash<int, QPointer<QAction>> m_matOverrideActions;
QPointer<QMenu> m_createSubMenu;
ModelNode m_contextMenuTarget;

View File

@@ -0,0 +1,193 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "indicatoractionwidget.h"
#include <theme.h>
#include <utils/icon.h>
#include <utils/stylehelper.h>
#include <QActionGroup>
#include <QMenu>
#include <QMouseEvent>
#include <QStyleOption>
#include <QStylePainter>
#include <QToolBar>
namespace QmlDesigner {
static void drawIndicator(QPainter *painter, const QPoint &point, int dimension)
{
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(Theme::getColor(Theme::DSamberLight));
painter->drawEllipse(point, dimension, dimension);
painter->restore();
}
IndicatorButton::IndicatorButton(QWidget *parent)
: QToolButton(parent)
{
Utils::StyleHelper::setPanelWidget(this);
Utils::StyleHelper::setPanelWidgetSingleRow(this);
}
bool IndicatorButton::indicator() const
{
return m_indicator;
}
void IndicatorButton::setIndicator(bool newIndicator)
{
if (m_indicator != newIndicator) {
m_indicator = newIndicator;
emit indicatorChanged(m_indicator);
update();
}
}
QSize IndicatorButton::sizeHint() const
{
if (QMenu *menu = qobject_cast<QMenu *>(parent())) {
ensurePolished();
QStyleOptionMenuItem opt;
initMenuStyleOption(menu, &opt, defaultAction());
QSize sz = style()
->itemTextRect(fontMetrics(), QRect(), Qt::TextShowMnemonic, false, text())
.size();
if (!opt.icon.isNull())
sz = QSize(sz.width() + opt.maxIconWidth + 4, qMax(sz.height(), opt.maxIconWidth));
QSize size = style()->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, this);
return size;
}
return Super::sizeHint();
}
void IndicatorButton::paintEvent([[maybe_unused]] QPaintEvent *event)
{
QStylePainter p(this);
if (QMenu *menu = qobject_cast<QMenu *>(parent())) {
this->setFixedWidth(menu->width());
QStyleOptionMenuItem opt;
initMenuStyleOption(menu, &opt, defaultAction());
p.drawControl(QStyle::CE_MenuItem, opt);
if (indicator() && opt.maxIconWidth && !opt.icon.isNull()) {
const int indicatorDim = opt.rect.height() / 8;
const int indicatorOffset = indicatorDim * 5 / 4;
drawIndicator(&p,
opt.rect.topLeft()
+ QPoint{opt.rect.height() - indicatorOffset, indicatorOffset},
indicatorDim);
}
} else {
QStyleOptionToolButton option;
initStyleOption(&option);
p.drawComplexControl(QStyle::CC_ToolButton, option);
if (indicator() && option.iconSize.isValid() && !option.icon.isNull()) {
const int indicatorDim = std::min(option.rect.width(), option.rect.height()) / 8;
const int indicatorOffset = indicatorDim * 5 / 4;
drawIndicator(&p,
option.rect.topRight() + QPoint{-indicatorOffset, indicatorOffset},
indicatorDim);
}
}
}
void IndicatorButton::initMenuStyleOption(QMenu *menu,
QStyleOptionMenuItem *option,
const QAction *action) const
{
if (!option || !action)
return;
option->initFrom(menu);
option->palette = palette();
option->state = QStyle::State_None;
if (window()->isActiveWindow())
option->state |= QStyle::State_Active;
if (isEnabled() && action->isEnabled() && (!action->menu() || action->menu()->isEnabled()))
option->state |= QStyle::State_Enabled;
else
option->palette.setCurrentColorGroup(QPalette::Disabled);
option->font = action->font().resolve(font());
option->fontMetrics = QFontMetrics(option->font);
if (menu->activeAction() && menu->activeAction() == action)
option->state |= QStyle::State_Selected;
option->menuHasCheckableItems = false;
if (!action->isCheckable()) {
option->checkType = QStyleOptionMenuItem::NotCheckable;
} else {
option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
? QStyleOptionMenuItem::Exclusive
: QStyleOptionMenuItem::NonExclusive;
option->checked = action->isChecked();
}
if (action->menu())
option->menuItemType = QStyleOptionMenuItem::SubMenu;
else if (action->isSeparator())
option->menuItemType = QStyleOptionMenuItem::Separator;
else
option->menuItemType = QStyleOptionMenuItem::Normal;
if (action->isIconVisibleInMenu())
option->icon = action->icon();
option->text = action->text();
option->maxIconWidth = 20;
option->rect = rect();
}
IndicatorButtonAction::IndicatorButtonAction(const QString &description,
const QIcon &icon,
QObject *parent)
: QWidgetAction(parent)
{
setText(description);
setToolTip(description);
setIcon(icon);
}
IndicatorButtonAction::~IndicatorButtonAction() = default;
void IndicatorButtonAction::setIndicator(bool indicator)
{
if (m_indicator != indicator) {
m_indicator = indicator;
emit indicatorChanged(m_indicator, QPrivateSignal{});
}
}
QWidget *IndicatorButtonAction::createWidget(QWidget *parent)
{
if (qobject_cast<QMenu *>(parent))
return nullptr;
IndicatorButton *button = new IndicatorButton(parent);
connect(this, &IndicatorButtonAction::indicatorChanged, button, &IndicatorButton::setIndicator);
connect(button, &IndicatorButton::indicatorChanged, this, &IndicatorButtonAction::setIndicator);
connect(button, &QToolButton::clicked, this, &QAction::trigger);
button->setIndicator(m_indicator);
button->setDefaultAction(this);
if (QToolBar *tb = qobject_cast<QToolBar *>(parent)) {
button->setAutoRaise(true);
button->setFocusPolicy(Qt::NoFocus);
button->setIconSize(tb->iconSize());
button->setToolButtonStyle(tb->toolButtonStyle());
connect(tb, &QToolBar::iconSizeChanged, button, &IndicatorButton::setIconSize);
connect(tb, &QToolBar::toolButtonStyleChanged, button, &IndicatorButton::setToolButtonStyle);
connect(button, &IndicatorButton::triggered, tb, &QToolBar::actionTriggered);
}
return button;
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,63 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QToolButton>
#include <QWidgetAction>
QT_BEGIN_NAMESPACE
class QStyleOptionToolButton;
class QPaintEvent;
class QStyleOptionMenuItem;
QT_END_NAMESPACE
namespace QmlDesigner {
class IndicatorButton : public QToolButton
{
Q_OBJECT
Q_PROPERTY(bool indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL)
public:
explicit IndicatorButton(QWidget *parent = nullptr);
bool indicator() const;
void setIndicator(bool newIndicator);
protected:
QSize sizeHint() const override;
void paintEvent(QPaintEvent *event) override;
void initMenuStyleOption(QMenu *menu, QStyleOptionMenuItem *option, const QAction *action) const;
signals:
void indicatorChanged(bool);
private:
bool m_indicator = false;
using Super = QToolButton;
};
class IndicatorButtonAction : public QWidgetAction
{
Q_OBJECT
public:
explicit IndicatorButtonAction(const QString &description,
const QIcon &icon,
QObject *parent = nullptr);
virtual ~IndicatorButtonAction();
public slots:
void setIndicator(bool indicator);
protected:
virtual QWidget *createWidget(QWidget *parent) override;
private:
bool m_indicator;
signals:
void indicatorChanged(bool, QPrivateSignal);
};
} // namespace QmlDesigner

View File

@@ -9,7 +9,7 @@
#include "modelnodecontextmenu.h"
#include "qmldesignerconstants.h"
#include <model/modelutils.h>
#include <modelutils.h>
#include <QDebug>
#include <QGraphicsSceneDragDropEvent>

View File

@@ -10,6 +10,8 @@
#include <QComboBox>
#include <QPainter>
#include <QStandardItemModel>
namespace QmlDesigner {
BackgroundAction::BackgroundAction(QObject *parent) :
@@ -21,7 +23,17 @@ void BackgroundAction::setColor(const QColor &color)
{
if (m_comboBox)
m_comboBox->setCurrentIndex(colors().indexOf(color));
}
void BackgroundAction::setColorEnabled(const QColor &color, bool enable)
{
if (!m_comboBox)
return;
QStandardItemModel *model = qobject_cast<QStandardItemModel *>(m_comboBox->model());
if (QStandardItem *item = model->item(colors().indexOf(color)))
item->setFlags(enable ? item->flags() | Qt::ItemIsEnabled
: item->flags() & ~Qt::ItemIsEnabled);
}
QIcon iconForColor(const QColor &color) {

View File

@@ -20,6 +20,7 @@ public:
explicit BackgroundAction(QObject *parent);
void setColor(const QColor &color);
void setColorEnabled(const QColor &color, bool enable);
signals:
void backgroundChanged(const QColor &color);

View File

@@ -200,7 +200,7 @@ qreal FormEditorItem::selectionWeigth(const QPointF &point, int iteration)
return weight;
}
void FormEditorItem::synchronizeOtherProperty(const QByteArray &propertyName)
void FormEditorItem::synchronizeOtherProperty(PropertyNameView propertyName)
{
if (propertyName == "opacity")
setOpacity(qmlItemNode().instanceValue("opacity").toDouble());
@@ -557,7 +557,7 @@ QmlItemNode FormEditorItem::qmlItemNode() const
return m_qmlItemNode;
}
void FormEditorFlowItem::synchronizeOtherProperty(const QByteArray &)
void FormEditorFlowItem::synchronizeOtherProperty(PropertyNameView)
{
setContentVisible(true);
}
@@ -783,7 +783,7 @@ QTransform FormEditorFlowActionItem::instanceSceneContentItemTransform() const
return sceneTransform();
}
void FormEditorTransitionItem::synchronizeOtherProperty(const QByteArray &)
void FormEditorTransitionItem::synchronizeOtherProperty(PropertyNameView)
{
setContentVisible(true);
}

View File

@@ -87,7 +87,7 @@ public:
QPointF center() const;
qreal selectionWeigth(const QPointF &point, int iteration);
virtual void synchronizeOtherProperty(const QByteArray &propertyName);
virtual void synchronizeOtherProperty(PropertyNameView propertyName);
virtual void setDataModelPosition(const QPointF &position);
virtual void setDataModelPositionInBaseState(const QPointF &position);
virtual QPointF instancePosition() const;
@@ -141,7 +141,7 @@ class FormEditorFlowItem : public FormEditorItem
friend FormEditorScene;
public:
void synchronizeOtherProperty(const QByteArray &propertyName) override;
void synchronizeOtherProperty(PropertyNameView propertyName) override;
void setDataModelPosition(const QPointF &position) override;
void setDataModelPositionInBaseState(const QPointF &position) override;
void updateGeometry() override;
@@ -198,7 +198,7 @@ class FormEditorTransitionItem : public FormEditorItem
friend FormEditorScene;
public:
void synchronizeOtherProperty(const QByteArray &propertyName) override;
void synchronizeOtherProperty(PropertyNameView propertyName) override;
void setDataModelPosition(const QPointF &position) override;
void setDataModelPositionInBaseState(const QPointF &position) override;
void updateGeometry() override;

View File

@@ -142,7 +142,7 @@ void FormEditorScene::synchronizeParent(const QmlItemNode &qmlItemNode)
reparentItem(qmlItemNode, parentNode);
}
void FormEditorScene::synchronizeOtherProperty(FormEditorItem *item, const QByteArray &propertyName)
void FormEditorScene::synchronizeOtherProperty(FormEditorItem *item, PropertyNameView propertyName)
{
Q_ASSERT(item);

View File

@@ -59,7 +59,7 @@ public:
void synchronizeTransformation(FormEditorItem *item);
void synchronizeParent(const QmlItemNode &qmlItemNode);
void synchronizeOtherProperty(FormEditorItem *item, const QByteArray &propertyName);
void synchronizeOtherProperty(FormEditorItem *item, PropertyNameView propertyName);
FormEditorItem* calulateNewParent(FormEditorItem *widget);
LayerItem* manipulatorLayerItem() const;

View File

@@ -364,7 +364,6 @@ WidgetInfo FormEditorView::widgetInfo()
return createWidgetInfo(m_formEditorWidget.data(),
"FormEditor",
WidgetInfo::CentralPane,
0,
tr("2D"),
tr("2D view"),
DesignerWidgetFlags::IgnoreErrors);
@@ -996,10 +995,8 @@ void FormEditorView::setupRootItemSize()
formEditorWidget()->setRootItemRect(rootRect);
formEditorWidget()->centerScene();
auto contextImage = rootModelNode().auxiliaryData(contextImageProperty);
if (contextImage)
m_formEditorWidget->setBackgoundImage(contextImage.value().value<QImage>());
if (auto contextImage = rootModelNode().auxiliaryData(contextImageProperty))
formEditorWidget()->setBackgoundImage(contextImage.value().value<QImage>());
}
}

View File

@@ -330,24 +330,14 @@ void FormEditorWidget::changeRootItemHeight(const QString &heighText)
}
}
namespace {
constexpr AuxiliaryDataKeyView formeditorColorProperty{AuxiliaryDataType::Temporary,
"formeditorColor"};
}
void FormEditorWidget::changeBackgound(const QColor &color)
{
if (color.alpha() == 0) {
if (color.alpha() == 0)
m_graphicsView->activateCheckboardBackground();
if (m_formEditorView->rootModelNode().hasAuxiliaryData(formeditorColorProperty)) {
m_formEditorView->rootModelNode().setAuxiliaryDataWithoutLock(formeditorColorProperty,
{});
}
} else {
else
m_graphicsView->activateColoredBackground(color);
m_formEditorView->rootModelNode().setAuxiliaryDataWithoutLock(formeditorColorProperty,
color);
}
m_formEditorView->rootModelNode().setAuxiliaryDataWithoutLock(formeditorColorProperty, color);
}
void FormEditorWidget::registerActionAsCommand(
@@ -408,8 +398,12 @@ void FormEditorWidget::updateActions()
m_backgroundAction->setColor(Qt::transparent);
}
if (m_formEditorView->rootModelNode().hasAuxiliaryData(contextImageProperty))
if (m_formEditorView->rootModelNode().hasAuxiliaryData(contextImageProperty)) {
m_backgroundAction->setColorEnabled(BackgroundAction::ContextImage, true);
m_backgroundAction->setColor(BackgroundAction::ContextImage);
} else {
m_backgroundAction->setColorEnabled(BackgroundAction::ContextImage, false);
}
} else {
m_rootWidthAction->clearLineEditText();

View File

@@ -34,6 +34,12 @@ void Import3dCanvas::updateRenderImage(const QImage &img)
update();
}
void Import3dCanvas::displayError(const QString &error)
{
m_errorMsg = error;
update();
}
void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e)
{
QWidget::paintEvent(e);
@@ -46,6 +52,9 @@ void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e)
} else {
painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height()));
}
if (!m_errorMsg.isEmpty())
painter.drawText(QRect(0, 0, width(), height()), Qt::AlignHCenter | Qt::AlignVCenter, m_errorMsg);
}
void Import3dCanvas::resizeEvent(QResizeEvent *)

View File

@@ -17,6 +17,7 @@ public:
Import3dCanvas(QWidget *parent);
void updateRenderImage(const QImage &img);
void displayError(const QString &error);
signals:
void requestImageUpdate();
@@ -32,6 +33,7 @@ protected:
private:
QImage m_image;
QPointF m_dragPos;
QString m_errorMsg;
};
} // namespace QmlDesigner

View File

@@ -16,6 +16,11 @@ Import3dConnectionManager::Import3dConnectionManager()
connections().emplace_back("Import 3D", "import3dmode");
}
void Import3dConnectionManager::setPreviewIconCallback(IconCallback callback)
{
m_previewIconCallback = std::move(callback);
}
void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback)
{
m_previewImageCallback = std::move(callback);
@@ -29,6 +34,15 @@ void Import3dConnectionManager::dispatchCommand(const QVariant &command,
if (command.typeId() == commandType) {
auto cmd = command.value<PuppetToCreatorCommand>();
switch (cmd.type()) {
case PuppetToCreatorCommand::Import3DPreviewIcon: {
const QVariantList data = cmd.data().toList();
const QString assetName = data[0].toString();
ImageContainer container = qvariant_cast<ImageContainer>(data[1]);
QImage image = container.image();
if (!image.isNull())
m_previewIconCallback(assetName, image);
break;
}
case PuppetToCreatorCommand::Import3DPreviewImage: {
ImageContainer container = qvariant_cast<ImageContainer>(cmd.data());
QImage image = container.image();

View File

@@ -12,16 +12,19 @@ namespace QmlDesigner {
class Import3dConnectionManager : public InteractiveConnectionManager
{
public:
using IconCallback = std::function<void(const QString &, const QImage &)>;
using ImageCallback = std::function<void(const QImage &)>;
Import3dConnectionManager();
void setPreviewIconCallback(IconCallback callback);
void setPreviewImageCallback(ImageCallback callback);
protected:
void dispatchCommand(const QVariant &command, Connection &connection) override;
private:
IconCallback m_previewIconCallback;
ImageCallback m_previewImageCallback;
};

View File

@@ -7,6 +7,8 @@
#include <designermcumanager.h>
#include <utils/algorithm.h>
#include <qmldesignerbase/qmldesignerbaseplugin.h>
#include <QDebug>
#include <QVariant>
#include <QMetaProperty>
@@ -73,6 +75,9 @@ void ItemLibraryAddImportModel::update(const Imports &possibleImports)
const DesignerMcuManager &mcuManager = DesignerMcuManager::instance();
const bool isQtForMCUs = mcuManager.isMCUProject();
Imports filteredImports;
const bool isLiteDesigner = QmlDesigner::QmlDesignerBasePlugin::isLiteModeEnabled();
if (isQtForMCUs) {
const QStringList mcuAllowedList = mcuManager.allowedImports();
const QStringList mcuBannedList = mcuManager.bannedImports();
@@ -82,6 +87,11 @@ void ItemLibraryAddImportModel::update(const Imports &possibleImports)
|| !import.url().startsWith("Qt"))
&& !mcuBannedList.contains(import.url());
});
} else if (isLiteDesigner) {
const QStringList liteAllowedList = {"QtQuick", "QtQuick.Layouts", "QtQuick.Controls"};
filteredImports = Utils::filtered(possibleImports, [&](const Import &import) {
return (liteAllowedList.contains(import.url()) || !import.url().startsWith("Qt"));
});
} else {
filteredImports = possibleImports;
}

View File

@@ -7,8 +7,10 @@
#include "import3dcanvas.h"
#include "import3dconnectionmanager.h"
#include <designeractionmanager.h>
#include <designericons.h>
#include <model.h>
#include <model/modelutils.h>
#include <modelutils.h>
#include <nodeinstanceview.h>
#include <nodemetainfo.h>
#include <qmldesignerconstants.h>
@@ -18,6 +20,7 @@
#include <theme.h>
#include <utils/outputformatter.h>
#include <utils/stylehelper.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
@@ -25,7 +28,9 @@
#include <coreplugin/icore.h>
#include <QFileInfo>
#include <QFontMetrics>
#include <QDir>
#include <QLocale>
#include <QLoggingCategory>
#include <QTimer>
#include <QJsonArray>
@@ -63,6 +68,17 @@ void addFormattedMessage(Utils::OutputFormatter *formatter,
formatter->plainTextEdit()->verticalScrollBar()->maximum());
}
QIcon iconFromIconFont(Theme::Icon iconType, const QColor &color)
{
const QString unicode = Theme::getIconUnicode(iconType);
const QString fontName = "qtds_propertyIconFont.ttf";
const auto helper = Utils::StyleHelper::IconFontHelper(
unicode, color, QSize(28, 28), QIcon::Normal);
return Utils::StyleHelper::getIconFromIconFont(fontName, {helper});
}
const int rowHeight = 32;
const int checkBoxColWidth = 18;
const int labelMinWidth = 130;
@@ -88,6 +104,11 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
setModal(true);
ui->setupUi(this);
m_selectedRemoveIcon = iconFromIconFont(Theme::Icon::delete_small,
Theme::getColor(Theme::IconsBaseColor));
m_unselectedRemoveIcon = iconFromIconFont(Theme::Icon::delete_small,
Theme::getColor(Theme::IconsDisabledColor));
m_outputFormatter = new Utils::OutputFormatter;
m_outputFormatter->setPlainTextEdit(ui->plainTextEdit);
@@ -239,20 +260,18 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
connect(ui->advancedSettingsButton, &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::toggleAdvanced);
connect(ui->importList, &QListWidget::currentItemChanged,
this, &ItemLibraryAssetImportDialog::onCurrentItemChanged);
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::updateUi);
if (m_quick3DFiles.size() != 1) {
addInfo(tr("Select import options and press \"Import\" to import the following files:"));
} else {
addInfo(tr("Importing:"));
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport);
}
addInfo(tr("Importing:"));
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport);
for (const auto &file : std::as_const(m_quick3DFiles))
addInfo(file);
updateImportButtonState();
m_updatingControlStates = false;
}
ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog()
@@ -385,6 +404,15 @@ void ItemLibraryAssetImportDialog::updateImport(AbstractView *view,
}
}
void ItemLibraryAssetImportDialog::keyPressEvent(QKeyEvent *event)
{
if ((event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete)
&& ui->importList->currentItem()) {
onRemoveAsset(assetNameForListItem(ui->importList->currentItem()));
}
return QDialog::keyPressEvent(event);
}
void ItemLibraryAssetImportDialog::createTab(const QString &tabLabel, int optionsIndex,
const QJsonObject &groups)
{
@@ -501,7 +529,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonValue value(optCheck->isChecked());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
updateImportButtonState();
updatePreviewOptions();
});
} else {
// Simple options also exist in advanced, so don't connect simple controls directly
@@ -512,13 +540,13 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QObject::connect(optCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() {
if (advCheck->isChecked() != optCheck->isChecked()) {
advCheck->setChecked(optCheck->isChecked());
updateImportButtonState();
updatePreviewOptions();
}
});
QObject::connect(advCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() {
if (advCheck->isChecked() != optCheck->isChecked()) {
optCheck->setChecked(advCheck->isChecked());
updateImportButtonState();
updatePreviewOptions();
}
});
}
@@ -555,7 +583,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonValue value(optSpin->value());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
updateImportButtonState();
updatePreviewOptions();
});
} else {
auto *advSpin = qobject_cast<QDoubleSpinBox *>(
@@ -566,14 +594,14 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
this, [this, optSpin, advSpin] {
if (advSpin->value() != optSpin->value()) {
advSpin->setValue(optSpin->value());
updateImportButtonState();
updatePreviewOptions();
}
});
QObject::connect(advSpin, &QDoubleSpinBox::valueChanged,
this, [this, optSpin, advSpin] {
if (advSpin->value() != optSpin->value()) {
optSpin->setValue(advSpin->value());
updateImportButtonState();
updatePreviewOptions();
}
});
}
@@ -830,6 +858,15 @@ void ItemLibraryAssetImportDialog::updateUi()
}
}
QString ItemLibraryAssetImportDialog::assetNameForListItem(QListWidgetItem *item)
{
for (const ImportData &data : std::as_const(m_importData)) {
if (data.listItem == item)
return data.previewData.name;
}
return {};
}
bool ItemLibraryAssetImportDialog::isSimpleGroup(const QString &id)
{
static QStringList simpleGroups {
@@ -859,6 +896,15 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id)
return hiddenOptions.contains(id);
}
bool ItemLibraryAssetImportDialog::optionsChanged()
{
for (const ImportData &data : std::as_const(m_importData)) {
if (data.previewData.renderedOptions != data.previewData.currentOptions)
return true;
}
return false;
}
void ItemLibraryAssetImportDialog::startPreview()
{
cleanupPreviewPuppet();
@@ -876,8 +922,9 @@ Rectangle {
property alias sceneNode: sceneNode
property alias view3d: view3d
property alias iconView3d: iconView3d
property string extents
property string sceneModelName: "%3"
property string sceneModelName
gradient: Gradient {
GradientStop { position: 1.0; color: "#222222" }
@@ -890,30 +937,46 @@ Rectangle {
camera: viewCamera
environment: SceneEnvironment {
id: sceneEnvironment
lightProbe: probeTexture
antialiasingMode: SceneEnvironment.MSAA
antialiasingQuality: SceneEnvironment.VeryHigh
}
PerspectiveCamera {
id: viewCamera
x: 600
y: 600
z: 600
eulerRotation.x: -45
eulerRotation.y: -45
clipFar: 100000
clipNear: 10
}
DirectionalLight {
rotation: viewCamera.rotation
}
Node {
id: sceneNode
PerspectiveCamera {
id: viewCamera
x: 600
y: 600
z: 600
eulerRotation.x: -45
eulerRotation.y: -45
clipFar: 100000
clipNear: 10
}
DirectionalLight {
rotation: viewCamera.rotation
}
Texture {
id: probeTexture
source: "qrc:/qtquickplugin/mockfiles/images/preview_studio.hdr"
}
}
}
View3D {
id: iconView3d
importScene: sceneNode
camera: viewCamera
environment: sceneEnvironment
visible: false
width: 48
height: 48
}
Text {
anchors.bottom: parent.bottom
anchors.left: parent.left
@@ -925,7 +988,7 @@ Rectangle {
)";
QSize size = canvas()->size();
previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName);
previewQml = previewQml.arg(size.width()).arg(size.height());
m_previewFile.writeFileContents(previewQml.toUtf8());
@@ -957,17 +1020,29 @@ Rectangle {
return;
}
m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target());
m_nodeInstanceView->setTarget(ProjectExplorer::ProjectManager::startupTarget());
auto previewIconCallback = [this](const QString &assetName, const QImage &image) {
if (!m_importData.contains(assetName)) {
addWarning(tr("Preview icon generated for non-existent asset: %1").arg(assetName));
return;
}
if (m_importData[assetName].iconLabel)
m_importData[assetName].iconLabel->setPixmap(QPixmap::fromImage(image));
};
auto previewImageCallback = [this](const QImage &image) {
canvas()->updateRenderImage(image);
};
auto crashCallback = [&] {
addWarning("Preview process crashed.");
cleanupPreviewPuppet();
const QString errorMsg(tr("Preview generation process crashed."));
addWarning(errorMsg);
canvas()->displayError(errorMsg);
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::cleanupPreviewPuppet);
};
m_connectionManager->setPreviewIconCallback(std::move(previewIconCallback));
m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback));
m_nodeInstanceView->setCrashCallback(std::move(crashCallback));
@@ -985,8 +1060,10 @@ void ItemLibraryAssetImportDialog::cleanupPreviewPuppet()
if (m_nodeInstanceView)
m_nodeInstanceView->setCrashCallback({});
if (m_connectionManager)
if (m_connectionManager) {
m_connectionManager->setPreviewIconCallback({});
m_connectionManager->setPreviewImageCallback({});
}
delete m_rewriterView;
delete m_nodeInstanceView;
@@ -998,6 +1075,31 @@ Import3dCanvas *ItemLibraryAssetImportDialog::canvas()
return ui->import3dcanvas;
}
void ItemLibraryAssetImportDialog::resetOptionControls()
{
const QString currentName = assetNameForListItem(ui->importList->currentItem());
if (!m_importData.contains(currentName))
return;
m_updatingControlStates = true;
const ImportData &data = m_importData[currentName];
const QJsonObject options = data.previewData.currentOptions;
const QStringList optKeys = options.keys();
for (const QString &optKey : optKeys) {
QWidget *w = m_labelToControlWidgetMaps[data.previewData.optionsIndex].value(optKey);
const QJsonObject optObj = options.value(optKey).toObject();
const QJsonValue optValue = optObj.value("value");
if (auto *cb = qobject_cast<QCheckBox *>(w))
cb->setChecked(optValue.toBool());
else if (auto *spin = qobject_cast<QDoubleSpinBox *>(w))
spin->setValue(optValue.toDouble());
}
m_updatingControlStates = false;
updatePreviewOptions();
}
void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
{
m_dialogHeight = event->size().height();
@@ -1010,9 +1112,20 @@ void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing)
ui->closeButton->setText(importing ? tr("Cancel") : tr("Close"));
}
void ItemLibraryAssetImportDialog::updateImportButtonState()
void ItemLibraryAssetImportDialog::updatePreviewOptions()
{
ui->importButton->setText(m_previewOptions == m_importOptions ? tr("Accept") : tr("Import"));
if (m_updatingControlStates)
return;
if (ui->importList->currentRow() >= 0) {
const QString assetName = assetNameForListItem(ui->importList->currentItem());
if (m_importData.contains(assetName)) {
ImportData &data = m_importData[assetName];
data.previewData.currentOptions = m_importOptions[data.previewData.optionsIndex];
}
}
ui->importButton->setText(optionsChanged() ? tr("Import") : tr("Accept"));
}
void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath)
@@ -1035,19 +1148,29 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s
void ItemLibraryAssetImportDialog::onImport()
{
ui->importButton->setEnabled(false);
ui->tabWidget->setEnabled(false);
ui->importList->setEnabled(false);
if (!m_previewCompName.isEmpty() && m_previewOptions == m_importOptions) {
if (!m_importData.isEmpty() && !optionsChanged()) {
cleanupPreviewPuppet();
m_importer.finalizeQuick3DImport();
return;
}
const QString assetName = assetNameForListItem(ui->importList->currentItem());
const ImportData &data = m_importData.value(assetName);
setCloseButtonState(true);
ui->progressBar->setValue(0);
if (!m_quick3DFiles.isEmpty()) {
if (!m_previewCompName.isEmpty()) {
m_importer.reImportQuick3D(m_previewCompName, m_importOptions);
if (!m_importData.isEmpty()) {
QHash<QString , QJsonObject> importOptions;
for (const ImportData &data : std::as_const(m_importData)) {
if (data.previewData.renderedOptions != data.previewData.currentOptions)
importOptions.insert(data.previewData.name, data.previewData.currentOptions);
}
m_importer.reImportQuick3D(importOptions);
} else {
m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath,
m_importOptions, m_extToImportOptionsMap,
@@ -1066,19 +1189,118 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t
ui->progressBar->setValue(value);
}
void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName)
void ItemLibraryAssetImportDialog::onImportReadyForPreview(
const QString &path, const QList<ItemLibraryAssetImporter::PreviewData> &previewData)
{
addInfo(tr("Import is ready for preview."));
if (m_previewCompName.isEmpty())
addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again."));
if (previewData.isEmpty()) {
m_importer.cancelImport();
return;
}
m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName());
m_previewCompName = compName;
m_previewOptions = m_importOptions;
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview);
QPixmap placeHolder = QPixmap(":/navigator/icon/tooltip_placeholder.png").scaled(48, 48);
int maxNameLen = 150;
// Used to initially layout infolabel with sufficient height
const QString tallStr = "Wj\nWj\nWj";
QStringList assetNames;
for (const ItemLibraryAssetImporter::PreviewData &data : previewData) {
const QString assetName = data.name;
assetNames.append(assetName);
if (!m_importData.contains(assetName)) {
ImportData impData;
impData.previewData = data;
auto lwi = new QListWidgetItem();
impData.listItem = lwi;
auto w = new QWidget(ui->importList);
w->setToolTip(assetName);
auto layout = new QHBoxLayout(w);
auto iconLabel = new QLabel(w);
iconLabel->setPixmap(placeHolder);
impData.iconLabel = iconLabel;
layout->addWidget(iconLabel);
auto infoLabel = new QLabel(w);
impData.infoLabel = infoLabel;
infoLabel->setText(tallStr);
infoLabel->setFixedWidth(maxNameLen);
layout->addWidget(infoLabel);
layout->addStretch(1);
auto removeButton = new QPushButton(m_unselectedRemoveIcon, {}, w);
removeButton->setFlat(true);
impData.removeButton = removeButton;
layout->addWidget(removeButton, 0, Qt::AlignRight);
layout->setSizeConstraint(QLayout::SetNoConstraint);
w->setLayout(layout);
w->resize(w->height(), ui->importList->width());
lwi->setSizeHint(w->sizeHint());
ui->importList->addItem(lwi);
ui->importList->setItemWidget(lwi, w);
m_importData[assetName] = impData;
QObject::connect(removeButton, &QPushButton::clicked, this, [this, assetName]() {
onRemoveAsset(assetName);
});
} else {
m_importData[assetName].previewData = data;
}
if (!m_importData.contains(assetName))
return;
const ImportData &impData = m_importData[assetName];
if (QLabel *l = impData.infoLabel) {
QFontMetrics fm = l->fontMetrics();
QString truncNameBase = assetName;
QString truncName = assetName;
int truncNameLen = fm.boundingRect(truncName).width();
while (!truncNameBase.isEmpty() && truncNameLen > maxNameLen) {
truncNameBase.chop(1);
truncName = truncNameBase + "...";
truncNameLen = fm.boundingRect(truncName).width();
}
QString s;
s += truncName + '\n';
s += tr("Object Type: %1\n").arg(data.type);
s += tr("Import Size: %1").arg(QLocale::system().formattedDataSize(
data.size, 2, QLocale::DataSizeTraditionalFormat));
l->setText(s);
}
addInfo(tr("Import ready for preview: %1").arg(assetName));
}
if (m_firstImport) {
addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again."));
m_firstImport = false;
}
if (m_previewFile.isEmpty()) {
m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName());
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview);
}
QTimer::singleShot(0, this, [this, assetNames]() {
if (!m_nodeInstanceView)
return;
for (const QString &assetName : std::as_const(assetNames)) {
const ImportData &data = m_importData.value(assetName);
if (!data.previewData.name.isEmpty()) {
QVariantHash msgData;
msgData.insert("name", data.previewData.name);
msgData.insert("qmlName", data.previewData.qmlName);
msgData.insert("folder", data.previewData.folderName);
m_nodeInstanceView->view3DAction(View3DActionType::Import3dAddPreviewModel, msgData);
}
}
});
ui->importButton->setEnabled(true);
updateImportButtonState();
ui->tabWidget->setEnabled(true);
ui->importList->setEnabled(true);
updatePreviewOptions();
if (ui->importList->currentRow() < 0)
ui->importList->setCurrentRow(0);
}
void ItemLibraryAssetImportDialog::onRequestImageUpdate()
@@ -1119,6 +1341,37 @@ void ItemLibraryAssetImportDialog::onImportFinished()
}
}
void ItemLibraryAssetImportDialog::onCurrentItemChanged(QListWidgetItem *current, QListWidgetItem *)
{
if (!current)
return;
for (const ImportData &data : std::as_const(m_importData)) {
if (data.removeButton) {
if (current == data.listItem)
data.removeButton->setIcon(m_selectedRemoveIcon);
else
data.removeButton->setIcon(m_unselectedRemoveIcon);
}
}
const QString assetName = assetNameForListItem(ui->importList->currentItem());
resetOptionControls();
const ImportData data = m_importData.value(assetName);
for (int i = 0; i < ui->tabWidget->count(); ++i)
ui->tabWidget->widget(i)->setVisible(i == data.previewData.optionsIndex);
ui->tabWidget->setCurrentIndex(data.previewData.optionsIndex);
QTimer::singleShot(0, this, [this, assetName]() {
if (!m_nodeInstanceView)
return;
if (m_importData.contains(assetName)) {
m_nodeInstanceView->view3DAction(View3DActionType::Import3dSetCurrentPreviewModel,
assetName);
}
});
}
void ItemLibraryAssetImportDialog::onClose()
{
ui->importButton->setEnabled(false);
@@ -1164,4 +1417,20 @@ void ItemLibraryAssetImportDialog::toggleAdvanced()
updateUi();
}
void ItemLibraryAssetImportDialog::onRemoveAsset(const QString &assetName)
{
m_importer.removeAssetFromImport(assetName);
if (m_importData.contains(assetName)) {
ImportData data = m_importData.take(assetName);
addInfo(tr("Removed %1 from the import.").arg(assetName));
if (data.listItem) {
ui->importList->removeItemWidget(data.listItem);
delete data.listItem;
}
}
if (m_importData.isEmpty())
onClose();
}
}

Some files were not shown because too many files have changed in this diff Show More