diff options
author | Henning Gruendl <henning.gruendl@qt.io> | 2020-10-06 12:29:09 +0200 |
---|---|---|
committer | Henning Gründl <henning.gruendl@qt.io> | 2020-10-12 15:24:55 +0000 |
commit | 2860e57112e147a5d639c773836f5e43a82524f7 (patch) | |
tree | 7a8f0a1993c983af685295c4a47b7fcb4b0fee0b /src/plugins/qmldesigner | |
parent | 15f39cf37c490a1cad6a67919e0a3734dcbdaf26 (diff) |
QmlDesigner: Add lock functionality to navigator
* Update icon font and change related theme and constants files
* Add locked column to Navigator
* Add auxiliary property "locked"
* Integrate locked feature into the following components:
* Transition Editor
* Connection Editor
* Form Editor
* Text Editor
* Timeline
* Navigator
* State Editor
Task-number: QDS-826
Change-Id: Ibf3ae96e0d5daeb1ab00279b94df5aaabe75e0bb
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Diffstat (limited to 'src/plugins/qmldesigner')
36 files changed, 472 insertions, 92 deletions
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 96d08e5baef..ea34b091cd4 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -347,25 +347,27 @@ public: && !selectionContext().currentSingleSelectedNode().isRootNode() && selectionContext().currentSingleSelectedNode().hasParentProperty()) { - ActionTemplate *selectionAction = new ActionTemplate(QString(), &ModelNodeOperations::select); - selectionAction->setParent(menu()); - parentNode = selectionContext().currentSingleSelectedNode().parentProperty().parentModelNode(); - selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select parent: %1")).arg( - captionForModelNode(parentNode))); + if (!ModelNode::isThisOrAncestorLocked(parentNode)) { + ActionTemplate *selectionAction = new ActionTemplate(QString(), &ModelNodeOperations::select); + selectionAction->setParent(menu()); + selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select parent: %1")).arg( + captionForModelNode(parentNode))); - SelectionContext nodeSelectionContext = selectionContext(); - nodeSelectionContext.setTargetNode(parentNode); - selectionAction->setSelectionContext(nodeSelectionContext); + SelectionContext nodeSelectionContext = selectionContext(); + nodeSelectionContext.setTargetNode(parentNode); + selectionAction->setSelectionContext(nodeSelectionContext); - menu()->addAction(selectionAction); + menu()->addAction(selectionAction); + } } - foreach (const ModelNode &node, selectionContext().view()->allModelNodes()) { + for (const ModelNode &node : selectionContext().view()->allModelNodes()) { if (node != selectionContext().currentSingleSelectedNode() && node != parentNode && contains(node, selectionContext().scenePosition()) - && !node.isRootNode()) { + && !node.isRootNode() + && !ModelNode::isThisOrAncestorLocked(node)) { selectionContext().setTargetNode(node); QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node)); ActionTemplate *selectionAction = new ActionTemplate(what, &ModelNodeOperations::select); @@ -377,6 +379,9 @@ public: menu()->addAction(selectionAction); } } + + if (menu()->isEmpty()) + action()->setEnabled(false); } } }; diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index f6a1df97cc1..513965b18f4 100644 --- a/src/plugins/qmldesigner/components/componentcore/theme.h +++ b/src/plugins/qmldesigner/components/componentcore/theme.h @@ -107,6 +107,8 @@ public: idAliasOff, idAliasOn, listView, + lockOff, + lockOn, mergeCells, minus, plus, @@ -129,6 +131,8 @@ public: undo, upDownIcon, upDownSquare2, + visibilityOff, + visibilityOn, wildcard, zoomAll, zoomIn, diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 69e0e202f40..e325b020346 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -75,6 +75,23 @@ ConnectionModel::ConnectionModel(ConnectionView *parent) connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged); } +Qt::ItemFlags ConnectionModel::flags(const QModelIndex &modelIndex) const +{ + if (!modelIndex.isValid()) + return Qt::ItemIsEnabled; + + if (!m_connectionView || !m_connectionView->model()) + return Qt::ItemIsEnabled; + + const int internalId = data(index(modelIndex.row(), TargetModelNodeRow), UserRoles::InternalIdRole).toInt(); + ModelNode modelNode = m_connectionView->modelNodeForInternalId(internalId); + + if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode)) + return Qt::ItemIsEnabled; + + return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; +} + void ConnectionModel::resetModel() { beginResetModel(); @@ -82,7 +99,7 @@ void ConnectionModel::resetModel() setHorizontalHeaderLabels(QStringList({ tr("Target"), tr("Signal Handler"), tr("Action") })); if (connectionView()->isAttached()) { - for (const ModelNode modelNode : connectionView()->allModelNodes()) + for (const ModelNode &modelNode : connectionView()->allModelNodes()) addModelNode(modelNode); } @@ -94,8 +111,8 @@ void ConnectionModel::resetModel() SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const { - const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); - const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); + const int internalId = data(index(rowNumber, TargetModelNodeRow), UserRoles::InternalIdRole).toInt(); + const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), UserRoles::TargetPropertyNameRole).toString(); ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); if (modelNode.isValid()) @@ -256,8 +273,8 @@ void ConnectionModel::updateTargetNode(int rowNumber) void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty) { - item->setData(signalHandlerProperty.parentModelNode().internalId(), Qt::UserRole + 1); - item->setData(signalHandlerProperty.name(), Qt::UserRole + 2); + item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole); + item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole); } ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index b7d24db7190..5acf4f6402c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -48,7 +48,14 @@ public: TargetPropertyNameRow = 1, SourceRow = 2 }; + enum UserRoles { + InternalIdRole = Qt::UserRole + 1, + TargetPropertyNameRole + }; ConnectionModel(ConnectionView *parent = nullptr); + + Qt::ItemFlags flags(const QModelIndex &modelIndex) const override; + void resetModel(); SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const; ConnectionView *connectionView() const; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 0410ccf9089..88dd6a971db 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -36,6 +36,8 @@ #include <variantproperty.h> #include <signalhandlerproperty.h> +#include <QTableView> + namespace QmlDesigner { namespace Internal { @@ -162,6 +164,33 @@ void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeL emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.count() == 1); } +void ConnectionView::auxiliaryDataChanged(const ModelNode &node, + const PropertyName &name, + const QVariant &data) +{ + Q_UNUSED(node) + + // Check if the auxiliary data is actually the locked property or if it is unlocked + if (name != QmlDesigner::lockedProperty || !data.toBool()) + return; + + QItemSelectionModel *selectionModel = connectionTableView()->selectionModel(); + if (!selectionModel->hasSelection()) + return; + + QModelIndex modelIndex = selectionModel->currentIndex(); + if (!modelIndex.isValid() || !model()) + return; + + const int internalId = connectionModel()->data(connectionModel()->index(modelIndex.row(), + ConnectionModel::TargetModelNodeRow), + ConnectionModel::UserRoles::InternalIdRole).toInt(); + ModelNode modelNode = modelNodeForInternalId(internalId); + + if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode)) + selectionModel->clearSelection(); +} + void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, const QList<Import> & /*removedImports*/) { backendModel()->resetModel(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 905fa02a587..dda496263d7 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -69,6 +69,7 @@ public: void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) override; + void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override; void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override; diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 0ed7abaefe3..fc7e8b97c7b 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -189,7 +189,7 @@ FormEditorItem *AbstractFormEditorTool::topMovableFormEditorItem(const QList<QGr FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &point, const QList<QGraphicsItem*> &itemList) { FormEditorItem* nearestItem = nullptr; - foreach (QGraphicsItem *item, itemList) { + for (QGraphicsItem *item : itemList) { FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); if (formEditorItem && formEditorItem->flowHitTest(point)) @@ -201,6 +201,9 @@ FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &poi if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible()) continue; + if (formEditorItem && ModelNode::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode())) + continue; + if (!nearestItem) nearestItem = formEditorItem; else if (formEditorItem->selectionWeigth(point, 1) < nearestItem->selectionWeigth(point, 0)) diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 9a643edef8e..8bd2d827f5e 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -250,7 +250,6 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSc if (m_dragNode.isValid()) view()->setSelectedModelNode(m_dragNode); - m_dragNode = QmlItemNode(); view()->changeToSelectionTool(); diff --git a/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp index 242b5649d27..fa399f8860a 100644 --- a/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp @@ -90,10 +90,10 @@ void RubberBandSelectionManipulator::select(SelectionType selectionType) if (!m_beginFormEditorItem) return; - QList<QGraphicsItem*> itemList = m_editorView->scene()->items(m_selectionRectangleElement.rect(), Qt::IntersectsItemBoundingRect); + QList<QGraphicsItem *> itemList = m_editorView->scene()->items(m_selectionRectangleElement.rect(), Qt::IntersectsItemBoundingRect); QList<QmlItemNode> newNodeList; - foreach (QGraphicsItem* item, itemList) + for (QGraphicsItem *item : itemList) { FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); @@ -137,7 +137,7 @@ void RubberBandSelectionManipulator::select(SelectionType selectionType) } -void RubberBandSelectionManipulator::setItems(const QList<FormEditorItem*> &itemList) +void RubberBandSelectionManipulator::setItems(const QList<FormEditorItem *> &itemList) { m_itemList = itemList; } diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 05929fbd0ef..ae5c2fbcc2f 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -49,6 +49,7 @@ #include <coreplugin/icore.h> #include <coreplugin/idocument.h> #include <coreplugin/editormanager/editormanager.h> +#include <utils/algorithm.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -57,6 +58,7 @@ #include <QDebug> #include <QApplication> +#include <QMessageBox> #include <QPlainTextEdit> #include <QRandomGenerator> @@ -375,9 +377,41 @@ void DesignDocument::deleteSelected() if (!currentModel()) return; + QStringList lockedNodes; + for (const ModelNode &modelNode : view()->selectedModelNodes()) { + for (const ModelNode &node : modelNode.allSubModelNodesAndThisNode()) { + if (node.isValid() && !node.isRootNode() && node.locked()) + lockedNodes.push_back(node.id()); + } + } + + if (!lockedNodes.empty()) { + Utils::sort(lockedNodes); + QString detailedText = QString("<b>" + tr("Locked items:") + "</b><br>"); + + for (const auto &id : qAsConst(lockedNodes)) + detailedText.append("- " + id + "<br>"); + + detailedText.chop(QString("<br>").size()); + + QMessageBox msgBox; + msgBox.setTextFormat(Qt::RichText); + msgBox.setIcon(QMessageBox::Question); + msgBox.setWindowTitle(tr("Delete/Cut Item")); + msgBox.setText(QString(tr("Deleting or cutting this item will modify locked items.") + "<br><br>%1") + .arg(detailedText)); + msgBox.setInformativeText(tr("Do you want to continue by removing the item (Delete) or removing it and copying it to the clipboard (Cut)?")); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Ok); + + if (msgBox.exec() == QMessageBox::Cancel) + return; + } + rewriterView()->executeInTransaction("DesignDocument::deleteSelected", [this](){ QList<ModelNode> toDelete = view()->selectedModelNodes(); - foreach (ModelNode node, toDelete) { + + for (ModelNode node : toDelete) { if (node.isValid() && !node.isRootNode() && QmlObjectNode::isValidQmlObjectNode(node)) QmlObjectNode(node).destroy(); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 5538a9cb8d2..78d38c082d7 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -200,10 +200,10 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const if (!modelNode.isValid()) return QVariant(); - if (role == ItemIsVisibleRole) //independent of column + if (role == ItemIsVisibleRole) // independent of column return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; - if (index.column() == 0) { + if (index.column() == ColumnType::Name) { if (role == Qt::DisplayRole) { return modelNode.displayName(); } else if (role == Qt::DecorationRole) { @@ -240,18 +240,24 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const } else if (role == ModelNodeRole) { return QVariant::fromValue<ModelNode>(modelNode); } - } else if (index.column() == 1) { //export + } else if (index.column() == ColumnType::Alias) { // export if (role == Qt::CheckStateRole) - return currentQmlObjectNode.isAliasExported() ? Qt::Checked : Qt::Unchecked; + return currentQmlObjectNode.isAliasExported() ? Qt::Checked : Qt::Unchecked; else if (role == Qt::ToolTipRole) return tr("Toggles whether this item is exported as an " "alias property of the root item."); - } else if (index.column() == 2) { //visible + } else if (index.column() == ColumnType::Visibility) { // visible if (role == Qt::CheckStateRole) return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; else if (role == Qt::ToolTipRole) return tr("Toggles the visibility of this item in the form editor.\n" "This is independent of the visibility property in QML."); + } else if (index.column() == ColumnType::Lock) { // lock + if (role == Qt::CheckStateRole) + return modelNode.locked() ? Qt::Checked : Qt::Unchecked; + else if (role == Qt::ToolTipRole) + return tr("Toggles whether this item is locked.\n" + "Locked items can't be modified or selected."); } return QVariant(); @@ -259,7 +265,16 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const Qt::ItemFlags NavigatorTreeModel::flags(const QModelIndex &index) const { - if (index.column() == 0) + if (index.column() == ColumnType::Alias + || index.column() == ColumnType::Visibility + || index.column() == ColumnType::Lock) + return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren; + + const ModelNode modelNode = modelNodeForIndex(index); + if (ModelNode::isThisOrAncestorLocked(modelNode)) + return Qt::NoItemFlags; + + if (index.column() == ColumnType::Name) return Qt::ItemIsEditable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable @@ -378,7 +393,7 @@ int NavigatorTreeModel::columnCount(const QModelIndex &parent) const if (parent.column() > 0) return 0; - return 3; + return ColumnType::Count; } ModelNode NavigatorTreeModel::modelNodeForIndex(const QModelIndex &index) const @@ -792,11 +807,13 @@ Qt::DropActions NavigatorTreeModel::supportedDragActions() const bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { ModelNode modelNode = modelNodeForIndex(index); - if (index.column() == 1 && role == Qt::CheckStateRole) { + if (index.column() == ColumnType::Alias && role == Qt::CheckStateRole) { QTC_ASSERT(m_view, return false); m_view->handleChangedExport(modelNode, value.toInt() != 0); - } else if (index.column() == 2 && role == Qt::CheckStateRole) { + } else if (index.column() == ColumnType::Visibility && role == Qt::CheckStateRole) { QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0); + } else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) { + modelNode.setLocked(value.toInt() != 0); } return true; @@ -806,7 +823,7 @@ void NavigatorTreeModel::notifyDataChanged(const ModelNode &modelNode) { const QModelIndex index = indexForModelNode(modelNode); const QAbstractItemModel *model = index.model(); - const QModelIndex sibling = model ? model->sibling(index.row(), 2, index) : QModelIndex(); + const QModelIndex sibling = model ? model->sibling(index.row(), ColumnType::Count - 1, index) : QModelIndex(); emit dataChanged(index, sibling); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index d474ee98b9c..bb2712148df 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -49,6 +49,14 @@ class NavigatorTreeModel : public QAbstractItemModel, public NavigatorModelInter public: + enum ColumnType { + Name = 0, + Alias, + Visibility, + Lock, + Count + }; + explicit NavigatorTreeModel(QObject *parent = nullptr); ~NavigatorTreeModel() override; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 87952613b1c..e1702bdac46 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -41,6 +41,7 @@ #include <qmlitemnode.h> #include <rewritingexception.h> #include <nodeinstanceview.h> +#include <theme.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> @@ -48,6 +49,7 @@ #include <utils/algorithm.h> #include <utils/icon.h> #include <utils/utilsicons.h> +#include <utils/stylehelper.h> #include <QHeaderView> #include <QTimer> @@ -138,13 +140,14 @@ void NavigatorView::modelAttached(Model *model) QTreeView *treeView = treeWidget(); - treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); - treeView->header()->resizeSection(1,26); + treeView->header()->setSectionResizeMode(NavigatorTreeModel::ColumnType::Name, QHeaderView::Stretch); + treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Alias, 26); + treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Visibility, 26); + treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Lock, 26); treeView->setIndentation(20); m_currentModelInterface->setFilter(false); - QTimer::singleShot(0, this, [this, treeView]() { m_currentModelInterface->setFilter( DesignerSettings::getValue(DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS).toBool()); @@ -166,10 +169,6 @@ void NavigatorView::modelAttached(Model *model) } } }); - -#ifdef _LOCK_ITEMS_ - treeView->header()->resizeSection(2,20); -#endif } void NavigatorView::modelAboutToBeDetached(Model *model) @@ -304,7 +303,7 @@ void NavigatorView::nodeIdChanged(const ModelNode& modelNode, const QString & /* m_currentModelInterface->notifyDataChanged(modelNode); } -void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/) +void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &/*propertyList*/) { } @@ -321,7 +320,7 @@ void NavigatorView::propertiesRemoved(const QList<AbstractProperty> &propertyLis m_currentModelInterface->notifyModelNodesRemoved(modelNodes); } -void NavigatorView::rootNodeTypeChanged(const QString & /*type*/, int /*majorVersion*/, int /*minorVersion*/) +void NavigatorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) { m_currentModelInterface->notifyDataChanged(rootModelNode()); } @@ -332,9 +331,12 @@ void NavigatorView::nodeTypeChanged(const ModelNode &modelNode, const TypeName & } void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode, - const PropertyName & /*name*/, - const QVariant & /*data*/) + const PropertyName &name, + const QVariant &data) { + Q_UNUSED(name) + Q_UNUSED(data) + m_currentModelInterface->notifyDataChanged(modelNode); } @@ -344,8 +346,8 @@ void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList m_currentModelInterface->notifyDataChanged(modelNode); } -void NavigatorView::nodeOrderChanged(const NodeListProperty & listProperty, - const ModelNode & /*node*/, +void NavigatorView::nodeOrderChanged(const NodeListProperty &listProperty, + const ModelNode &/*node*/, int /*oldIndex*/) { m_currentModelInterface->notifyModelNodesMoved(listProperty.directSubNodes()); @@ -613,33 +615,50 @@ void NavigatorView::setupWidget() connect(m_widget.data(), &NavigatorWidget::reverseOrderToggled, this, &NavigatorView::reverseOrderToggled); #ifndef QMLDESIGNER_TEST + const QString fontName = "qtds_propertyIconFont.ttf"; + + const QIcon visibilityOnIcon = + Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::visibilityOn), + 28, 28, QColor(Qt::white)); + const QIcon visibilityOffIcon = + Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::visibilityOff), + 28, 28, QColor(Qt::white)); + + const QIcon aliasOnIcon = + Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::idAliasOn), + 28, 28, QColor(Qt::red)); + const QIcon aliasOffIcon = + Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::idAliasOff), + 28, 28, QColor(Qt::white)); + + const QIcon lockOnIcon = + Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::lockOn), + 28, 28, QColor(Qt::white)); + const QIcon lockOffIcon = + Utils::StyleHelper::getIconFromIconFont(fontName, + Theme::getIconUnicode(Theme::Icon::lockOff), + 28, 28, QColor(Qt::white)); + auto idDelegate = new NameItemDelegate(this); - IconCheckboxItemDelegate *showDelegate = - new IconCheckboxItemDelegate(this, - Utils::Icons::EYE_OPEN_TOOLBAR.icon(), - Utils::Icons::EYE_CLOSED_TOOLBAR.icon()); - IconCheckboxItemDelegate *exportDelegate = - new IconCheckboxItemDelegate(this, - Icons::EXPORT_CHECKED.icon(), - Icons::EXPORT_UNCHECKED.icon()); + IconCheckboxItemDelegate *visibilityDelegate = + new IconCheckboxItemDelegate(this, visibilityOnIcon, visibilityOffIcon); -#ifdef _LOCK_ITEMS_ - IconCheckboxItemDelegate *lockDelegate = - new IconCheckboxItemDelegate(this, - Utils::Icons::LOCKED_TOOLBAR.icon(), - Utils::Icons::UNLOCKED_TOOLBAR.icon()); -#endif + IconCheckboxItemDelegate *aliasDelegate = + new IconCheckboxItemDelegate(this, aliasOnIcon, aliasOffIcon); + IconCheckboxItemDelegate *lockDelegate = + new IconCheckboxItemDelegate(this, lockOnIcon, lockOffIcon); - treeWidget()->setItemDelegateForColumn(0, idDelegate); -#ifdef _LOCK_ITEMS_ - treeWidget()->setItemDelegateForColumn(1,lockDelegate); - treeWidget()->setItemDelegateForColumn(2,showDelegate); -#else - treeWidget()->setItemDelegateForColumn(1, exportDelegate); - treeWidget()->setItemDelegateForColumn(2, showDelegate); -#endif + treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Name, idDelegate); + treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Alias, aliasDelegate); + treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Visibility, visibilityDelegate); + treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Lock, lockDelegate); #endif //QMLDESIGNER_TEST } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index 67042634e90..6189b24559a 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -82,7 +82,7 @@ public: void propertiesRemoved(const QList<AbstractProperty>& propertyList) override; void selectedNodesChanged(const QList<ModelNode> &selectedNodeList , - const QList<ModelNode> &lastSelectedNodeList) override; + const QList<ModelNode> &lastSelectedNodeList) override; void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override; void instanceErrorChanged(const QVector<ModelNode> &errorNodeList) override; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp index 3813881f550..649b53b5ffa 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp @@ -78,7 +78,7 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view) #endif } -void NavigatorWidget::setTreeModel(QAbstractItemModel* model) +void NavigatorWidget::setTreeModel(QAbstractItemModel *model) { m_treeView->setModel(model); } @@ -92,7 +92,6 @@ QList<QToolButton *> NavigatorWidget::createToolBarWidgets() { QList<QToolButton *> buttons; - auto button = new QToolButton(); button->setIcon(Icons::ARROW_LEFT.icon()); button->setToolTip(tr("Become last sibling of parent (CTRL + Left).")); @@ -180,7 +179,6 @@ void NavigatorWidget::enableNavigator() m_treeView->setEnabled(true); } - NavigatorView *NavigatorWidget::navigatorView() const { return m_navigatorView.data(); diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index 3a152836633..a8792ef2041 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -30,6 +30,7 @@ #include <QDebug> #include <QRegularExpression> +#include <QMessageBox> #include <cmath> #include <memory> @@ -42,6 +43,7 @@ #include <qmlitemnode.h> #include <qmlstate.h> #include <annotationeditor/annotationeditor.h> +#include <utils/algorithm.h> namespace QmlDesigner { @@ -92,11 +94,46 @@ void StatesEditorView::removeState(int nodeId) if (nodeId > 0 && hasModelNodeForInternalId(nodeId)) { ModelNode stateNode(modelNodeForInternalId(nodeId)); Q_ASSERT(stateNode.metaInfo().isSubclassOf("QtQuick.State")); + + QmlModelState modelState(stateNode); + if (modelState.isValid()) { + QStringList lockedTargets; + const auto propertyChanges = modelState.propertyChanges(); + for (const QmlPropertyChanges &change : propertyChanges) { + const ModelNode target = change.target(); + if (target.locked()) + lockedTargets.push_back(target.id()); + } + + if (!lockedTargets.empty()) { + Utils::sort(lockedTargets); + QString detailedText = QString("<b>" + tr("Locked items:") + "</b><br>"); + + for (const auto &id : qAsConst(lockedTargets)) + detailedText.append("- " + id + "<br>"); + + detailedText.chop(QString("<br>").size()); + + QMessageBox msgBox; + msgBox.setTextFormat(Qt::RichText); + msgBox.setIcon(QMessageBox::Question); + msgBox.setWindowTitle(tr("Remove State")); + msgBox.setText(QString(tr("Removing this state will modify locked items.") + "<br><br>%1") + .arg(detailedText)); + msgBox.setInformativeText(tr("Do you want to continue by removing the state?")); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Ok); + + if (msgBox.exec() == QMessageBox::Cancel) + return; + } + } + NodeListProperty parentProperty = stateNode.parentProperty().toNodeListProperty(); if (parentProperty.count() <= 1) { setCurrentState(baseState()); - } else if (parentProperty.isValid()){ + } else if (parentProperty.isValid()) { int index = parentProperty.indexOf(stateNode); if (index == 0) setCurrentState(parentProperty.at(1)); @@ -104,7 +141,6 @@ void StatesEditorView::removeState(int nodeId) setCurrentState(parentProperty.at(index - 1)); } - stateNode.destroy(); } } catch (const RewritingException &e) { diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp index 3b87f22c517..0f87032519c 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp @@ -428,6 +428,18 @@ void TimelineGraphicsScene::invalidateKeyframesForTarget(const ModelNode &target TimelineSectionItem::updateFramesForTarget(child, target); } +void TimelineGraphicsScene::invalidateHeightForTarget(const ModelNode &target) +{ + if (!target.isValid()) + return; + + const auto children = m_layout->childItems(); + for (auto child : children) + TimelineSectionItem::updateHeightForTarget(child, target); + + invalidateLayout(); +} + void TimelineGraphicsScene::invalidateScene() { ModelNode node = timelineView()->modelNodeForId( diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h index 7413cb1dbb2..8ef1c13092e 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h @@ -158,6 +158,7 @@ public: void invalidateSectionForTarget(const ModelNode &modelNode); void invalidateKeyframesForTarget(const ModelNode &modelNode); + void invalidateHeightForTarget(const ModelNode &modelNode); void invalidateScene(); void invalidateScrollbar() override; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp index 12345a404e4..1b0b2cb7e53 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp @@ -156,4 +156,9 @@ TimelineFrameHandle *TimelineMovableAbstractItem::asTimelineFrameHandle() return nullptr; } +bool TimelineMovableAbstractItem::isLocked() const +{ + return false; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h index 199a78ad997..d79101b4d9d 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h @@ -69,6 +69,8 @@ public: virtual TimelineKeyframeItem *asTimelineKeyframeItem(); virtual TimelineFrameHandle *asTimelineFrameHandle(); + virtual bool isLocked() const; + protected: int scrollOffset() const; void mousePressEvent(QGraphicsSceneMouseEvent *event) override; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp index 5dc52bbc9b8..0508606c11f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp @@ -70,6 +70,9 @@ void TimelineMoveTool::mousePressEvent(TimelineMovableAbstractItem *item, { Q_UNUSED(item) + if (currentItem() && currentItem()->isLocked()) + return; + if (auto *current = currentItem()->asTimelineKeyframeItem()) { const qreal sourceFrame = qRound(current->mapFromSceneToFrame(current->rect().center().x())); const qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x())); @@ -85,6 +88,9 @@ void TimelineMoveTool::mouseMoveEvent(TimelineMovableAbstractItem *item, if (!currentItem()) return; + if (currentItem()->isLocked()) + return; + if (auto *current = currentItem()->asTimelineKeyframeItem()) { // prevent dragging if deselecting a keyframe (Ctrl+click and drag a selected keyframe) if (!current->highlighted()) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp index c77d466585f..4580240c5fb 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp @@ -171,6 +171,17 @@ void TimelineSectionItem::updateFramesForTarget(QGraphicsItem *item, const Model } } +void TimelineSectionItem::updateHeightForTarget(QGraphicsItem *item, const ModelNode &target) +{ + if (!target.isValid()) + return; + + if (auto sectionItem = qgraphicsitem_cast<TimelineSectionItem *>(item)) { + if (sectionItem->targetNode() == target) + sectionItem->updateHeight(); + } +} + void TimelineSectionItem::moveAllFrames(qreal offset) { if (m_timeline.isValid()) @@ -313,7 +324,8 @@ void TimelineSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) if (event->button() == Qt::LeftButton) { event->accept(); - toggleCollapsed(); + if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + toggleCollapsed(); } } @@ -345,7 +357,8 @@ void TimelineSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (m_targetNode.isValid()) m_targetNode.view()->setSelectedModelNode(m_targetNode); } else { - toggleCollapsed(); + if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + toggleCollapsed(); } update(); } @@ -414,6 +427,12 @@ void TimelineSectionItem::updateFrames() update(); } +void TimelineSectionItem::updateHeight() +{ + invalidateHeight(); + update(); +} + void TimelineSectionItem::invalidateHeight() { int height = 0; @@ -464,7 +483,8 @@ void TimelineSectionItem::invalidateFrames() bool TimelineSectionItem::collapsed() const { - return m_targetNode.isValid() && !m_targetNode.hasAuxiliaryData("timeline_expanded"); + return m_targetNode.isValid() + && (!m_targetNode.hasAuxiliaryData("timeline_expanded") || m_targetNode.locked()); } void TimelineSectionItem::createPropertyItems() @@ -845,6 +865,11 @@ void TimelineBarItem::commitPosition(const QPointF & /*point*/) m_oldRect = QRectF(); } +bool TimelineBarItem::isLocked() const +{ + return sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked(); +} + void TimelineBarItem::scrollOffsetChanged() { sectionItem()->invalidateBar(); @@ -904,7 +929,9 @@ void TimelineBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) const auto p = event->pos(); QRectF left, right; - if (handleRects(rect(), left, right)) { + if (isLocked() && rect().contains(p)) { + setCursor(QCursor(Qt::ForbiddenCursor)); + } else if (handleRects(rect(), left, right)) { if (left.contains(p) || right.contains(p)) { if (cursor().shape() != Qt::SizeHorCursor) setCursor(QCursor(Qt::SizeHorCursor)); @@ -920,6 +947,9 @@ void TimelineBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) void TimelineBarItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { + if (isLocked()) + return; + QMenu menu; QAction* overrideColor = menu.addAction(tr("Override Color")); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h index e5403bcb74f..a31930b709b 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h @@ -50,6 +50,8 @@ public: void itemMoved(const QPointF &start, const QPointF &end) override; void commitPosition(const QPointF &point) override; + bool isLocked() const override; + protected: void scrollOffsetChanged() override; void paint(QPainter *painter, @@ -100,6 +102,7 @@ public: static void updateData(QGraphicsItem *item); static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b); static void updateFramesForTarget(QGraphicsItem *item, const ModelNode &target); + static void updateHeightForTarget(QGraphicsItem *item, const ModelNode &target); void moveAllFrames(qreal offset); void scaleAllFrames(qreal scale); @@ -121,6 +124,7 @@ protected: private: void updateData(); void updateFrames(); + void updateHeight(); void invalidateHeight(); void invalidateProperties(); void invalidateFrames(); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index d0679c80bb6..dab580a1469 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -236,6 +236,18 @@ void TimelineView::selectedNodesChanged(const QList<ModelNode> & /*selectedNodeL m_timelineWidget->graphicsScene()->update(); } +void TimelineView::auxiliaryDataChanged(const ModelNode &modelNode, + const PropertyName &name, + const QVariant &data) +{ + if (name == QmlDesigner::lockedProperty && data.toBool() && modelNode.isValid()) { + for (const auto &node : modelNode.allSubModelNodesAndThisNode()) { + if (node.hasAuxiliaryData("timeline_expanded")) + m_timelineWidget->graphicsScene()->invalidateHeightForTarget(node); + } + } +} + void TimelineView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) { for (const auto &property : propertyList) { diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.h b/src/plugins/qmldesigner/components/timelineeditor/timelineview.h index fe3f5903ff3..f1f39b6a35f 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.h @@ -62,6 +62,9 @@ public: PropertyChangeFlags propertyChange) override; void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) override; + void auxiliaryDataChanged(const ModelNode &node, + const PropertyName &name, + const QVariant &data) override; void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override; void propertiesRemoved(const QList<AbstractProperty> &propertyList) override; diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp index 036fe173f5c..3b14a9986cd 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp @@ -218,7 +218,7 @@ void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode & const QList<QGraphicsItem *> items = m_layout->childItems(); for (auto child : items) - TimelineSectionItem::updateDataForTarget(child, target, &found); + TransitionEditorSectionItem::updateDataForTarget(child, target, &found); if (!found) invalidateScene(); @@ -227,6 +227,18 @@ void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode & invalidateLayout(); } +void TransitionEditorGraphicsScene::invalidateHeightForTarget(const ModelNode &target) +{ + if (!target.isValid()) + return; + + const auto children = m_layout->childItems(); + for (auto child : children) + TransitionEditorSectionItem::updateHeightForTarget(child, target); + + invalidateLayout(); +} + void TransitionEditorGraphicsScene::invalidateScene() { invalidateScrollbar(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h index 2f04c5b7298..d7174df55a6 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h @@ -93,6 +93,7 @@ public: void setRulerScaling(int scaling); void invalidateSectionForTarget(const ModelNode &modelNode); + void invalidateHeightForTarget(const ModelNode &modelNode); void invalidateScene(); void invalidateCurrentValues(); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp index 86442059d9c..7172465e0d5 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp @@ -196,6 +196,17 @@ void TransitionEditorSectionItem::updateData(QGraphicsItem *item) sectionItem->updateData(); } +void TransitionEditorSectionItem::updateHeightForTarget(QGraphicsItem *item, const ModelNode &target) +{ + if (!target.isValid()) + return; + + if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item)) { + if (sectionItem->targetNode() == target) + sectionItem->updateHeight(); + } +} + void TransitionEditorSectionItem::invalidateBar(QGraphicsItem *item) { if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item)) @@ -360,7 +371,8 @@ void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent if (event->button() == Qt::LeftButton) { event->accept(); - toggleCollapsed(); + if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + toggleCollapsed(); } } @@ -392,7 +404,8 @@ void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *ev if (m_targetNode.isValid()) m_targetNode.view()->setSelectedModelNode(m_targetNode); } else { - toggleCollapsed(); + if (!ModelNode::isThisOrAncestorLocked(m_targetNode)) + toggleCollapsed(); } update(); } @@ -417,6 +430,12 @@ void TransitionEditorSectionItem::updateData() update(); } +void TransitionEditorSectionItem::updateHeight() +{ + invalidateHeight(); + update(); +} + const QList<QGraphicsItem *> TransitionEditorSectionItem::propertyItems() const { QList<QGraphicsItem *> list; @@ -488,7 +507,8 @@ void TransitionEditorSectionItem::invalidateProperties() bool TransitionEditorSectionItem::collapsed() const { - return m_targetNode.isValid() && !m_targetNode.hasAuxiliaryData("timeline_expanded"); + return m_targetNode.isValid() + && (!m_targetNode.hasAuxiliaryData("transition_expanded") || m_targetNode.locked()); } qreal TransitionEditorSectionItem::rulerWidth() const @@ -501,9 +521,9 @@ void TransitionEditorSectionItem::toggleCollapsed() QTC_ASSERT(m_targetNode.isValid(), return ); if (collapsed()) - m_targetNode.setAuxiliaryData("timeline_expanded", true); + m_targetNode.setAuxiliaryData("transition_expanded", true); else - m_targetNode.removeAuxiliaryData("timeline_expanded"); + m_targetNode.removeAuxiliaryData("transition_expanded"); invalidateHeight(); } @@ -592,6 +612,11 @@ void TransitionEditorBarItem::commitPosition(const QPointF & /*point*/) scrollOffsetChanged(); } +bool TransitionEditorBarItem::isLocked() const +{ + return sectionItem() && sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked(); +} + void TransitionEditorBarItem::scrollOffsetChanged() { if (sectionItem()) @@ -637,7 +662,9 @@ void TransitionEditorBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) const auto p = event->pos(); QRectF left, right; - if (handleRects(rect(), left, right)) { + if (isLocked() && rect().contains(p)) { + setCursor(QCursor(Qt::ForbiddenCursor)); + } else if (handleRects(rect(), left, right)) { if (left.contains(p) || right.contains(p)) { if (cursor().shape() != Qt::SizeHorCursor) setCursor(QCursor(Qt::SizeHorCursor)); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h index d7ce78f56c2..63828b52f54 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h @@ -54,6 +54,8 @@ public: void itemMoved(const QPointF &start, const QPointF &end) override; void commitPosition(const QPointF &point) override; + bool isLocked() const override; + protected: void scrollOffsetChanged() override; void paint(QPainter *painter, @@ -106,6 +108,7 @@ public: static void updateData(QGraphicsItem *item); static void invalidateBar(QGraphicsItem *item); static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b); + static void updateHeightForTarget(QGraphicsItem *item, const ModelNode &target); void moveAllDurations(qreal offset); void scaleAllDurations(qreal scale); @@ -125,6 +128,7 @@ protected: void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; private: + void updateHeight(); void invalidateHeight(); void invalidateProperties(); bool collapsed() const; diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index ef83b17d90a..e9cfb3fd729 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -142,6 +142,18 @@ void TransitionEditorView::selectedNodesChanged(const QList<ModelNode> & /*selec } +void TransitionEditorView::auxiliaryDataChanged(const ModelNode &modelNode, + const PropertyName &name, + const QVariant &data) +{ + if (name == QmlDesigner::lockedProperty && data.toBool() && modelNode.isValid()) { + for (const auto &node : modelNode.allSubModelNodesAndThisNode()) { + if (node.hasAuxiliaryData("transition_expanded")) + m_transitionEditorWidget->graphicsScene()->invalidateHeightForTarget(node); + } + } +} + void TransitionEditorView::propertiesAboutToBeRemoved( const QList<AbstractProperty> & /*propertyList */) { @@ -217,7 +229,7 @@ ModelNode TransitionEditorView::addNewTransition() QStringList newlist = idPropertyList.value(targetId); for (const QString &str :locList) if (!newlist.contains(str)) - newlist.append(str); + newlist.append(str); idPropertyList.insert(targetId, newlist); } else { if (!locList.isEmpty()) diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h index 1476a07353d..857467e3cd9 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h @@ -60,6 +60,9 @@ public: PropertyChangeFlags propertyChange) override; void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, const QList<ModelNode> &lastSelectedNodeList) override; + void auxiliaryDataChanged(const ModelNode &node, + const PropertyName &name, + const QVariant &data) override; void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override; void propertiesRemoved(const QList<AbstractProperty> &propertyList) override; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 12cb4575717..6b0276c8b15 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -147,6 +147,7 @@ public: void setSelectedModelNodes(const QList<ModelNode> &selectedNodeList); void setSelectedModelNode(const ModelNode &modelNode); + void selectModelNode(const ModelNode &node); void deselectModelNode(const ModelNode &node); void clearSelectedModelNodes(); diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 3197e746ab5..19bed05a255 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -65,7 +65,9 @@ QMLDESIGNERCORE_EXPORT QList<Internal::InternalNodePointer> toInternalNodeList(c using PropertyListType = QList<QPair<PropertyName, QVariant> >; -class QMLDESIGNERCORE_EXPORT ModelNode +static const PropertyName lockedProperty = {("locked")}; + +class QMLDESIGNERCORE_EXPORT ModelNode { friend QMLDESIGNERCORE_EXPORT bool operator ==(const ModelNode &firstNode, const ModelNode &secondNode); friend QMLDESIGNERCORE_EXPORT bool operator !=(const ModelNode &firstNode, const ModelNode &secondNode); @@ -216,6 +218,11 @@ public: void setGlobalStatus(const GlobalAnnotationStatus &status); void removeGlobalStatus(); + bool locked() const; + void setLocked(bool value); + + static bool isThisOrAncestorLocked(const ModelNode &node); + qint32 internalId() const; void setNodeSource(const QString&); @@ -241,6 +248,8 @@ public: private: // functions Internal::InternalNodePointer internalNode() const; + void removeLocked(); + bool hasLocked() const; private: // variables Internal::InternalNodePointer m_internalNode; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 492d6c62b6b..c4779543e84 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -534,11 +534,11 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &value) { - if (((node.isRootNode() && (name == "width" || name == "height")) || name == "invisible") + if (((node.isRootNode() && (name == "width" || name == "height")) || name == "invisible" || name == "locked") || name.endsWith(PropertyName("@NodeInstance"))) { if (hasInstanceForModelNode(node)) { NodeInstance instance = instanceForModelNode(node); - if (value.isValid() || name == "invisible") { + if (value.isValid() || name == "invisible" || name == "locked") { PropertyValueContainer container{instance.instanceId(), name, value, TypeName()}; m_nodeInstanceServer->changeAuxiliaryValues({{container}}); } else { diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index d7d512334d9..6a1a8d4897b 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -35,6 +35,7 @@ #ifndef QMLDESIGNER_TEST #include <qmldesignerplugin.h> #include <viewmanager.h> +#include <nodeabstractproperty.h> #endif #include <coreplugin/helpmanager.h> @@ -397,7 +398,7 @@ QList<ModelNode> AbstractView::toModelNodeList(const QList<Internal::InternalNod QList<ModelNode> toModelNodeList(const QList<Internal::InternalNode::Pointer> &nodeList, AbstractView *view) { QList<ModelNode> newNodeList; - foreach (const Internal::InternalNode::Pointer &node, nodeList) + for (const Internal::InternalNode::Pointer &node : nodeList) newNodeList.append(ModelNode(node, view->model(), view)); return newNodeList; @@ -406,7 +407,7 @@ QList<ModelNode> toModelNodeList(const QList<Internal::InternalNode::Pointer> &n QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode> &nodeList) { QList<Internal::InternalNode::Pointer> newNodeList; - foreach (const ModelNode &node, nodeList) + for (const ModelNode &node : nodeList) newNodeList.append(node.internalNode()); return newNodeList; @@ -414,15 +415,26 @@ QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode> /*! Sets the list of nodes to the actual selected nodes specified by - \a selectedNodeList. + \a selectedNodeList if the node or its ancestors are not locked. */ void AbstractView::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList) { - model()->d->setSelectedNodes(toInternalNodeList(selectedNodeList)); + QList<ModelNode> unlockedNodes; + + for (const auto &modelNode : selectedNodeList) { + if (!ModelNode::isThisOrAncestorLocked(modelNode)) + unlockedNodes.push_back(modelNode); + } + + model()->d->setSelectedNodes(toInternalNodeList(unlockedNodes)); } void AbstractView::setSelectedModelNode(const ModelNode &modelNode) { + if (ModelNode::isThisOrAncestorLocked(modelNode)) { + clearSelectedModelNodes(); + return; + } setSelectedModelNodes({modelNode}); } diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 5c1ffce639a..688dae2d074 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -1228,6 +1228,53 @@ void ModelNode::removeGlobalStatus() } } +bool ModelNode::locked() const +{ + if (hasLocked()) + return auxiliaryData(lockedProperty).toBool(); + + return false; +} + +bool ModelNode::hasLocked() const +{ + return hasAuxiliaryData(lockedProperty); +} + +void ModelNode::setLocked(bool value) +{ + setAuxiliaryData(lockedProperty, value); + + if (value) { + // Remove newly locked node and all its descendants from potential selection + for (ModelNode node : allSubModelNodesAndThisNode()) { + node.deselectNode(); + node.removeAuxiliaryData("timeline_expanded"); + node.removeAuxiliaryData("transition_expanded"); + } + } +} + +void ModelNode::removeLocked() +{ + if (hasLocked()) + removeAuxiliaryData(lockedProperty); +} + +bool ModelNode::isThisOrAncestorLocked(const ModelNode &node) +{ + if (!node.isValid()) + return false; + + if (node.locked()) + return true; + + if (node.isRootNode() || !node.hasParentProperty()) + return false; + + return isThisOrAncestorLocked(node.parentProperty().parentModelNode()); +} + void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList) { model()->d->setScriptFunctions(internalNode(), scriptFunctionList); |