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>
This commit is contained in:
Henning Gruendl
2020-10-06 12:29:09 +02:00
committed by Henning Gründl
parent 15f39cf37c
commit 2860e57112
38 changed files with 503 additions and 119 deletions

View File

@@ -98,33 +98,37 @@ QtObject {
readonly property string idAliasOff: "\u005B"
readonly property string idAliasOn: "\u005C"
readonly property string listView: "\u005D"
readonly property string mergeCells: "\u005E"
readonly property string minus: "\u005F"
readonly property string plus: "\u0060"
readonly property string redo: "\u0061"
readonly property string splitColumns: "\u0062"
readonly property string splitRows: "\u0063"
readonly property string startNode: "\u0064"
readonly property string testIcon: "\u0065"
readonly property string textAlignBottom: "\u0066"
readonly property string textAlignCenter: "\u0067"
readonly property string textAlignLeft: "\u0068"
readonly property string textAlignMiddle: "\u0069"
readonly property string textAlignRight: "\u006A"
readonly property string textAlignTop: "\u006B"
readonly property string textBulletList: "\u006C"
readonly property string textFullJustification: "\u006D"
readonly property string textNumberedList: "\u006E"
readonly property string tickIcon: "\u006F"
readonly property string triState: "\u0070"
readonly property string undo: "\u0071"
readonly property string upDownIcon: "\u0072"
readonly property string upDownSquare2: "\u0073"
readonly property string wildcard: "\u0074"
readonly property string zoomAll: "\u0075"
readonly property string zoomIn: "\u0076"
readonly property string zoomOut: "\u0077"
readonly property string zoomSelection: "\u0078"
readonly property string lockOff: "\u005E"
readonly property string lockOn: "\u005F"
readonly property string mergeCells: "\u0060"
readonly property string minus: "\u0061"
readonly property string plus: "\u0062"
readonly property string redo: "\u0063"
readonly property string splitColumns: "\u0064"
readonly property string splitRows: "\u0065"
readonly property string startNode: "\u0066"
readonly property string testIcon: "\u0067"
readonly property string textAlignBottom: "\u0068"
readonly property string textAlignCenter: "\u0069"
readonly property string textAlignLeft: "\u006A"
readonly property string textAlignMiddle: "\u006B"
readonly property string textAlignRight: "\u006C"
readonly property string textAlignTop: "\u006D"
readonly property string textBulletList: "\u006E"
readonly property string textFullJustification: "\u006F"
readonly property string textNumberedList: "\u0070"
readonly property string tickIcon: "\u0071"
readonly property string triState: "\u0072"
readonly property string undo: "\u0073"
readonly property string upDownIcon: "\u0074"
readonly property string upDownSquare2: "\u0075"
readonly property string visibilityOff: "\u0076"
readonly property string visibilityOn: "\u0077"
readonly property string wildcard: "\u0078"
readonly property string zoomAll: "\u0079"
readonly property string zoomIn: "\u007A"
readonly property string zoomOut: "\u007B"
readonly property string zoomSelection: "\u007C"
readonly property font iconFont: Qt.font({
"family": controlIcons.name,

View File

@@ -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);
}
}
};

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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))

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);
IconCheckboxItemDelegate *aliasDelegate =
new IconCheckboxItemDelegate(this, aliasOnIcon, aliasOffIcon);
#ifdef _LOCK_ITEMS_
IconCheckboxItemDelegate *lockDelegate =
new IconCheckboxItemDelegate(this,
Utils::Icons::LOCKED_TOOLBAR.icon(),
Utils::Icons::UNLOCKED_TOOLBAR.icon());
#endif
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
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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(

View File

@@ -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;

View File

@@ -156,4 +156,9 @@ TimelineFrameHandle *TimelineMovableAbstractItem::asTimelineFrameHandle()
return nullptr;
}
bool TimelineMovableAbstractItem::isLocked() const
{
return false;
}
} // namespace QmlDesigner

View File

@@ -69,6 +69,8 @@ public:
virtual TimelineKeyframeItem *asTimelineKeyframeItem();
virtual TimelineFrameHandle *asTimelineFrameHandle();
virtual bool isLocked() const;
protected:
int scrollOffset() const;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;

View File

@@ -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())

View File

@@ -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"));

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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();

View File

@@ -93,6 +93,7 @@ public:
void setRulerScaling(int scaling);
void invalidateSectionForTarget(const ModelNode &modelNode);
void invalidateHeightForTarget(const ModelNode &modelNode);
void invalidateScene();
void invalidateCurrentValues();

View File

@@ -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));

View File

@@ -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;

View File

@@ -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())

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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});
}

View File

@@ -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);