diff options
Diffstat (limited to 'src/plugins/qmldesigner/components')
61 files changed, 1443 insertions, 901 deletions
diff --git a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp index 0b73a1f39d..f98a32b969 100644 --- a/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/changestyleaction.cpp @@ -98,11 +98,7 @@ QWidget *ChangeStyleWidgetAction::createWidget(QWidget *parent) }); connect(comboBox, - #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - QOverload<const QString &>::of(&QComboBox::activated), - #else &QComboBox::textActivated, - #endif this, [this](const QString &style) { diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore.pri b/src/plugins/qmldesigner/components/componentcore/componentcore.pri index 174ed0341a..a64f61ab46 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore.pri +++ b/src/plugins/qmldesigner/components/componentcore/componentcore.pri @@ -15,6 +15,7 @@ SOURCES += modelnodecontextmenu_helper.cpp SOURCES += selectioncontext.cpp SOURCES += designeractionmanager.cpp SOURCES += modelnodeoperations.cpp +SOURCES += formatoperation.cpp SOURCES += navigation2d.cpp SOURCES += crumblebar.cpp SOURCES += qmldesignericonprovider.cpp @@ -37,6 +38,7 @@ HEADERS += selectioncontext.h HEADERS += componentcore_constants.h HEADERS += designeractionmanager.h HEADERS += modelnodeoperations.h +HEADERS += formatoperation.h HEADERS += navigation2d.h HEADERS += actioninterface.h HEADERS += crumblebar.h diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 69e4e39b55..9024b32776 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -55,6 +55,8 @@ 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"; @@ -123,6 +125,8 @@ const char visibilityDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", 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"); const char goIntoComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go into Component"); const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Merge File With Template"); @@ -176,6 +180,8 @@ const char lowerToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Lower s 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."); const char anchorsFillToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fill selected component to parent."); const char anchorsResetToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Reset anchors for selected component."); diff --git a/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp b/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp index 33f44af9dc..c729fd0751 100644 --- a/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp +++ b/src/plugins/qmldesigner/components/componentcore/crumblebar.cpp @@ -178,7 +178,7 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data) } else { crumblePath()->popElement(); nextFileIsCalledInternally(); - Core::EditorManager::openEditor(clickedCrumbleBarInfo.fileName.toString(), + Core::EditorManager::openEditor(clickedCrumbleBarInfo.fileName, Utils::Id(), Core::EditorManager::DoNotMakeVisible); if (clickedCrumbleBarInfo.modelNode.isValid()) { diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 808882220e..dbfa2f82d0 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -28,6 +28,7 @@ #include "changestyleaction.h" #include "designeractionmanagerview.h" #include "modelnodecontextmenu_helper.h" +#include "formatoperation.h" #include "qmldesignerconstants.h" #include "rewritingexception.h" #include <bindingproperty.h> @@ -909,11 +910,13 @@ bool anchorsMenuEnabled(const SelectionContext &context) || singleSelectionItemIsAnchored(context); } + void DesignerActionManager::createDefaultDesignerActions() { using namespace SelectionContextFunctors; using namespace ComponentCoreConstants; using namespace ModelNodeOperations; + using namespace FormatOperation; const Utils::Icon prevIcon({ {QLatin1String(":/utils/images/prev.png"), Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle); @@ -1002,6 +1005,29 @@ void DesignerActionManager::createDefaultDesignerActions() &selectionNotEmptyAndHasXorYProperty)); addDesignerAction(new ModelNodeAction( + copyFormatCommandId, + copyFormatDisplayName, + Utils::Icon({{":/qmldesigner/icon/designeractions/images/raise.png", Utils::Theme::IconsBaseColor}}).icon(), + copyFormatTooltip, + editCategory, + QKeySequence(), + 120, + ©Format, + &propertiesCopyable)); + + addDesignerAction(new ModelNodeAction( + applyFormatCommandId, + applyFormatDisplayName, + Utils::Icon({{":/qmldesigner/icon/designeractions/images/lower.png", Utils::Theme::IconsBaseColor}}).icon(), + applyFormatTooltip, + editCategory, + QKeySequence(), + 120, + &applyFormat, + &propertiesApplyable)); + + + addDesignerAction(new ModelNodeAction( resetSizeCommandId, resetSizeDisplayName, Utils::Icon({{":/utils/images/fittoview.png", Utils::Theme::IconsBaseColor}, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp index 8a6c06fcdc..026596b876 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp @@ -112,7 +112,7 @@ void DesignerActionManagerView::selectedNodesChanged(const QList<ModelNode> &sel emit selectionChanged(!selectedNodes.isEmpty(), singleSelectedModelNode().isRootNode()); } -void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &, const ModelNode &, int) +void DesignerActionManagerView::nodeOrderChanged(const NodeListProperty &) { setupContext(SelectionContext::UpdateMode::NodeHierachy); } diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h index a7a34271ea..f7777bcac1 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h @@ -58,7 +58,7 @@ public: void currentStateChanged(const ModelNode &) override; void selectedNodesChanged(const QList<ModelNode> &, const QList<ModelNode> &) override; - void nodeOrderChanged(const NodeListProperty &, const ModelNode &, int ) override; + void nodeOrderChanged(const NodeListProperty &) override; void importsChanged(const QList<Import> &, const QList<Import> &) override; void signalHandlerPropertiesChanged(const QVector<SignalHandlerProperty> &/*propertyList*/, PropertyChangeFlags /*propertyChange*/) override; void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChangeFlag) override; diff --git a/src/plugins/qmldesigner/components/componentcore/formatoperation.cpp b/src/plugins/qmldesigner/components/componentcore/formatoperation.cpp new file mode 100644 index 0000000000..cfa3bf4f9e --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/formatoperation.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "formatoperation.h" +#include "utils/fileutils.h" + +#include <coreplugin/icore.h> +#include <qmlobjectnode.h> +#include <nodemetainfo.h> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +namespace QmlDesigner { +namespace FormatOperation{ + +void readFormatConfiguration(){ + + if (copyableProperties.isEmpty()){ + QString source = "formatconfiguration.json"; + Utils::FilePath path = Core::ICore::resourcePath() + "/qmldesigner/" + source; + QString errorString; + Utils::FileReader reader; + + if (reader.fetch(path, &errorString)){ + QJsonParseError jsonError; + QJsonDocument document = QJsonDocument::fromJson(reader.data(), &jsonError ); + + if (jsonError.error != QJsonParseError::NoError) + return; + + QJsonObject jsonObject = document.object(); + QVariantMap rootMap = jsonObject.toVariantMap(); + QJsonArray jsonArray = rootMap["propertylist"].toJsonArray(); + + for (int i=0; i< jsonArray.count(); ++i){ + auto item = jsonArray.at(i).toObject(); + QVariantMap itemMap = item.toVariantMap(); + StylePropertyStruct current; + current.id = itemMap["id"].toString(); + QVariantList subclassVariantList = itemMap["subclasses"].toList(); + QStringList subclassList; + + for (auto subclass : subclassVariantList) + subclassList.append(subclass.toString()); + + current.subclasses = subclassList; + + QVariantList propertyList = itemMap["properties"].toList(); + QStringList propertyStringList; + + for (auto property : propertyList) + propertyStringList.append(property.toString()); + + current.properties = propertyStringList; + copyableProperties.append(current); + } + } + } +} + +bool propertiesCopyable(const SelectionContext &selectionState) +{ + if (!selectionState.singleNodeIsSelected()) + return false; + + readFormatConfiguration(); + + ModelNode modelNode = selectionState.currentSingleSelectedNode(); + + for (StylePropertyStruct copyable : copyableProperties) + for (QString copyableSubclass : copyable.subclasses) + if (modelNode.metaInfo().isSubclassOf(copyableSubclass.toUtf8())) + return true; + + return false; +} + +bool propertiesApplyable(const SelectionContext &selectionState) +{ + if (selectionState.selectedModelNodes().isEmpty()) + return false; + + if (chosenItem.subclasses.isEmpty()) + return false; + + const ModelNode firstSelectedNode = selectionState.firstSelectedModelNode(); + bool found = false; + + for (QString copyableSubclass : chosenItem.subclasses){ + if (firstSelectedNode.metaInfo().isSubclassOf(copyableSubclass.toUtf8())){ + found = true; + break; + } + } + + if (!found) + return false; + + for (const ModelNode &modelNode : selectionState.selectedModelNodes()){ + found = false; + + for (QString subclass : chosenItem.subclasses) + if (modelNode.metaInfo().isSubclassOf(subclass.toUtf8())){ + found = true; + break; + } + + if (found) + continue; + + return false; + } + + return true; +} + +void copyFormat(const SelectionContext &selectionState) +{ + if (!selectionState.view()) + return; + + selectionState.view()->executeInTransaction("DesignerActionManager|copyFormat",[selectionState](){ + + applyableProperties.clear(); + + ModelNode node = selectionState.currentSingleSelectedNode(); + QStringList propertyList; + for (StylePropertyStruct copyable : copyableProperties){ + bool found = false; + for (QString copyableSubclass : copyable.subclasses) + if (node.metaInfo().isSubclassOf(copyableSubclass.toUtf8())){ + propertyList = copyable.properties; + chosenItem = copyable; + found = true; + break; + } + if (found) + break; + } + + QmlObjectNode qmlObjectNode(node); + + for (auto propertyName : propertyList){ + if (qmlObjectNode.propertyAffectedByCurrentState(propertyName.toUtf8())) { + StyleProperties property; + property.propertyName = propertyName.toUtf8(); + property.value = qmlObjectNode.modelValue(propertyName.toUtf8()); + applyableProperties.append(property); + } + } + }); +} + +void applyFormat(const SelectionContext &selectionState) +{ + if (!selectionState.view()) + return; + + selectionState.view()->executeInTransaction("DesignerActionManager|applyFormat",[selectionState](){ + + for (ModelNode node : selectionState.selectedModelNodes()) { + QmlObjectNode qmlObjectNode(node); + QStringList propertyList; + + for (StylePropertyStruct copyable : copyableProperties){ + bool found = false; + for (QString copyableSubclass : copyable.subclasses) + if (node.metaInfo().isSubclassOf(copyableSubclass.toUtf8())){ + propertyList = copyable.properties; + found = true; + break; + } + if (found) + break; + } + + for (auto propertyName : propertyList) + if (qmlObjectNode.propertyAffectedByCurrentState(propertyName.toUtf8())) + qmlObjectNode.removeProperty(propertyName.toUtf8()); + + for (StyleProperties currentProperty : applyableProperties) + if (node.metaInfo().hasProperty((currentProperty.propertyName))) + qmlObjectNode.setVariantProperty(currentProperty.propertyName, currentProperty.value); + } + }); +} + +} // namespace FormatOperation +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/formatoperation.h b/src/plugins/qmldesigner/components/componentcore/formatoperation.h new file mode 100644 index 0000000000..c4aa94476b --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/formatoperation.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "selectioncontext.h" + +namespace QmlDesigner { +namespace FormatOperation { + +struct StylePropertyStruct +{ +QString id; +QStringList subclasses; +QStringList properties; +}; + +struct StyleProperties +{ + QmlDesigner::PropertyName propertyName; + QVariant value; +}; + +static QList<StylePropertyStruct> copyableProperties = {}; +static QList<StyleProperties> applyableProperties = {}; +static StylePropertyStruct chosenItem = {}; + +bool propertiesCopyable(const SelectionContext &selectionState); +bool propertiesApplyable(const SelectionContext &selectionState); +void copyFormat(const SelectionContext &selectionState); +void applyFormat(const SelectionContext &selectionState); +void readFormatConfiguration(); + +} // namespace FormatOperation +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 3acfa1fce4..34c519e1bb 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1458,7 +1458,7 @@ void styleMerge(const SelectionContext &selectionContext, const QString &templat QPlainTextEdit textEditTemplate; Utils::FileReader reader; - QTC_ASSERT(reader.fetch(templateFile), return); + QTC_ASSERT(reader.fetch(Utils::FilePath::fromString(templateFile)), return); QString qmlTemplateString = QString::fromUtf8(reader.data()); QString imports; diff --git a/src/plugins/qmldesigner/components/componentcore/qmldesignericonprovider.cpp b/src/plugins/qmldesigner/components/componentcore/qmldesignericonprovider.cpp index a1ca4a42dc..cbc19b4362 100644 --- a/src/plugins/qmldesigner/components/componentcore/qmldesignericonprovider.cpp +++ b/src/plugins/qmldesigner/components/componentcore/qmldesignericonprovider.cpp @@ -42,9 +42,10 @@ QmlDesignerIconProvider::QmlDesignerIconProvider() } -static QString iconPath() +static Utils::FilePath iconPath() { - return Core::ICore::resourcePath() + QLatin1String("/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/"); + return Core::ICore::resourcePath( + "qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/images/"); } QPixmap QmlDesignerIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) @@ -69,18 +70,15 @@ QPixmap QmlDesignerIconProvider::getPixmap(const QString &id) else if (id == "plus") result = Utils::Icons::PLUS_TOOLBAR.pixmap(); else if (id == "expression") - result = Icon({ - { iconPath() + QLatin1String("expression.png"), Theme::QmlDesigner_HighlightColor}}).pixmap(); + result = Icon({{iconPath() / "expression.png", Theme::QmlDesigner_HighlightColor}}).pixmap(); else if (id == "placeholder") - result = Icon(iconPath() + "placeholder.png").pixmap(); + result = Icon(iconPath() / "placeholder.png").pixmap(); else if (id == "submenu") - result = Icon(iconPath() + "submenu.png").pixmap(); + result = Icon(iconPath() / "submenu.png").pixmap(); else if (id == "up-arrow") - result = Icon({ - { iconPath() + QLatin1String("up-arrow.png"), Theme::IconsBaseColor}}, Icon::Tint).pixmap(); + result = Icon({{iconPath() / "up-arrow.png", Theme::IconsBaseColor}}, Icon::Tint).pixmap(); else if (id == "down-arrow") - result = Icon({ - { iconPath() + QLatin1String("down-arrow.png"), Theme::IconsBaseColor}}, Icon::Tint).pixmap(); + result = Icon({{iconPath() / "down-arrow.png", Theme::IconsBaseColor}}, Icon::Tint).pixmap(); else if (id == "checkbox-indicator") result = Icon({ { ":/qmldesigner/images/checkbox_indicator.png", Theme::IconsBaseColor}}, Icon::Tint).pixmap(); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp index 2c31414db3..cb5cdb218e 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.cpp +++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp @@ -49,8 +49,10 @@ Theme::Theme(Utils::Theme *originTheme, QObject *parent) : Utils::Theme(originTheme, parent) , m_constants(nullptr) { - QString constantsPath = Core::ICore::resourcePath() + - QStringLiteral("/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml"); + QString constantsPath + = Core::ICore::resourcePath( + "qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml") + .toString(); QQmlEngine* engine = new QQmlEngine(this); QQmlComponent component(engine, QUrl::fromLocalFile(constantsPath)); diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index e2d26720ad..3d7ed9cd32 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -54,8 +54,8 @@ public: adsClose, adsDetach, adsDropDown, + alias, aliasAnimated, - aliasProperty, alignBottom, alignCenterHorizontal, alignCenterVertical, @@ -73,9 +73,15 @@ public: annotationBubble, annotationDecal, assign, + bevelAll, + bevelCorner, centerHorizontal, centerVertical, closeCross, + copyStyle, + cornerA, + cornerB, + cornersAll, curveDesigner, curveEditor, decisionNode, @@ -96,22 +102,32 @@ public: distributeSpacingVertical, distributeTop, edit, + eyeDropper, flowAction, flowTransition, fontStyleBold, fontStyleItalic, fontStyleStrikethrough, fontStyleUnderline, + gradient, gridView, idAliasOff, idAliasOn, + keyframe, + linkTriangle, + linked, listView, lockOff, lockOn, mergeCells, minus, + mirror, + paddingEdge, + paddingFrame, + pasteStyle, pin, plus, + promote, redo, rotationFill, rotationOutline, @@ -130,7 +146,9 @@ public: textFullJustification, textNumberedList, tickIcon, + transparent, triState, + unLinked, undo, unpin, upDownIcon, diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp index 8ed0653a11..092fd42358 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp @@ -88,8 +88,8 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent) applyZoom(m_zoomX, m_zoomY); update(); - const QString css = Theme::replaceCssColors(QString::fromUtf8( - Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css")))); + const QString css = Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); horizontalScrollBar()->setStyleSheet(css); verticalScrollBar()->setStyleSheet(css); } diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h index c2d2b0c714..22b474d1bb 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h +++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.h @@ -44,7 +44,7 @@ class GraphicsView : public QGraphicsView { Q_OBJECT - friend class Playhead; + friend Playhead; signals: void currentFrameChanged(int frame, bool notify); diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index c4dd86c44e..a8b0b34f10 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -513,16 +513,13 @@ void DebugView::currentStateChanged(const ModelNode &/*node*/) } -void DebugView::nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int oldIndex) +void DebugView::nodeOrderChanged(const NodeListProperty &listProperty) { if (isDebugViewEnabled()) { QTextStream message; QString string; message.setString(&string); - message << movedNode << listProperty; - message << oldIndex << "to" << listProperty.indexOf(movedNode); - log("::nodeSlide:", string); } } diff --git a/src/plugins/qmldesigner/components/debugview/debugview.h b/src/plugins/qmldesigner/components/debugview/debugview.h index ba6e053786..3e986917e1 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.h +++ b/src/plugins/qmldesigner/components/debugview/debugview.h @@ -88,7 +88,7 @@ public: void nodeAboutToBeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, PropertyChangeFlags propertyChange) override; void instancesToken(const QString &tokenName, int tokenNumber, const QVector<ModelNode> &nodeVector) override; void currentStateChanged(const ModelNode &node) override; - void nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int oldIndex) override; + void nodeOrderChanged(const NodeListProperty &listProperty) override; protected: void log(const QString &title, const QString &message, bool highlight = false); diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h index 02cbc5a289..c1fe3c34d5 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h @@ -40,7 +40,8 @@ class FormEditorView; class QMLDESIGNERCORE_EXPORT AbstractFormEditorTool { - friend class FormEditorView; + friend FormEditorView; + public: AbstractFormEditorTool(FormEditorView* view); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index 8304f1a36b..769488aa4a 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -49,7 +49,8 @@ namespace Internal { class QMLDESIGNERCORE_EXPORT FormEditorItem : public QGraphicsItem { - friend class QmlDesigner::FormEditorScene; + friend FormEditorScene; + public: ~FormEditorItem() override; @@ -156,7 +157,8 @@ private: // variables class FormEditorFlowItem : public FormEditorItem { - friend class QmlDesigner::FormEditorScene; + friend FormEditorScene; + public: void synchronizeOtherProperty(const QByteArray &propertyName) override; void setDataModelPosition(const QPointF &position) override; @@ -175,7 +177,8 @@ private: class FormEditorFlowActionItem : public FormEditorItem { - friend class QmlDesigner::FormEditorScene; + friend FormEditorScene; + public: void setDataModelPosition(const QPointF &position) override; void setDataModelPositionInBaseState(const QPointF &position) override; @@ -194,7 +197,8 @@ private: class FormEditorTransitionItem : public FormEditorItem { - friend class QmlDesigner::FormEditorScene; + friend FormEditorScene; + public: void synchronizeOtherProperty(const QByteArray &propertyName) override; void setDataModelPosition(const QPointF &position) override; @@ -220,7 +224,7 @@ private: class FormEditorFlowDecisionItem : FormEditorFlowItem { - friend class QmlDesigner::FormEditorScene; + friend FormEditorScene; public: void updateGeometry() override; @@ -243,7 +247,7 @@ protected: class FormEditorFlowWildcardItem : FormEditorFlowDecisionItem { - friend class QmlDesigner::FormEditorScene; + friend FormEditorScene; public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h index 6ac22f47f4..070d587776 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorscene.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorscene.h @@ -47,8 +47,8 @@ class QMLDESIGNERCORE_EXPORT FormEditorScene : public QGraphicsScene { Q_OBJECT - friend class QmlDesigner::FormEditorItem; - friend class QmlDesigner::FormEditorView; + friend FormEditorItem; + friend FormEditorView; public: diff --git a/src/plugins/qmldesigner/components/formeditor/resizecontroller.h b/src/plugins/qmldesigner/components/formeditor/resizecontroller.h index 334ce19c0c..c7846fd7c8 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizecontroller.h +++ b/src/plugins/qmldesigner/components/formeditor/resizecontroller.h @@ -37,7 +37,8 @@ class WeakResizeController; class ResizeController { - friend class WeakResizeController; + friend WeakResizeController; + public: ResizeController(); ResizeController(LayerItem *layerItem, FormEditorItem *formEditorItem); @@ -76,7 +77,8 @@ private: // variables class WeakResizeController { - friend class ResizeController; + friend ResizeController; + public: WeakResizeController(); WeakResizeController(const WeakResizeController &resizeController); diff --git a/src/plugins/qmldesigner/components/formeditor/rotationcontroller.h b/src/plugins/qmldesigner/components/formeditor/rotationcontroller.h index 52b5002387..ffca7bae5a 100644 --- a/src/plugins/qmldesigner/components/formeditor/rotationcontroller.h +++ b/src/plugins/qmldesigner/components/formeditor/rotationcontroller.h @@ -37,7 +37,8 @@ class WeakRotationController; class RotationController { - friend class WeakRotationController; + friend WeakRotationController; + public: RotationController(); RotationController(LayerItem *layerItem, FormEditorItem *formEditorItem); @@ -74,7 +75,8 @@ private: class WeakRotationController { - friend class RotationController; + friend RotationController; + public: WeakRotationController(); WeakRotationController(const WeakRotationController &rotationController); diff --git a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp deleted file mode 100644 index 9873963411..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.cpp +++ /dev/null @@ -1,437 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "customfilesystemmodel.h" - -#include <synchronousimagecache.h> -#include <theme.h> -#include <hdrimage.h> - -#include <utils/filesystemwatcher.h> - -#include <QDir> -#include <QDirIterator> -#include <QFileIconProvider> -#include <QFileSystemModel> -#include <QFont> -#include <QImageReader> -#include <QPainter> -#include <QRawFont> -#include <qmath.h> -#include <condition_variable> -#include <mutex> - -namespace QmlDesigner { - -static const QStringList &supportedImageSuffixes() -{ - static QStringList retList; - if (retList.isEmpty()) { - const QList<QByteArray> suffixes = QImageReader::supportedImageFormats(); - for (const QByteArray &suffix : suffixes) - retList.append(QString::fromUtf8(suffix)); - } - return retList; -} - -static const QStringList &supportedFragmentShaderSuffixes() -{ - static const QStringList retList {"frag", "glsl", "glslf", "fsh"}; - return retList; -} - -static const QStringList &supportedShaderSuffixes() -{ - static const QStringList retList {"frag", "vert", - "glsl", "glslv", "glslf", - "vsh", "fsh"}; - return retList; -} - -static const QStringList &supportedFontSuffixes() -{ - static const QStringList retList {"ttf", "otf"}; - return retList; -} - -static const QStringList &supportedAudioSuffixes() -{ - static const QStringList retList {"wav"}; - return retList; -} - -static const QStringList &supportedTexture3DSuffixes() -{ - // These are file types only supported by 3D textures - static QStringList retList {"hdr"}; - return retList; -} - -static QPixmap defaultPixmapForType(const QString &type, const QSize &size) -{ - return QPixmap(QStringLiteral(":/ItemLibrary/images/asset_%1_%2.png").arg(type).arg(size.width())); -} - -static QPixmap texturePixmap(const QString &fileName) -{ - return HdrImage{fileName}.toPixmap(); -} - -QString fontFamily(const QFileInfo &info) -{ - QRawFont font(info.absoluteFilePath(), 10); - if (font.isValid()) - return font.familyName(); - return {}; -} - -class ItemLibraryFileIconProvider : public QFileIconProvider -{ -public: - ItemLibraryFileIconProvider(SynchronousImageCache &fontImageCache, - QHash<QString, QPair<QDateTime, QIcon>> &iconCache) - : QFileIconProvider() - , m_fontImageCache(fontImageCache) - , m_iconCache(iconCache) - { - } - - QIcon icon( const QFileInfo & info ) const override - { - const QString filePath = info.absoluteFilePath(); - QPair<QDateTime, QIcon> &cachedIcon = m_iconCache[filePath]; - if (!cachedIcon.second.isNull() && cachedIcon.first == info.lastModified()) - return cachedIcon.second; - - QIcon icon; - const QString suffix = info.suffix().toLower(); - - // Provide icon depending on suffix - QPixmap origPixmap; - - if (supportedFontSuffixes().contains(suffix)) - return generateFontIcons(filePath); - else if (supportedImageSuffixes().contains(suffix)) - origPixmap.load(filePath); - else if (supportedTexture3DSuffixes().contains(suffix)) - origPixmap = texturePixmap(filePath); - - for (auto iconSize : iconSizes) { - QPixmap pixmap = origPixmap; - if (pixmap.isNull()) { - if (supportedAudioSuffixes().contains(suffix)) - pixmap = defaultPixmapForType("sound", iconSize); - else if (supportedShaderSuffixes().contains(suffix)) - pixmap = defaultPixmapForType("shader", iconSize); - if (pixmap.isNull()) - return QFileIconProvider::icon(info); - } - - if ((pixmap.width() > iconSize.width()) || (pixmap.height() > iconSize.height())) - pixmap = pixmap.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - - icon.addPixmap(pixmap); - } - - cachedIcon.first = info.lastModified(); - cachedIcon.second = icon; - return icon; - } - - QIcon generateFontIcons(const QString &filePath) const - { - return m_fontImageCache.icon( - filePath, - {}, - ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes}, - Theme::getColor(Theme::DStextColor).name(), - "Abc"}); - } - -private: - // Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their - // x2 versions for HDPI sceens - std::vector<QSize> iconSizes = {{384, 384}, - {192, 192}, // Large - {256, 256}, - {128, 128}, // Drag - {96, 96}, // Medium - {48, 48}, // Small - {64, 64}, - {32, 32}}; // List - - SynchronousImageCache &m_fontImageCache; - QHash<QString, QPair<QDateTime, QIcon>> &m_iconCache; -}; - -CustomFileSystemModel::CustomFileSystemModel(SynchronousImageCache &fontImageCache, QObject *parent) - : QAbstractListModel(parent) - , m_fileSystemModel(new QFileSystemModel(this)) - , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) - , m_fontImageCache(fontImageCache) -{ - m_updatePathTimer.setInterval(200); - m_updatePathTimer.setSingleShot(true); - m_updatePathTimer.callOnTimeout([this]() { - updatePath(m_fileSystemModel->rootPath()); - }); - - // If project directory contents change, or one of the asset files is modified, we must - // reconstruct the model to update the icons - connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this] { - m_updatePathTimer.start(); - }); - connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::fileChanged, [this] { - m_updatePathTimer.start(); - }); -} - -void CustomFileSystemModel::setFilter(QDir::Filters) -{ - -} - -bool filterMetaIcons(const QString &fileName) -{ - - QFileInfo info(fileName); - - if (info.dir().path().split("/").contains("designer")) { - - QDir currentDir = info.dir(); - - int i = 0; - while (!currentDir.isRoot() && i < 3) { - if (currentDir.dirName() == "designer") { - if (!currentDir.entryList({"*.metainfo"}).isEmpty()) - return false; - } - - currentDir.cdUp(); - ++i; - } - - if (info.dir().dirName() == "designer") - return false; - } - - return true; -} - -QModelIndex CustomFileSystemModel::setRootPath(const QString &newPath) -{ - if (m_fileSystemModel->rootPath() == newPath) - return QAbstractListModel::index(0, 0); - - return updatePath(newPath); -} - -QVariant CustomFileSystemModel::data(const QModelIndex &index, int role) const -{ - if (role == Qt::ToolTipRole) - return fileInfo(index).filePath(); - - if (role == Qt::FontRole) { - QFont font = m_fileSystemModel->data(fileSystemModelIndex(index), role).value<QFont>(); - font.setPixelSize(Theme::instance()->smallFontPixelSize()); - return font; - } - - - return m_fileSystemModel->data(fileSystemModelIndex(index), role); -} - -int CustomFileSystemModel::rowCount(const QModelIndex &) const -{ - return m_files.count(); -} - -int CustomFileSystemModel::columnCount(const QModelIndex &) const -{ - return 1; -} - -QModelIndex CustomFileSystemModel::indexForPath(const QString &path, int /*column*/) const -{ - return QAbstractListModel::index(m_files.indexOf(path), 0); -} - -QIcon CustomFileSystemModel::fileIcon(const QModelIndex &index) const -{ - return m_fileSystemModel->fileIcon(fileSystemModelIndex(index)); -} - -QString CustomFileSystemModel::fileName(const QModelIndex &index) const -{ - return m_fileSystemModel->fileName(fileSystemModelIndex(index)); -} - -QFileInfo CustomFileSystemModel::fileInfo(const QModelIndex &index) const -{ - return m_fileSystemModel->fileInfo(fileSystemModelIndex(index)); -} - -Qt::ItemFlags CustomFileSystemModel::flags(const QModelIndex &index) const -{ - return m_fileSystemModel->flags (fileSystemModelIndex(index)); -} - -void CustomFileSystemModel::setSearchFilter(const QString &nameFilterList) -{ - m_searchFilter = nameFilterList; - setRootPath(m_fileSystemModel->rootPath()); -} - -QPair<QString, QByteArray> CustomFileSystemModel::resourceTypeAndData(const QModelIndex &index) const -{ - QFileInfo fi = fileInfo(index); - QString suffix = fi.suffix().toLower(); - if (!suffix.isEmpty()) { - if (supportedImageSuffixes().contains(suffix)) { - // Data: Image format (suffix) - return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()}; - } else if (supportedFontSuffixes().contains(suffix)) { - // Data: Font family name - return {"application/vnd.bauhaus.libraryresource.font", fontFamily(fi).toUtf8()}; - } else if (supportedShaderSuffixes().contains(suffix)) { - // Data: shader type, frament (f) or vertex (v) - return {"application/vnd.bauhaus.libraryresource.shader", - supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"}; - } else if (supportedAudioSuffixes().contains(suffix)) { - // No extra data for sounds - return {"application/vnd.bauhaus.libraryresource.sound", {}}; - } else if (supportedTexture3DSuffixes().contains(suffix)) { - // Data: Image format (suffix) - return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()}; - } - } - return {}; -} - -const QSet<QString> &CustomFileSystemModel::supportedSuffixes() const -{ - static QSet<QString> allSuffixes; - if (allSuffixes.isEmpty()) { - auto insertSuffixes = [](const QStringList &suffixes) { - for (const auto &suffix : suffixes) - allSuffixes.insert(suffix); - }; - insertSuffixes(supportedImageSuffixes()); - insertSuffixes(supportedShaderSuffixes()); - insertSuffixes(supportedFontSuffixes()); - insertSuffixes(supportedAudioSuffixes()); - insertSuffixes(supportedTexture3DSuffixes()); - } - return allSuffixes; -} - -const QSet<QString> &CustomFileSystemModel::previewableSuffixes() const -{ - static QSet<QString> previewableSuffixes; - if (previewableSuffixes.isEmpty()) { - auto insertSuffixes = [](const QStringList &suffixes) { - for (const auto &suffix : suffixes) - previewableSuffixes.insert(suffix); - }; - insertSuffixes(supportedFontSuffixes()); - } - return previewableSuffixes; - -} - -void CustomFileSystemModel::appendIfNotFiltered(const QString &file) -{ - if (filterMetaIcons(file)) - m_files.append(file); -} - -QModelIndex CustomFileSystemModel::updatePath(const QString &newPath) -{ - beginResetModel(); - - // We must recreate icon provider to ensure modified icons are recreated - auto newProvider = new ItemLibraryFileIconProvider(m_fontImageCache, m_iconCache); - m_fileSystemModel->setIconProvider(newProvider); - delete m_fileIconProvider; - m_fileIconProvider = newProvider; - - m_fileSystemModel->setRootPath(newPath); - - m_fileSystemWatcher->removeDirectories(m_fileSystemWatcher->directories()); - m_fileSystemWatcher->removeFiles(m_fileSystemWatcher->files()); - - m_fileSystemWatcher->addDirectory(newPath, Utils::FileSystemWatcher::WatchAllChanges); - - QStringList nameFilterList; - - const QString searchFilter = m_searchFilter; - - if (searchFilter.contains(QLatin1Char('.'))) { - nameFilterList.append(QString(QStringLiteral("*%1*")).arg(searchFilter)); - } else { - const QString filterTemplate("*%1*.%2"); - auto appendFilters = [&](const QStringList &suffixes) { - for (const QString &ext : suffixes) { - nameFilterList.append(filterTemplate.arg(searchFilter, ext)); - nameFilterList.append(filterTemplate.arg(searchFilter, ext.toUpper())); - } - }; - appendFilters(supportedImageSuffixes()); - appendFilters(supportedShaderSuffixes()); - appendFilters(supportedFontSuffixes()); - appendFilters(supportedAudioSuffixes()); - appendFilters(supportedTexture3DSuffixes()); - } - - m_files.clear(); - - QDirIterator fileIterator(newPath, nameFilterList, QDir::Files, QDirIterator::Subdirectories); - - while (fileIterator.hasNext()) - appendIfNotFiltered(fileIterator.next()); - - QDirIterator dirIterator(newPath, {}, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, - QDirIterator::Subdirectories); - while (dirIterator.hasNext()) { - const QString entry = dirIterator.next(); - QFileInfo fi{entry}; - if (fi.isDir()) - m_fileSystemWatcher->addDirectory(entry, Utils::FileSystemWatcher::WatchAllChanges); - else if (supportedSuffixes().contains(fi.suffix())) - m_fileSystemWatcher->addFile(entry, Utils::FileSystemWatcher::WatchAllChanges); - } - - endResetModel(); - - return QAbstractListModel::index(0, 0); -} - -QModelIndex CustomFileSystemModel::fileSystemModelIndex(const QModelIndex &index) const -{ - const int row = index.row(); - return m_fileSystemModel->index(m_files.at(row)); -} - -} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri index 8099c87853..d1059d11d7 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri @@ -5,7 +5,6 @@ HEADERS += itemlibraryview.h \ $$PWD/itemlibraryiconimageprovider.h \ itemlibrarywidget.h \ itemlibrarymodel.h \ - itemlibraryresourceview.h \ itemlibraryimageprovider.h \ itemlibraryitem.h \ itemlibrarycategory.h \ @@ -15,7 +14,11 @@ HEADERS += itemlibraryview.h \ itemlibraryaddimportmodel.h \ itemlibraryassetimportdialog.h \ itemlibraryassetimporter.h \ - customfilesystemmodel.h \ + itemlibraryassetsdir.h \ + itemlibraryassetsdirsmodel.h \ + itemlibraryassetsfilesmodel.h \ + itemlibraryassetsiconprovider.h \ + itemlibraryassetsmodel.h \ assetimportupdatedialog.h \ assetimportupdatetreeitem.h \ assetimportupdatetreeitemdelegate.h \ @@ -26,7 +29,6 @@ SOURCES += itemlibraryview.cpp \ $$PWD/itemlibraryiconimageprovider.cpp \ itemlibrarywidget.cpp \ itemlibrarymodel.cpp \ - itemlibraryresourceview.cpp \ itemlibraryimageprovider.cpp \ itemlibraryitem.cpp \ itemlibrarycategory.cpp \ @@ -36,7 +38,11 @@ SOURCES += itemlibraryview.cpp \ itemlibraryaddimportmodel.cpp \ itemlibraryassetimportdialog.cpp \ itemlibraryassetimporter.cpp \ - customfilesystemmodel.cpp \ + itemlibraryassetsdir.cpp \ + itemlibraryassetsdirsmodel.cpp \ + itemlibraryassetsfilesmodel.cpp \ + itemlibraryassetsiconprovider.cpp \ + itemlibraryassetsmodel.cpp \ assetimportupdatedialog.cpp \ assetimportupdatetreeitem.cpp \ assetimportupdatetreeitemdelegate.cpp \ diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp new file mode 100644 index 0000000000..755752f4fc --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "itemlibraryassetsdir.h" +#include "itemlibraryassetsdirsmodel.h" +#include "itemlibraryassetsfilesmodel.h" + +namespace QmlDesigner { + +ItemLibraryAssetsDir::ItemLibraryAssetsDir(const QString &path, int depth, bool expanded, QObject *parent) + : QObject(parent) + , m_dirPath(path) + , m_dirDepth(depth) + , m_dirExpanded(expanded) +{ + +} + +QString ItemLibraryAssetsDir::dirName() const { return m_dirPath.split('/').last(); } +QString ItemLibraryAssetsDir::dirPath() const { return m_dirPath; } +int ItemLibraryAssetsDir::dirDepth() const { return m_dirDepth; } +bool ItemLibraryAssetsDir::dirExpanded() const { return m_dirExpanded; } +bool ItemLibraryAssetsDir::dirVisible() const { return m_dirVisible; } + +void ItemLibraryAssetsDir::setDirExpanded(bool expand) +{ + if (m_dirExpanded != expand) { + m_dirExpanded = expand; + emit dirExpandedChanged(); + } +} + +void ItemLibraryAssetsDir::setDirVisible(bool visible) +{ + if (m_dirVisible != visible) { + m_dirVisible = visible; + emit dirVisibleChanged(); + } +} + +QObject *ItemLibraryAssetsDir::filesModel() const +{ + return m_filesModel; +} + +QObject *ItemLibraryAssetsDir::dirsModel() const +{ + return m_dirsModel; +} + +void ItemLibraryAssetsDir::addDir(ItemLibraryAssetsDir *assetsDir) +{ + if (!m_dirsModel) + m_dirsModel = new ItemLibraryAssetsDirsModel(this); + + m_dirsModel->addDir(assetsDir); +} + +void ItemLibraryAssetsDir::addFile(const QString &filePath) +{ + if (!m_filesModel) + m_filesModel = new ItemLibraryAssetsFilesModel(this); + + m_filesModel->addFile(filePath); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h new file mode 100644 index 0000000000..4ef67ce63e --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdir.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QObject> + +namespace QmlDesigner { + +class ItemLibraryAssetsDirsModel; +class ItemLibraryAssetsFilesModel; + +class ItemLibraryAssetsDir : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString dirName READ dirName NOTIFY dirNameChanged) + Q_PROPERTY(QString dirPath READ dirPath NOTIFY dirPathChanged) + Q_PROPERTY(bool dirExpanded READ dirExpanded WRITE setDirExpanded NOTIFY dirExpandedChanged) + Q_PROPERTY(bool dirVisible READ dirVisible WRITE setDirVisible NOTIFY dirVisibleChanged) + Q_PROPERTY(int dirDepth READ dirDepth NOTIFY dirDepthChanged) + Q_PROPERTY(QObject *filesModel READ filesModel NOTIFY filesModelChanged) + Q_PROPERTY(QObject *dirsModel READ dirsModel NOTIFY dirsModelChanged) + +public: + ItemLibraryAssetsDir(const QString &path, int depth, bool expanded = true, QObject *parent = nullptr); + + QString dirName() const; + QString dirPath() const; + int dirDepth() const; + + bool dirExpanded() const; + bool dirVisible() const; + void setDirExpanded(bool expand); + void setDirVisible(bool visible); + + QObject *filesModel() const; + QObject *dirsModel() const; + + void addDir(ItemLibraryAssetsDir *assetsDir); + void addFile(const QString &filePath); + +signals: + void dirNameChanged(); + void dirPathChanged(); + void dirDepthChanged(); + void dirExpandedChanged(); + void dirVisibleChanged(); + void filesModelChanged(); + void dirsModelChanged(); + +private: + QString m_dirPath; + int m_dirDepth = 0; + bool m_dirExpanded = true; + bool m_dirVisible = true; + ItemLibraryAssetsDirsModel *m_dirsModel = nullptr; + ItemLibraryAssetsFilesModel *m_filesModel = nullptr; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp new file mode 100644 index 0000000000..4eae703002 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "itemlibraryassetsdirsmodel.h" +#include "itemlibraryassetsmodel.h" + +#include <QDebug> +#include <QMetaProperty> + +namespace QmlDesigner { + +ItemLibraryAssetsDirsModel::ItemLibraryAssetsDirsModel(QObject *parent) + : QAbstractListModel(parent) +{ + // add roles + const QMetaObject meta = ItemLibraryAssetsDir::staticMetaObject; + for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i) + m_roleNames.insert(i, meta.property(i).name()); +} + +QVariant ItemLibraryAssetsDirsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row()); + return {}; + } + + if (m_roleNames.contains(role)) + return m_dirs[index.row()]->property(m_roleNames[role]); + + qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role); + return {}; +} + +bool ItemLibraryAssetsDirsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + // currently only dirExpanded property is updatable + if (index.isValid() && m_roleNames.contains(role)) { + QVariant currValue = m_dirs.at(index.row())->property(m_roleNames.value(role)); + if (currValue != value) { + m_dirs.at(index.row())->setProperty(m_roleNames.value(role), value); + if (m_roleNames.value(role) == "dirExpanded") + ItemLibraryAssetsModel::saveExpandedState(value.toBool(), m_dirs.at(index.row())->dirPath()); + emit dataChanged(index, index, {role}); + return true; + } + } + return false; +} + +int ItemLibraryAssetsDirsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_dirs.size(); +} + +QHash<int, QByteArray> ItemLibraryAssetsDirsModel::roleNames() const +{ + return m_roleNames; +} + +void ItemLibraryAssetsDirsModel::addDir(ItemLibraryAssetsDir *assetsDir) +{ + m_dirs.append(assetsDir); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h index fa22973c0c..06bc5a5839 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsdirsmodel.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,32 +25,28 @@ #pragma once -#include <previewtooltip/previewtooltipbackend.h> - -#include <QListView> - -QT_BEGIN_NAMESPACE -class QActionGroup; -QT_END_NAMESPACE +#include <QAbstractListModel> +#include "itemlibraryassetsdir.h" namespace QmlDesigner { -class AsynchronousImageCache; - -class ItemLibraryResourceView : public QListView { - +class ItemLibraryAssetsDirsModel : public QAbstractListModel +{ Q_OBJECT + public: - explicit ItemLibraryResourceView(AsynchronousImageCache &fontImageCache, - QWidget *parent = nullptr); + ItemLibraryAssetsDirsModel(QObject *parent = nullptr); - void startDrag(Qt::DropActions supportedActions) override; - bool viewportEvent(QEvent *event) override; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QHash<int, QByteArray> roleNames() const override; -private: - void addSizeAction(QActionGroup *group, const QString &text, int size, int iconSize); + void addDir(ItemLibraryAssetsDir *assetsDir); - std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; +private: + QList<ItemLibraryAssetsDir *> m_dirs; + QHash<int, QByteArray> m_roleNames; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp new file mode 100644 index 0000000000..1140ba43d7 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#include "itemlibraryassetsfilesmodel.h" + +#include <QDebug> + +namespace QmlDesigner { + +ItemLibraryAssetsFilesModel::ItemLibraryAssetsFilesModel(QObject *parent) + : QAbstractListModel(parent) +{ + // add roles + m_roleNames.insert(FileNameRole, "fileName"); + m_roleNames.insert(FilePathRole, "filePath"); +} + +QVariant ItemLibraryAssetsFilesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row()); + return {}; + } + + if (role == FileNameRole) + return m_files[index.row()].split('/').last(); + + if (role == FilePathRole) + return m_files[index.row()]; + + qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role); + return {}; +} + +int ItemLibraryAssetsFilesModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_files.size(); +} + +QHash<int, QByteArray> ItemLibraryAssetsFilesModel::roleNames() const +{ + return m_roleNames; +} + +void ItemLibraryAssetsFilesModel::addFile(const QString &filePath) +{ + m_files.append(filePath); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h new file mode 100644 index 0000000000..25577dce51 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsfilesmodel.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QAbstractListModel> + +namespace QmlDesigner { + +class ItemLibraryAssetsFilesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + ItemLibraryAssetsFilesModel(QObject *parent = nullptr); + + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QHash<int, QByteArray> roleNames() const override; + + void addFile(const QString &filePath); + +private: + enum Roles {FileNameRole = Qt::UserRole + 1, + FilePathRole}; + + QStringList m_files; + QHash<int, QByteArray> m_roleNames; +}; + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp new file mode 100644 index 0000000000..7382a175fc --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "itemlibraryassetsiconprovider.h" +#include "itemlibraryassetsmodel.h" + +#include <hdrimage.h> +#include <theme.h> +#include <utils/stylehelper.h> + +namespace QmlDesigner { + +ItemLibraryAssetsIconProvider::ItemLibraryAssetsIconProvider(SynchronousImageCache &fontImageCache) + : QQuickImageProvider(QQuickImageProvider::Pixmap) + , m_fontImageCache(fontImageCache) +{ +} + +QPixmap ItemLibraryAssetsIconProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) +{ + QPixmap pixmap; + const QString suffix = "*." + id.split('.').last(); + if (ItemLibraryAssetsModel::supportedFontSuffixes().contains(suffix)) + pixmap = generateFontIcons(id); + else if (ItemLibraryAssetsModel::supportedImageSuffixes().contains(suffix)) + pixmap = Utils::StyleHelper::dpiSpecificImageFile(id); + else if (ItemLibraryAssetsModel::supportedTexture3DSuffixes().contains(suffix)) + pixmap = HdrImage{id}.toPixmap(); + else if (ItemLibraryAssetsModel::supportedAudioSuffixes().contains(suffix)) + pixmap = QPixmap(Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/asset_sound_48.png")); + + if (size) { + size->setWidth(pixmap.width()); + size->setHeight(pixmap.height()); + } + + if (pixmap.isNull()) { + pixmap = QPixmap(Utils::StyleHelper::dpiSpecificImageFile( + QStringLiteral(":/ItemLibrary/images/item-default-icon.png"))); + } + + if (requestedSize.isValid()) + return pixmap.scaled(requestedSize); + + return pixmap; +} + +QPixmap ItemLibraryAssetsIconProvider::generateFontIcons(const QString &filePath) const +{ + return m_fontImageCache.icon(filePath, {}, + ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes}, + Theme::getColor(Theme::DStextColor).name(), + "Abc"}).pixmap({48, 48}); +} + +} // namespace QmlDesigner + diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h new file mode 100644 index 0000000000..3c5bc4081e --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsiconprovider.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <synchronousimagecache.h> + +#include <QQuickImageProvider> + +namespace QmlDesigner { + +class ItemLibraryAssetsIconProvider : public QQuickImageProvider +{ +public: + ItemLibraryAssetsIconProvider(SynchronousImageCache &fontImageCache); + + QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override; + +private: + QPixmap generateFontIcons(const QString &filePath) const; + + SynchronousImageCache &m_fontImageCache; + + // Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their + // x2 versions for HDPI sceens + std::vector<QSize> iconSizes = {{384, 384}, + {192, 192}, // Large + {256, 256}, + {128, 128}, // Drag + {96, 96}, // Medium + {48, 48}, // Small + {64, 64}, + {32, 32}}; // List +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp new file mode 100644 index 0000000000..f96af59029 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "itemlibraryassetsmodel.h" +#include "itemlibraryassetsdirsmodel.h" +#include "itemlibraryassetsfilesmodel.h" + +#include <synchronousimagecache.h> +#include <theme.h> +#include <hdrimage.h> + +#include <QDebug> +#include <QDir> +#include <QDirIterator> +#include <QFont> +#include <QImageReader> +#include <QMetaProperty> +#include <QPainter> +#include <QRawFont> +#include "qmldesignerplugin.h" +#include <projectexplorer/project.h> +#include <projectexplorer/session.h> +#include <utils/stylehelper.h> +#include <utils/filesystemwatcher.h> + +namespace QmlDesigner { + +void ItemLibraryAssetsModel::saveExpandedState(bool expanded, const QString §ionName) +{ + m_expandedStateHash.insert(sectionName, expanded); +} + +bool ItemLibraryAssetsModel::loadExpandedState(const QString §ionName) +{ + return m_expandedStateHash.value(sectionName, true); +} + +const QStringList &ItemLibraryAssetsModel::supportedImageSuffixes() +{ + static QStringList retList; + if (retList.isEmpty()) { + const QList<QByteArray> suffixes = QImageReader::supportedImageFormats(); + for (const QByteArray &suffix : suffixes) + retList.append("*." + QString::fromUtf8(suffix)); + } + return retList; +} + +const QStringList &ItemLibraryAssetsModel::supportedFragmentShaderSuffixes() +{ + static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"}; + return retList; +} + +const QStringList &ItemLibraryAssetsModel::supportedShaderSuffixes() +{ + static const QStringList retList {"*.frag", "*.vert", + "*.glsl", "*.glslv", "*.glslf", + "*.vsh", "*.fsh"}; + return retList; +} + +const QStringList &ItemLibraryAssetsModel::supportedFontSuffixes() +{ + static const QStringList retList {"*.ttf", "*.otf"}; + return retList; +} + +const QStringList &ItemLibraryAssetsModel::supportedAudioSuffixes() +{ + static const QStringList retList {"*.wav"}; + return retList; +} + +const QStringList &ItemLibraryAssetsModel::supportedTexture3DSuffixes() +{ + // These are file types only supported by 3D textures + static QStringList retList {"*.hdr"}; + return retList; +} + +ItemLibraryAssetsModel::ItemLibraryAssetsModel(SynchronousImageCache &fontImageCache, + Utils::FileSystemWatcher *fileSystemWatcher, + QObject *parent) + : QAbstractListModel(parent) + , m_fontImageCache(fontImageCache) + , m_fileSystemWatcher(fileSystemWatcher) +{ + // add role names + int role = 0; + const QMetaObject meta = ItemLibraryAssetsDir::staticMetaObject; + for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i) + m_roleNames.insert(role++, meta.property(i).name()); +} + +QVariant ItemLibraryAssetsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + qWarning() << Q_FUNC_INFO << "Invalid index requested: " << QString::number(index.row()); + return {}; + } + + if (m_assetsDir && m_roleNames.contains(role)) { + return m_assetsDir->property(m_roleNames.value(role)); + } + + qWarning() << Q_FUNC_INFO << "Invalid role requested: " << QString::number(role); + return {}; +} + +int ItemLibraryAssetsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return 1; +} + +QHash<int, QByteArray> ItemLibraryAssetsModel::roleNames() const +{ + return m_roleNames; +} + +// called when a directory is changed to refresh the model for this directory +void ItemLibraryAssetsModel::refresh() +{ + setRootPath(m_assetsDir->dirPath()); +} + +void ItemLibraryAssetsModel::setRootPath(const QString &path) +{ + static const QStringList supportedTopLevelDirs {"images", "sounds", "fonts", "assets"}; + + m_fileSystemWatcher->removeDirectories(m_fileSystemWatcher->directories()); + m_fileSystemWatcher->removeFiles(m_fileSystemWatcher->files()); + + DesignDocument *currDesignDoc = QmlDesignerPlugin::instance()->currentDesignDocument(); + if (!currDesignDoc) // happens sometimes on QDS shutdown + return; + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::projectForFile(currDesignDoc->fileName()); + QString projectName = project ? project->displayName() : ""; + + std::function<bool(ItemLibraryAssetsDir *, int)> parseDirRecursive; + parseDirRecursive = [this, &parseDirRecursive, &projectName](ItemLibraryAssetsDir *currAssetsDir, int currDepth) { + m_fileSystemWatcher->addDirectory(currAssetsDir->dirPath(), Utils::FileSystemWatcher::WatchAllChanges); + + QDir dir(currAssetsDir->dirPath()); + dir.setNameFilters(supportedSuffixes().values()); + dir.setFilter(QDir::Files); + QDirIterator itFiles(dir); + bool isEmpty = true; + while (itFiles.hasNext()) { + QString filePath = itFiles.next(); + QString fileName = filePath.split('/').last(); + if (m_searchText.isEmpty() || fileName.contains(m_searchText, Qt::CaseInsensitive)) { + currAssetsDir->addFile(filePath); + m_fileSystemWatcher->addFile(filePath, Utils::FileSystemWatcher::WatchAllChanges); + isEmpty = false; + } + } + + dir.setNameFilters({}); + dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + QDirIterator itDirs(dir); + + while (itDirs.hasNext()) { + QDir subDir = itDirs.next(); + if (subDir.isEmpty() || projectName == subDir.dirName() + || (currDepth == 1 && !supportedTopLevelDirs.contains(subDir.dirName()))) { + continue; + } + + ItemLibraryAssetsDir *assetsDir = new ItemLibraryAssetsDir(subDir.path(), currDepth, loadExpandedState(subDir.path()), currAssetsDir); + currAssetsDir->addDir(assetsDir); + isEmpty &= parseDirRecursive(assetsDir, currDepth + 1); + } + + if (isEmpty) + currAssetsDir->setDirVisible(false); + + return isEmpty; + }; + + if (m_assetsDir) + delete m_assetsDir; + + beginResetModel(); + m_assetsDir = new ItemLibraryAssetsDir(path, 0, true, this); + parseDirRecursive(m_assetsDir, 1); + endResetModel(); +} + +void ItemLibraryAssetsModel::setSearchText(const QString &searchText) +{ + if (m_searchText != searchText) { + m_searchText = searchText; + refresh(); + } +} + +const QSet<QString> &ItemLibraryAssetsModel::supportedSuffixes() const +{ + static QSet<QString> allSuffixes; + if (allSuffixes.isEmpty()) { + auto insertSuffixes = [](const QStringList &suffixes) { + for (const auto &suffix : suffixes) + allSuffixes.insert(suffix); + }; + insertSuffixes(supportedImageSuffixes()); + insertSuffixes(supportedShaderSuffixes()); + insertSuffixes(supportedFontSuffixes()); + insertSuffixes(supportedAudioSuffixes()); + insertSuffixes(supportedTexture3DSuffixes()); + } + return allSuffixes; +} + +const QSet<QString> &ItemLibraryAssetsModel::previewableSuffixes() const +{ + static QSet<QString> previewableSuffixes; + if (previewableSuffixes.isEmpty()) { + auto insertSuffixes = [](const QStringList &suffixes) { + for (const auto &suffix : suffixes) + previewableSuffixes.insert(suffix); + }; + insertSuffixes(supportedFontSuffixes()); + } + return previewableSuffixes; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h index eabd6c1e23..1a6535bdfe 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/customfilesystemmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetsmodel.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,7 +25,7 @@ #pragma once -#include <QAbstractTableModel> +#include <QAbstractListModel> #include <QDateTime> #include <QDir> #include <QHash> @@ -34,59 +34,54 @@ #include <QSet> #include <QTimer> -QT_BEGIN_NAMESPACE -class QFileIconProvider; -class QFileSystemModel; -QT_END_NAMESPACE +#include "itemlibraryassetsdir.h" namespace Utils { class FileSystemWatcher; } namespace QmlDesigner { class SynchronousImageCache; -class ItemLibraryFileIconProvider; -class CustomFileSystemModel : public QAbstractListModel +class ItemLibraryAssetsModel : public QAbstractListModel { Q_OBJECT -public: - CustomFileSystemModel(QmlDesigner::SynchronousImageCache &fontImageCache, - QObject *parent = nullptr); - void setFilter(QDir::Filters filters); - QString rootPath() const; - QModelIndex setRootPath(const QString &newPath); +public: + ItemLibraryAssetsModel(QmlDesigner::SynchronousImageCache &fontImageCache, + Utils::FileSystemWatcher *fileSystemWatcher, + QObject *parent = nullptr); QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex & parent = QModelIndex()) const override; - int columnCount(const QModelIndex & parent = QModelIndex()) const override; - - QModelIndex indexForPath(const QString & path, int column = 0) const; + QHash<int, QByteArray> roleNames() const override; - QIcon fileIcon(const QModelIndex & index) const; - QString fileName(const QModelIndex & index) const; - QFileInfo fileInfo(const QModelIndex & index) const; + void refresh(); + void setRootPath(const QString &path); + void setSearchText(const QString &searchText); - Qt::ItemFlags flags(const QModelIndex &index) const override; - void setSearchFilter(const QString &nameFilterList); + static const QStringList &supportedImageSuffixes(); + static const QStringList &supportedFragmentShaderSuffixes(); + static const QStringList &supportedShaderSuffixes(); + static const QStringList &supportedFontSuffixes(); + static const QStringList &supportedAudioSuffixes(); + static const QStringList &supportedTexture3DSuffixes(); - QPair<QString, QByteArray> resourceTypeAndData(const QModelIndex &index) const; const QSet<QString> &supportedSuffixes() const; const QSet<QString> &previewableSuffixes() const; + static void saveExpandedState(bool expanded, const QString §ionName); + static bool loadExpandedState(const QString §ionName); + private: - QModelIndex updatePath(const QString &newPath); - QModelIndex fileSystemModelIndex(const QModelIndex &index) const; - void appendIfNotFiltered(const QString &file); - - QFileSystemModel *m_fileSystemModel; - QStringList m_files; - QString m_searchFilter; - Utils::FileSystemWatcher *m_fileSystemWatcher; SynchronousImageCache &m_fontImageCache; - ItemLibraryFileIconProvider *m_fileIconProvider = nullptr; QHash<QString, QPair<QDateTime, QIcon>> m_iconCache; - QTimer m_updatePathTimer; + + QString m_searchText; + Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; + ItemLibraryAssetsDir *m_assetsDir = nullptr; + + QHash<int, QByteArray> m_roleNames; + inline static QHash<QString, bool> m_expandedStateHash; }; -} //QmlDesigner +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp deleted file mode 100644 index 1eef27473d..0000000000 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryresourceview.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "itemlibraryresourceview.h" - -#include "customfilesystemmodel.h" - -#include <theme.h> -#include <asynchronousimagecache.h> - -#include <QAction> -#include <QActionGroup> -#include <QDebug> -#include <QDrag> -#include <QFileSystemModel> -#include <QMimeData> -#include <QPainter> -#include <QPixmap> -#include <QtGui/qevent.h> - -#include <QProxyStyle> - -#include <functional> - -enum { debug = 0 }; - -namespace QmlDesigner { - -void ItemLibraryResourceView::addSizeAction(QActionGroup *group, const QString &text, int gridSize, int iconSize) -{ - auto action = new QAction(text, group); - group->addAction(action); - action->setCheckable(true); - QAction::connect(action, &QAction::triggered, this, [this, gridSize, iconSize]() { - setViewMode(QListView::IconMode); - setGridSize(QSize(gridSize, gridSize)); - setIconSize(QSize(iconSize, iconSize)); - setDragEnabled(true); - setWrapping(true); - }); -} - -ItemLibraryResourceView::ItemLibraryResourceView(AsynchronousImageCache &fontImageCache, - QWidget *parent) - : QListView(parent) -{ - setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setAttribute(Qt::WA_MacShowFocusRect, false); - - setGridSize(QSize(128, 128)); - setIconSize(QSize(96, 96)); - setSpacing(4); - - setViewMode(QListView::IconMode); - setMovement(QListView::Snap); - setResizeMode(QListView::Adjust); - setSelectionRectVisible(false); - setWrapping(true); - setWordWrap(true); - - setDragDropMode(QAbstractItemView::DragOnly); - - setContextMenuPolicy(Qt::ActionsContextMenu); - - auto actionGroup = new QActionGroup(this); - actionGroup->setExclusive(true); - - addSizeAction(actionGroup, tr("Large Icons"), 256, 192); - addSizeAction(actionGroup, tr("Medium Icons"), 128, 96); - addSizeAction(actionGroup, tr("Small Icons"), 96, 48); - - QAction *action = new QAction(tr("List"), actionGroup); - actionGroup->addAction(action); - action->setCheckable(true); - QAction::connect(action, &QAction::triggered, this, [this](){ - setViewMode(QListView::ListMode); - setGridSize(QSize()); - setIconSize(QSize(32, 32)); - setDragEnabled(true); - setWrapping(false); - }); - - QAction *defaultAction = actionGroup->actions().at(1); - defaultAction->toggle(); - - addActions(actionGroup->actions()); - - viewport()->setAttribute(Qt::WA_Hover); - m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(fontImageCache); - // Note: Though the text specified here appears in UI, it shouldn't be translated, as it's - // a commonly used sentence to preview the font glyphs in latin fonts. - // For fonts that do not have latin glyphs, the font family name will have to - // suffice for preview. Font family name is inserted into %1 at render time. - m_fontPreviewTooltipBackend->setAuxiliaryData( - ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300}, - Theme::getColor(Theme::DStextColor).name(), - QStringLiteral("The quick brown fox jumps\n" - "over the lazy dog\n" - "1234567890")}); -} - -void ItemLibraryResourceView::startDrag(Qt::DropActions /* supportedActions */) -{ - if (debug) - qDebug() << Q_FUNC_INFO; - - const auto indexes = selectedIndexes(); - if (indexes.isEmpty()) - return; - - const QModelIndex &index = indexes.constFirst(); - if (!index.isValid()) - return; - - auto fileSystemModel = qobject_cast<CustomFileSystemModel*>(model()); - Q_ASSERT(fileSystemModel); - QPair<QString, QByteArray> typeAndData = fileSystemModel->resourceTypeAndData(index); - - if (typeAndData.first.isEmpty()) - return; - - QFileInfo fileInfo = fileSystemModel->fileInfo(index); - - auto drag = new QDrag(this); - drag->setPixmap(fileSystemModel->fileIcon(index).pixmap(128, 128)); - QMimeData *mimeData = new QMimeData; - mimeData->setData(QLatin1String("application/vnd.bauhaus.libraryresource"), - fileInfo.absoluteFilePath().toUtf8()); - mimeData->setData(typeAndData.first, typeAndData.second); - drag->setMimeData(mimeData); - drag->exec(); -} - -bool ItemLibraryResourceView::viewportEvent(QEvent *event) -{ - if (event->type() == QEvent::ToolTip) { - auto fileSystemModel = qobject_cast<CustomFileSystemModel *>(model()); - Q_ASSERT(fileSystemModel); - QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); - QModelIndex index = indexAt(helpEvent->pos()); - if (index.isValid()) { - QFileInfo fi = fileSystemModel->fileInfo(index); - if (fileSystemModel->previewableSuffixes().contains(fi.suffix())) { - QString filePath = fi.absoluteFilePath(); - if (!filePath.isEmpty()) { - if (!m_fontPreviewTooltipBackend->isVisible() - || m_fontPreviewTooltipBackend->path() != filePath) { - m_fontPreviewTooltipBackend->setPath(filePath); - m_fontPreviewTooltipBackend->setName(fi.fileName()); - m_fontPreviewTooltipBackend->showTooltip(); - } else { - m_fontPreviewTooltipBackend->reposition(); - } - return true; - } - } - } - m_fontPreviewTooltipBackend->hideTooltip(); - } else if (event->type() == QEvent::Leave) { - m_fontPreviewTooltipBackend->hideTooltip(); - } else if (event->type() == QEvent::HoverMove) { - if (m_fontPreviewTooltipBackend->isVisible()) { - auto fileSystemModel = qobject_cast<CustomFileSystemModel *>(model()); - Q_ASSERT(fileSystemModel); - auto *he = static_cast<QHoverEvent *>(event); - QModelIndex index = indexAt(he->pos()); - if (index.isValid()) { - QFileInfo fi = fileSystemModel->fileInfo(index); - if (fi.absoluteFilePath() != m_fontPreviewTooltipBackend->path()) - m_fontPreviewTooltipBackend->hideTooltip(); - else - m_fontPreviewTooltipBackend->reposition(); - } - } - } - - return QListView::viewportEvent(event); -} - -} // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 151e6c5a59..b7f89ec96e 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -65,8 +65,8 @@ ProjectExplorer::Target *activeTarget(ProjectExplorer::Project *project) class ImageCacheData { public: - Sqlite::Database database{ - Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v2.db"}}; + Sqlite::Database database{Utils::PathString{ + Core::ICore::cacheResourcePath("imagecache-v2.db").toString()}}; ImageCacheStorage<Sqlite::Database> storage{database}; ImageCacheConnectionManager connectionManager; ImageCacheCollector collector{connectionManager}; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 290611e2e0..27e9fa3d51 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -25,7 +25,7 @@ #include "itemlibrarywidget.h" -#include "customfilesystemmodel.h" +#include "itemlibraryassetsmodel.h" #include "itemlibraryiconimageprovider.h" #include "itemlibraryimport.h" @@ -37,6 +37,7 @@ #include <itemlibraryinfo.h> #include <itemlibrarymodel.h> #include <itemlibraryaddimportmodel.h> +#include "itemlibraryassetsiconprovider.h" #include <metainfo.h> #include <model.h> #include <rewritingexception.h> @@ -46,6 +47,7 @@ #include <utils/algorithm.h> #include <utils/flowlayout.h> #include <utils/fileutils.h> +#include <utils/filesystemwatcher.h> #include <utils/stylehelper.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -75,8 +77,9 @@ namespace QmlDesigner { -static QString propertyEditorResourcesPath() { - return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources"); +static QString propertyEditorResourcesPath() +{ + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); } bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) @@ -124,13 +127,16 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, AsynchronousImageCache &asynchronousFontImageCache, SynchronousImageCache &synchronousFontImageCache) : m_itemIconSize(24, 24) + , m_fontImageCache(synchronousFontImageCache) , m_itemLibraryModel(new ItemLibraryModel(this)) , m_itemLibraryAddImportModel(new ItemLibraryAddImportModel(this)) - , m_resourcesFileSystemModel{new CustomFileSystemModel(synchronousFontImageCache, this)} + , m_assetsIconProvider(new ItemLibraryAssetsIconProvider(synchronousFontImageCache)) + , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) + , m_assetsModel(new ItemLibraryAssetsModel(synchronousFontImageCache, m_fileSystemWatcher, this)) , m_headerWidget(new QQuickWidget(this)) , m_addImportWidget(new QQuickWidget(this)) , m_itemViewQuickWidget(new QQuickWidget(this)) - , m_resourcesView(new ItemLibraryResourceView(asynchronousFontImageCache, this)) + , m_assetsWidget(new QQuickWidget(this)) , m_imageCache{imageCache} { m_compressionTimer.setInterval(200); @@ -142,6 +148,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, // create header widget m_headerWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_headerWidget->setMinimumHeight(75); + m_headerWidget->setMinimumWidth(100); Theme::setupTheme(m_headerWidget->engine()); m_headerWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_headerWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); @@ -179,12 +187,47 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, Theme::setupTheme(m_itemViewQuickWidget->engine()); m_itemViewQuickWidget->installEventFilter(this); - // connect Resources view and its model - m_resourcesView->setModel(m_resourcesFileSystemModel.data()); + m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache); + // Note: Though the text specified here appears in UI, it shouldn't be translated, as it's + // a commonly used sentence to preview the font glyphs in latin fonts. + // For fonts that do not have latin glyphs, the font family name will have to suffice for preview. + m_fontPreviewTooltipBackend->setAuxiliaryData( + ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300}, + Theme::getColor(Theme::DStextColor).name(), + QStringLiteral("The quick brown fox jumps\n" + "over the lazy dog\n" + "1234567890")}); + // create assets widget + m_assetsWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + Theme::setupTheme(m_assetsWidget->engine()); + m_assetsWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_assetsWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); + m_assetsWidget->engine()->addImageProvider("qmldesigner_assets", m_assetsIconProvider); + m_assetsWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{ + {{"assetsModel"}, QVariant::fromValue(m_assetsModel.data())}, + {{"rootView"}, QVariant::fromValue(this)}, + {{"tooltipBackend"}, QVariant::fromValue(m_fontPreviewTooltipBackend.get())} + }); + + // If project directory contents change, or one of the asset files is modified, we must + // reconstruct the model to update the icons + connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged, [this](const QString & changedDirPath) { + Q_UNUSED(changedDirPath) + // TODO: find a clever way to only refresh the changed directory part of the model + + m_assetsModel->refresh(); + + // reload assets qml so that an overridden file's image shows the new image + QTimer::singleShot(100, [this] { + const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml"; + m_assetsWidget->engine()->clearComponentCache(); + m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath)); + }); + }); m_stackedWidget = new QStackedWidget(this); m_stackedWidget->addWidget(m_itemViewQuickWidget.data()); - m_stackedWidget->addWidget(m_resourcesView.data()); + m_stackedWidget->addWidget(m_assetsWidget.data()); m_stackedWidget->addWidget(m_addImportWidget.data()); m_stackedWidget->setMinimumHeight(30); m_stackedWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); @@ -198,34 +241,14 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache, updateSearch(); /* style sheets */ - setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/stylesheet.css"))))); - m_resourcesView->setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))))); + setStyleSheet(Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F5), this); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, &ItemLibraryWidget::reloadQmlSource); connect(&m_compressionTimer, &QTimer::timeout, this, &ItemLibraryWidget::updateModel); - const auto dropSupport = new Utils::DropSupport( - m_resourcesView.data(), [this](QDropEvent *event, Utils::DropSupport *) { - // Accept supported file types - if (event->type() == QDropEvent::DragEnter && !Utils::DropSupport::isFileDrop(event)) - return false; // do not accept drops without files - bool accept = false; - const QSet<QString> &suffixes = m_resourcesFileSystemModel->supportedSuffixes(); - const QList<QUrl> urls = event->mimeData()->urls(); - for (const QUrl &url : urls) { - QFileInfo fi(url.toLocalFile()); - if (suffixes.contains(fi.suffix().toLower())) { - accept = true; - break; - } - } - return accept; - }); - connect(dropSupport, &Utils::DropSupport::filesDropped, - this, &ItemLibraryWidget::importDroppedFiles); - m_itemViewQuickWidget->engine()->addImageProvider("itemlibrary_preview", new ItemLibraryIconImageProvider{m_imageCache}); @@ -298,6 +321,16 @@ void ItemLibraryWidget::handleAddImport(int index) updateSearch(); } +bool ItemLibraryWidget::isSearchActive() const +{ + return !m_filterText.isEmpty(); +} + +void ItemLibraryWidget::handleFilesDrop(const QStringList &filesPaths) +{ + addResources(filesPaths); +} + void ItemLibraryWidget::delayedUpdateModel() { static bool disableTimer = DesignerSettings::getValue(DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER).toBool(); @@ -324,7 +357,7 @@ void ItemLibraryWidget::handleTabChanged(int index) QString ItemLibraryWidget::qmlSourcesPath() { - return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/itemLibraryQmlSources"); + return Core::ICore::resourcePath("qmldesigner/itemLibraryQmlSources").toString(); } void ItemLibraryWidget::clearSearchFilter() @@ -348,6 +381,11 @@ void ItemLibraryWidget::reloadQmlSource() QTC_ASSERT(QFileInfo::exists(itemLibraryQmlPath), return); m_itemViewQuickWidget->engine()->clearComponentCache(); m_itemViewQuickWidget->setSource(QUrl::fromLocalFile(itemLibraryQmlPath)); + + const QString assetsQmlPath = qmlSourcesPath() + "/Assets.qml"; + QTC_ASSERT(QFileInfo::exists(assetsQmlPath), return); + m_assetsWidget->engine()->clearComponentCache(); + m_assetsWidget->setSource(QUrl::fromLocalFile(assetsQmlPath)); } void ItemLibraryWidget::updateModel() @@ -387,9 +425,7 @@ void ItemLibraryWidget::updateSearch() m_itemLibraryModel->setSearchText(m_filterText); m_itemViewQuickWidget->update(); } else if (m_stackedWidget->currentIndex() == 1) { // Assets tab selected - m_resourcesFileSystemModel->setSearchFilter(m_filterText); - m_resourcesFileSystemModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - m_resourcesView->scrollToTop(); + m_assetsModel->setSearchText(m_filterText); } else if (m_stackedWidget->currentIndex() == 2) { // QML imports tab selected m_itemLibraryAddImportModel->setSearchText(m_filterText); } @@ -405,10 +441,7 @@ void ItemLibraryWidget::handlePriorityImportsChanged() void ItemLibraryWidget::setResourcePath(const QString &resourcePath) { - if (m_resourcesView->model() == m_resourcesFileSystemModel.data()) { - m_resourcesFileSystemModel->setRootPath(resourcePath); - m_resourcesView->setRootIndex(m_resourcesFileSystemModel->indexForPath(resourcePath)); - } + m_assetsModel->setRootPath(resourcePath); updateSearch(); } @@ -421,6 +454,51 @@ void ItemLibraryWidget::startDragAndDrop(const QVariant &itemLibEntry, const QPo m_dragStartPoint = mousePos.toPoint(); } +void ItemLibraryWidget::startDragAsset(const QString &assetPath) +{ + QFileInfo fileInfo(assetPath); + QPair<QString, QByteArray> typeAndData = getAssetTypeAndData(fileInfo); + + if (typeAndData.first.isEmpty()) + return; + + auto drag = new QDrag(this); + drag->setPixmap(m_assetsIconProvider->requestPixmap(assetPath, nullptr, {128, 128})); + QMimeData *mimeData = new QMimeData; + mimeData->setData(QLatin1String("application/vnd.bauhaus.libraryresource"), + fileInfo.absoluteFilePath().toUtf8()); + mimeData->setData(typeAndData.first, typeAndData.second); + drag->setMimeData(mimeData); + drag->exec(); +} + +QPair<QString, QByteArray> ItemLibraryWidget::getAssetTypeAndData(const QFileInfo &fi) const +{ + QString suffix = "*." + fi.suffix().toLower(); + if (!suffix.isEmpty()) { + if (ItemLibraryAssetsModel::supportedImageSuffixes().contains(suffix)) { + // Data: Image format (suffix) + return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()}; + } else if (ItemLibraryAssetsModel::supportedFontSuffixes().contains(suffix)) { + // Data: Font family name + QRawFont font(fi.absoluteFilePath(), 10); + QString fontFamily = font.isValid() ? font.familyName() : ""; + return {"application/vnd.bauhaus.libraryresource.font", fontFamily.toUtf8()}; + } else if (ItemLibraryAssetsModel::supportedShaderSuffixes().contains(suffix)) { + // Data: shader type, frament (f) or vertex (v) + return {"application/vnd.bauhaus.libraryresource.shader", + ItemLibraryAssetsModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"}; + } else if (ItemLibraryAssetsModel::supportedAudioSuffixes().contains(suffix)) { + // No extra data for sounds + return {"application/vnd.bauhaus.libraryresource.sound", {}}; + } else if (ItemLibraryAssetsModel::supportedTexture3DSuffixes().contains(suffix)) { + // Data: Image format (suffix) + return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()}; + } + } + return {}; +} + void ItemLibraryWidget::setFlowMode(bool b) { m_itemLibraryModel->setFlowMode(b); @@ -525,15 +603,4 @@ void ItemLibraryWidget::addResources(const QStringList &files) } } -void ItemLibraryWidget::importDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files) -{ - QStringList fileNames; - for (const auto &file : files) { - QFileInfo fi(file.filePath); - if (m_resourcesFileSystemModel->supportedSuffixes().contains(fi.suffix().toLower())) - fileNames.append(fi.absoluteFilePath()); - } - if (!fileNames.isEmpty()) - addResources(fileNames); -} } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index 2883cebb63..122e67eacd 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -26,12 +26,12 @@ #pragma once #include "itemlibraryinfo.h" -#include "itemlibraryresourceview.h" #include "import.h" #include <utils/fancylineedit.h> #include <utils/dropsupport.h> #include <previewtooltip/previewtooltipbackend.h> +#include "itemlibraryassetsmodel.h" #include <QFrame> #include <QToolButton> @@ -48,6 +48,8 @@ class QStackedWidget; class QShortcut; QT_END_NAMESPACE +namespace Utils { class FileSystemWatcher; } + namespace QmlDesigner { class MetaInfo; @@ -55,8 +57,9 @@ class ItemLibraryEntry; class Model; class CustomFileSystemModel; - class ItemLibraryModel; +class ItemLibraryAssetsIconProvider; +class ItemLibraryAssetsModel; class ItemLibraryAddImportModel; class ItemLibraryResourceView; class SynchronousImageCache; @@ -87,8 +90,10 @@ public: void setResourcePath(const QString &resourcePath); void setModel(Model *model); void setFlowMode(bool b); + QPair<QString, QByteArray> getAssetTypeAndData(const QFileInfo &fi) const; Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos); + Q_INVOKABLE void startDragAsset(const QString &assetPath); Q_INVOKABLE void removeImport(const QString &importUrl); Q_INVOKABLE void addImportForItem(const QString &importUrl); Q_INVOKABLE void handleTabChanged(int index); @@ -96,6 +101,9 @@ public: Q_INVOKABLE void handleAddAsset(); Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); Q_INVOKABLE void handleAddImport(int index); + Q_INVOKABLE bool isSearchActive() const; + Q_INVOKABLE void handleFilesDrop(const QStringList &filesPaths); + Q_INVOKABLE QSet<QString> supportedSuffixes() const { return m_assetsModel->supportedSuffixes(); }; signals: void itemActivated(const QString& itemName); @@ -107,26 +115,29 @@ private: void reloadQmlSource(); void addResources(const QStringList &files); - void importDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files); void updateSearch(); void handlePriorityImportsChanged(); QTimer m_compressionTimer; QSize m_itemIconSize; + SynchronousImageCache &m_fontImageCache; QPointer<ItemLibraryInfo> m_itemLibraryInfo; QPointer<ItemLibraryModel> m_itemLibraryModel; QPointer<ItemLibraryAddImportModel> m_itemLibraryAddImportModel; - QPointer<CustomFileSystemModel> m_resourcesFileSystemModel; + ItemLibraryAssetsIconProvider *m_assetsIconProvider = nullptr; + Utils::FileSystemWatcher *m_fileSystemWatcher = nullptr; + QPointer<ItemLibraryAssetsModel> m_assetsModel; QPointer<QStackedWidget> m_stackedWidget; QScopedPointer<QQuickWidget> m_headerWidget; QScopedPointer<QQuickWidget> m_addImportWidget; QScopedPointer<QQuickWidget> m_itemViewQuickWidget; - QScopedPointer<ItemLibraryResourceView> m_resourcesView; + QScopedPointer<QQuickWidget> m_assetsWidget; std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend; + std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut; AsynchronousImageCache &m_imageCache; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index b344077788..2ba7941b3d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -346,9 +346,7 @@ void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList m_currentModelInterface->notifyDataChanged(modelNode); } -void NavigatorView::nodeOrderChanged(const NodeListProperty &listProperty, - const ModelNode &/*node*/, - int /*oldIndex*/) +void NavigatorView::nodeOrderChanged(const NodeListProperty &listProperty) { m_currentModelInterface->notifyModelNodesMoved(listProperty.directSubNodes()); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index b78c9e612a..7e811ca39f 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -75,7 +75,7 @@ public: void nodeAboutToBeRemoved(const ModelNode &removedNode) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; - void nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int oldIndex) override; + void nodeOrderChanged(const NodeListProperty &listProperty) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; @@ -133,8 +133,6 @@ private: QHash<QUrl, QHash<QString, bool>> m_expandMap; NavigatorModelInterface *m_currentModelInterface = nullptr; - - friend class TestNavigator; }; } diff --git a/src/plugins/qmldesigner/components/pathtool/pathitem.h b/src/plugins/qmldesigner/components/pathtool/pathitem.h index 17981283a6..aa75a0aff0 100644 --- a/src/plugins/qmldesigner/components/pathtool/pathitem.h +++ b/src/plugins/qmldesigner/components/pathtool/pathitem.h @@ -65,7 +65,8 @@ private: class PathItem : public QGraphicsObject { Q_OBJECT - friend class PathUpdateDisabler; + friend PathUpdateDisabler; + public: enum { diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetcustomlistmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetcustomlistmodel.cpp index 64008fade5..7396b32b41 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetcustomlistmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetcustomlistmodel.cpp @@ -39,14 +39,14 @@ namespace Internal { static const char settingsKey[] = "GradientPresetCustomList"; -static const char settingsFileName[] = "/GradientPresets.ini"; +static const char settingsFileName[] = "GradientPresets.ini"; QString settingsFullFilePath(const QSettings::Scope &scope) { if (scope == QSettings::SystemScope) - return Core::ICore::installerResourcePath() + settingsFileName; + return Core::ICore::installerResourcePath(settingsFileName).toString(); - return Core::ICore::userResourcePath() + settingsFileName; + return Core::ICore::userResourcePath(settingsFileName).toString(); } } // namespace Internal diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.cpp index fbead98717..6afa715f4d 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.cpp @@ -167,12 +167,7 @@ QString GradientPresetItem::getNameByPreset(Preset value) QGradient GradientPresetItem::createGradientFromPreset(Preset value) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) return QGradient(value); -#else - Q_UNUSED(value) - return {}; -#endif } QDebug &operator<<(QDebug &stream, const GradientPresetItem &gradient) diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.h b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.h index cec45bb4af..ba5f2903e5 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.h +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetitem.h @@ -39,17 +39,8 @@ class GradientPresetItem Q_PROPERTY(int presetID READ presetID FINAL) public: -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) using Preset = QGradient::Preset; -#else - enum Preset {}; -#endif - -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) static const int numPresets = Preset::NumPresets; -#else - static const int numPresets = 181; -#endif explicit GradientPresetItem(); explicit GradientPresetItem(const QGradient &value, const QString &name = QString()); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 967f5cbd4c..09a2d2b058 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -28,10 +28,11 @@ #include <abstractview.h> #include <nodemetainfo.h> +#include <rewritingexception.h> #include <qmldesignerplugin.h> -#include <qmlobjectnode.h> #include <qmlmodelnodeproxy.h> -#include <rewritingexception.h> +#include <qmlobjectnode.h> +#include <qmltimeline.h> #include <coreplugin/messagebox.h> #include <utils/algorithm.h> @@ -302,6 +303,9 @@ void PropertyEditorContextObject::insertKeyframe(const QString &propertyName) { QTC_ASSERT(m_model && m_model->rewriterView(), return); + if (isBlocked(propertyName)) + return; + /* Ideally we should not missuse the rewriterView * If we add more code here we have to forward the property editor view */ RewriterView *rewriterView = m_model->rewriterView(); @@ -310,9 +314,12 @@ void PropertyEditorContextObject::insertKeyframe(const QString &propertyName) ModelNode selectedNode = rewriterView->selectedModelNodes().constFirst(); - rewriterView->emitCustomNotification("INSERT_KEYFRAME", - { selectedNode }, - { propertyName }); + QmlTimeline timeline = rewriterView->currentTimeline(); + + QTC_ASSERT(timeline.isValid(), return ); + QTC_ASSERT(selectedNode.isValid(), return ); + + timeline.insertKeyframe(selectedNode, propertyName.toUtf8()); } int PropertyEditorContextObject::majorVersion() const @@ -553,6 +560,20 @@ QStringList PropertyEditorContextObject::allStatesForId(const QString &id) return {}; } +bool PropertyEditorContextObject::isBlocked(const QString &propName) const +{ + if (m_model && m_model->rewriterView()) { + const QList<ModelNode> nodes = m_model->rewriterView()->selectedModelNodes(); + QScopedPointer<QmlObjectNode> objNode; + for (const auto &node : nodes) { + objNode.reset(QmlObjectNode::getQmlObjectNodeOfCorrectType(node)); + if (objNode->isBlocked(propName.toUtf8())) + return true; + } + } + return false; +} + void EasingCurveEditor::registerDeclarativeType() { qmlRegisterType<EasingCurveEditor>("HelperWidgets", 2, 0, "EasingCurveEditor"); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h index 04d5d074bb..8b7947bd52 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h @@ -99,6 +99,8 @@ public: Q_INVOKABLE QStringList allStatesForId(const QString &id); + Q_INVOKABLE bool isBlocked(const QString &propName) const; + int majorVersion() const; int majorQtQuickVersion() const; int minorQtQuickVersion() const; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index e498d20156..ebaf1da7b6 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -530,8 +530,9 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl contextObject()->setGlobalBaseUrl(QUrl()); } -QString PropertyEditorQmlBackend::propertyEditorResourcesPath() { - return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources"); +QString PropertyEditorQmlBackend::propertyEditorResourcesPath() +{ + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); } inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo &type, const PropertyName &name) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index ec78ed97af..684a1746e0 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -39,6 +39,7 @@ #include <QRegularExpression> #include <QUrl> +#include <QScopedPointer> //using namespace QmlDesigner; @@ -610,14 +611,15 @@ void PropertyEditorNodeWrapper::changeValue(const QString &propertyName) if (name.isNull()) return; if (m_modelNode.isValid()) { - QmlDesigner::QmlObjectNode qmlObjectNode(m_modelNode); + QScopedPointer<QmlDesigner::QmlObjectNode> qmlObjectNode{ + QmlDesigner::QmlObjectNode::getQmlObjectNodeOfCorrectType(m_modelNode)}; auto valueObject = qvariant_cast<PropertyEditorValue *>(m_valuesPropertyMap.value(QString::fromLatin1(name))); if (valueObject->value().isValid()) - qmlObjectNode.setVariantProperty(name, valueObject->value()); + qmlObjectNode->setVariantProperty(name, valueObject->value()); else - qmlObjectNode.removeProperty(name); + qmlObjectNode->removeProperty(name); } } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 873d7ba6a8..abdfc2ebd4 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -56,6 +56,7 @@ #include <QTimer> #include <QShortcut> #include <QApplication> +#include <QScopedPointer> enum { debug = false @@ -88,7 +89,7 @@ PropertyEditorView::PropertyEditorView(QWidget *parent) : connect(m_updateShortcut, &QShortcut::activated, this, &PropertyEditorView::reloadQml); m_stackedWidget->setStyleSheet(Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(QStringLiteral(":/qmldesigner/stylesheet.css"))))); + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); m_stackedWidget->setMinimumWidth(340); m_stackedWidget->move(0, 0); connect(m_stackedWidget, &PropertyEditorWidget::resized, this, &PropertyEditorView::updateSize); @@ -251,7 +252,7 @@ void PropertyEditorView::changeExpression(const QString &propertyName) PropertyName underscoreName(name); underscoreName.replace('.', '_'); - QmlObjectNode qmlObjectNode(m_selectedNode); + QScopedPointer<QmlObjectNode> qmlObjectNode {QmlObjectNode::getQmlObjectNodeOfCorrectType(m_selectedNode)}; PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName)); if (!value) { @@ -259,33 +260,33 @@ void PropertyEditorView::changeExpression(const QString &propertyName) return; } - if (qmlObjectNode.modelNode().metaInfo().isValid() && qmlObjectNode.modelNode().metaInfo().hasProperty(name)) { - if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "QColor") { + if (qmlObjectNode->modelNode().metaInfo().isValid() && qmlObjectNode->modelNode().metaInfo().hasProperty(name)) { + if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "QColor") { if (QColor(value->expression().remove('"')).isValid()) { - qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"'))); + qmlObjectNode->setVariantProperty(name, QColor(value->expression().remove('"'))); return; } - } else if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "bool") { + } else if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "bool") { if (value->expression().compare(QLatin1String("false"), Qt::CaseInsensitive) == 0 || value->expression().compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) { if (value->expression().compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) - qmlObjectNode.setVariantProperty(name, true); + qmlObjectNode->setVariantProperty(name, true); else - qmlObjectNode.setVariantProperty(name, false); + qmlObjectNode->setVariantProperty(name, false); return; } - } else if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "int") { + } else if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "int") { bool ok; int intValue = value->expression().toInt(&ok); if (ok) { - qmlObjectNode.setVariantProperty(name, intValue); + qmlObjectNode->setVariantProperty(name, intValue); return; } - } else if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(name) == "qreal") { + } else if (qmlObjectNode->modelNode().metaInfo().propertyTypeName(name) == "qreal") { bool ok; qreal realValue = value->expression().toDouble(&ok); if (ok) { - qmlObjectNode.setVariantProperty(name, realValue); + qmlObjectNode->setVariantProperty(name, realValue); return; } } @@ -296,8 +297,8 @@ void PropertyEditorView::changeExpression(const QString &propertyName) return; } - if (qmlObjectNode.expression(name) != value->expression() || !qmlObjectNode.propertyAffectedByCurrentState(name)) - qmlObjectNode.setBindingProperty(name, value->expression()); + if (qmlObjectNode->expression(name) != value->expression() || !qmlObjectNode->propertyAffectedByCurrentState(name)) + qmlObjectNode->setBindingProperty(name, value->expression()); }); /* end of transaction */ } @@ -465,11 +466,13 @@ void PropertyEditorView::setupQmlBackend() m_stackedWidget->addWidget(currentQmlBackend->widget()); m_qmlBackendHash.insert(qmlFile.toString(), currentQmlBackend); - QmlObjectNode qmlObjectNode; + QScopedPointer<QmlObjectNode> qmlObjectNode; if (m_selectedNode.isValid()) { - qmlObjectNode = QmlObjectNode(m_selectedNode); - Q_ASSERT(qmlObjectNode.isValid()); - currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this); + qmlObjectNode.reset(QmlObjectNode::getQmlObjectNodeOfCorrectType(m_selectedNode)); + Q_ASSERT(qmlObjectNode->isValid()); + currentQmlBackend->setup(*qmlObjectNode, currentStateName, qmlSpecificsFile, this); + } else { + qmlObjectNode.reset(new QmlObjectNode); } currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false)); if (specificQmlData.isEmpty()) @@ -480,14 +483,16 @@ void PropertyEditorView::setupQmlBackend() currentQmlBackend->setSource(qmlFile); currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(true)); } else { - QmlObjectNode qmlObjectNode; + QScopedPointer<QmlObjectNode> qmlObjectNode; if (m_selectedNode.isValid()) - qmlObjectNode = QmlObjectNode(m_selectedNode); + qmlObjectNode.reset(QmlObjectNode::getQmlObjectNodeOfCorrectType(m_selectedNode)); + else + qmlObjectNode.reset(new QmlObjectNode); currentQmlBackend->context()->setContextProperty("finishedNotify", QVariant(false)); if (specificQmlData.isEmpty()) currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); - currentQmlBackend->setup(qmlObjectNode, currentStateName, qmlSpecificsFile, this); + currentQmlBackend->setup(*qmlObjectNode, currentStateName, qmlSpecificsFile, this); currentQmlBackend->contextObject()->setGlobalBaseUrl(qmlFile); currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); } @@ -509,8 +514,10 @@ void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyN RewriterTransaction transaction = beginRewriterTransaction("PropertyEditorView::commitVariantValueToMode"); for (const ModelNode &node : m_selectedNode.view()->selectedModelNodes()) { - if (QmlObjectNode::isValidQmlObjectNode(node)) - QmlObjectNode(node).setVariantProperty(propertyName, value); + if (QmlObjectNode::isValidQmlObjectNode(node)) { + QScopedPointer<QmlObjectNode>{QmlObjectNode::getQmlObjectNodeOfCorrectType(node)} + ->setVariantProperty(propertyName, value); + } } transaction.commit(); } diff --git a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp index 1cce16b26c..5121de7685 100644 --- a/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp +++ b/src/plugins/qmldesigner/components/richtexteditor/richtexteditor.cpp @@ -587,11 +587,7 @@ void RichTextEditor::setupTableActions() tableFormat.setCellSpacing(2.0); tableFormat.setCellPadding(2.0); tableFormat.setBorder(1.0); - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) tableFormat.setBorderCollapse(true); -#endif - cursor.insertTable(1, 1, tableFormat); //move cursor into the first cell of the table: diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index a25cb385d3..12fff40c79 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -211,9 +211,9 @@ void StatesEditorView::resetModel() if (m_statesEditorWidget) { if (currentState().isBaseState()) - m_statesEditorWidget->setCurrentStateInternalId(currentState().modelNode().internalId()); - else m_statesEditorWidget->setCurrentStateInternalId(0); + else + m_statesEditorWidget->setCurrentStateInternalId(currentState().modelNode().internalId()); } } @@ -526,7 +526,7 @@ void StatesEditorView::nodeReparented(const ModelNode &node, const NodeAbstractP } } -void StatesEditorView::nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode & /*movedNode*/, int /*oldIndex*/) +void StatesEditorView::nodeOrderChanged(const NodeListProperty &listProperty) { if (listProperty.isValid() && listProperty.parentModelNode().isRootNode() && listProperty.name() == "states") resetModel(); diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h index 44b4a3a380..1ac7a6f39f 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.h @@ -73,7 +73,7 @@ public: const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; - void nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int oldIndex) override; + void nodeOrderChanged(const NodeListProperty &listProperty) override; void bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange) override; void variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange) override; diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp index b6b301b855..6b13b890d4 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp @@ -56,8 +56,9 @@ enum { namespace QmlDesigner { -static QString propertyEditorResourcesPath() { - return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources"); +static QString propertyEditorResourcesPath() +{ + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); } int StatesEditorWidget::currentStateInternalId() const @@ -119,8 +120,9 @@ StatesEditorWidget::StatesEditorWidget(StatesEditorView *statesEditorView, State StatesEditorWidget::~StatesEditorWidget() = default; -QString StatesEditorWidget::qmlSourcesPath() { - return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/statesEditorQmlSources"); +QString StatesEditorWidget::qmlSourcesPath() +{ + return Core::ICore::resourcePath("qmldesigner/statesEditorQmlSources").toString(); } void StatesEditorWidget::toggleStatesViewExpanded() diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index a1e2cb6fc1..b8747f7102 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -85,7 +85,9 @@ void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) }); textEditor->editorWidget()->installEventFilter(this); - static QString styleSheet = Theme::replaceCssColors(QString::fromUtf8(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css")))); + static QString styleSheet = Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc( + ":/qmldesigner/scrollbar.css"))); textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } diff --git a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp index fd17b10d9a..d57caf0e1a 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp @@ -141,14 +141,14 @@ QIcon paintPreview(const EasingCurve &curve, const QColor& background, const QCo namespace Internal { static const char settingsKey[] = "EasingCurveList"; -static const char settingsFileName[] = "/EasingCurves.ini"; +static const char settingsFileName[] = "EasingCurves.ini"; QString settingsFullFilePath(const QSettings::Scope &scope) { if (scope == QSettings::SystemScope) - return Core::ICore::installerResourcePath() + settingsFileName; + return Core::ICore::installerResourcePath(settingsFileName).toString(); - return Core::ICore::userResourcePath() + settingsFileName; + return Core::ICore::userResourcePath(settingsFileName).toString(); } } // namespace Internal diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp index 21f5af9eff..185b12ea42 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinecontrols.cpp @@ -56,9 +56,7 @@ FloatControl::FloatControl() setValue(0.0); setButtonSymbols(QAbstractSpinBox::NoButtons); setFrame(false); -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) setStepType(QAbstractSpinBox::AdaptiveDecimalStepType); -#endif setMinimum(std::numeric_limits<float>::lowest()); setMaximum(std::numeric_limits<float>::max()); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp index c86d3fffdb..0b67542fc6 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp @@ -511,7 +511,11 @@ QGraphicsView *AbstractScrollGraphicsScene::rulerView() const QmlTimeline TimelineGraphicsScene::currentTimeline() const { - return QmlTimeline(timelineModelNode()); + QmlTimeline timeline(timelineModelNode()); + if (timeline.isValid()) { + QTC_ASSERT(timeline == timelineView()->currentTimeline(), ;); + } + return timelineView()->currentTimeline(); } QRectF AbstractScrollGraphicsScene::selectionBounds() const diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp index fa1d258c28..77f0bbf34c 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp @@ -56,6 +56,7 @@ #include <QLineEdit> #include <QMenu> #include <QPainter> +#include <QScopedPointer> #include <algorithm> @@ -354,8 +355,9 @@ void TimelinePropertyItem::changePropertyValue(const QVariant &value) QTimer::singleShot(0, deferredFunc); } else { - QmlObjectNode objectNode(m_frames.target()); - objectNode.setVariantProperty(m_frames.propertyName(), value); + QScopedPointer<QmlObjectNode> objectNode { + QmlObjectNode::getQmlObjectNodeOfCorrectType(m_frames.target())}; + objectNode->setVariantProperty(m_frames.propertyName(), value); } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index 9f87a33c00..ed35459ded 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -378,6 +378,7 @@ void TimelineView::addNewTimelineDialog() { auto timeline = addNewTimeline(); addAnimation(timeline); + setCurrentTimeline(timeline); openSettingsDialog(); } @@ -404,13 +405,13 @@ void TimelineView::openSettingsDialog() void TimelineView::setTimelineRecording(bool value) { - ModelNode node = widget()->graphicsScene()->currentTimeline(); + const ModelNode node = timelineForState(currentState()).modelNode(); if (value && node.isValid()) { activateTimelineRecording(node); } else { deactivateTimelineRecording(); - activateTimeline(node); + setCurrentTimeline(node); } } @@ -423,30 +424,16 @@ void TimelineView::customNotification(const AbstractView * /*view*/, QmlTimeline timeline = widget()->graphicsScene()->currentTimeline(); if (timeline.isValid()) timeline.modelNode().removeAuxiliaryData("currentFrame@NodeInstance"); - } else if (identifier == "INSERT_KEYFRAME" && !nodeList.isEmpty() && !data.isEmpty()) { - insertKeyframe(nodeList.constFirst(), data.constFirst().toString().toUtf8()); } } void TimelineView::insertKeyframe(const ModelNode &target, const PropertyName &propertyName) { - QmlTimeline timeline = widget()->graphicsScene()->currentTimeline(); - ModelNode targetNode = target; - if (timeline.isValid() && targetNode.isValid() - && QmlObjectNode::isValidQmlObjectNode(targetNode)) { - executeInTransaction("TimelineView::insertKeyframe", [=, &timeline, &targetNode]() { - targetNode.validId(); - - QmlTimelineKeyframeGroup timelineFrames( - timeline.keyframeGroup(targetNode, propertyName)); - - QTC_ASSERT(timelineFrames.isValid(), return ); - - const qreal frame - = timeline.modelNode().auxiliaryData("currentFrame@NodeInstance").toReal(); - const QVariant value = QmlObjectNode(targetNode).instanceValue(propertyName); + QmlTimeline timeline = currentTimeline(); - timelineFrames.setValue(value, frame); + if (timeline.isValid() && target.isValid() && QmlObjectNode::isValidQmlObjectNode(target)) { + executeInTransaction("TimelineView::insertKeyframe", [=, &timeline, &target]() { + timeline.insertKeyframe(target, propertyName); }); } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp index 00af769a63..b71173d4a1 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp @@ -128,7 +128,7 @@ TimelineWidget::TimelineWidget(TimelineView *view) setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); const QString css = Theme::replaceCssColors(QString::fromUtf8( - Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css")))); + Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); m_scrollbar->setStyleSheet(css); m_scrollbar->setOrientation(Qt::Horizontal); @@ -391,7 +391,7 @@ void TimelineWidget::setTimelineRecording(bool value) timelineView()->activateTimelineRecording(node); } else { timelineView()->deactivateTimelineRecording(); - timelineView()->activateTimeline(node); + timelineView()->setCurrentTimeline(node); } graphicsScene()->invalidateRecordButtonsStatus(); @@ -417,7 +417,7 @@ void TimelineWidget::init() m_statusBar->clear(); } - invalidateTimelineDuration(m_graphicsScene->currentTimeline()); + invalidateTimelineDuration(currentTimeline); m_graphicsScene->setWidth(m_graphicsView->viewport()->width()); @@ -446,7 +446,7 @@ TimelineToolBar *TimelineWidget::toolBar() const void TimelineWidget::invalidateTimelineDuration(const QmlTimeline &timeline) { if (timelineView() && timelineView()->model()) { - QmlTimeline currentTimeline = graphicsScene()->currentTimeline(); + QmlTimeline currentTimeline = timelineView()->currentTimeline(); if (currentTimeline.isValid() && currentTimeline == timeline) { m_toolbar->setStartFrame(timeline.startKeyframe()); m_toolbar->setEndFrame(timeline.endKeyframe()); @@ -470,7 +470,7 @@ void TimelineWidget::invalidateTimelineDuration(const QmlTimeline &timeline) void TimelineWidget::invalidateTimelinePosition(const QmlTimeline &timeline) { if (timelineView() && timelineView()->model()) { - QmlTimeline currentTimeline = graphicsScene()->currentTimeline(); + QmlTimeline currentTimeline = timelineView()->currentTimeline(); if (currentTimeline.isValid() && currentTimeline == timeline) { qreal frame = getcurrentFrame(timeline); m_toolbar->setCurrentFrame(frame); @@ -519,6 +519,7 @@ void TimelineWidget::setTimelineActive(bool b) m_graphicsView->setVisible(false); m_rulerView->setVisible(false); m_scrollbar->setVisible(false); + m_statusBar->clear(); m_addButton->setVisible(true); m_onboardingContainer->setVisible(true); } diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp index a981537a4f..1c8c042fdc 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp @@ -100,8 +100,8 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view) setWindowTitle(tr("Transition", "Title of transition view")); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - const QString css = Theme::replaceCssColors(QString::fromUtf8( - Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css")))); + const QString css = Theme::replaceCssColors( + QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/scrollbar.css"))); m_scrollbar->setStyleSheet(css); m_scrollbar->setOrientation(Qt::Horizontal); |