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

View File

@@ -347,25 +347,27 @@ public:
&& !selectionContext().currentSingleSelectedNode().isRootNode() && !selectionContext().currentSingleSelectedNode().isRootNode()
&& selectionContext().currentSingleSelectedNode().hasParentProperty()) { && selectionContext().currentSingleSelectedNode().hasParentProperty()) {
ActionTemplate *selectionAction = new ActionTemplate(QString(), &ModelNodeOperations::select);
selectionAction->setParent(menu());
parentNode = selectionContext().currentSingleSelectedNode().parentProperty().parentModelNode(); parentNode = selectionContext().currentSingleSelectedNode().parentProperty().parentModelNode();
selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select parent: %1")).arg( if (!ModelNode::isThisOrAncestorLocked(parentNode)) {
captionForModelNode(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(); SelectionContext nodeSelectionContext = selectionContext();
nodeSelectionContext.setTargetNode(parentNode); nodeSelectionContext.setTargetNode(parentNode);
selectionAction->setSelectionContext(nodeSelectionContext); 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() if (node != selectionContext().currentSingleSelectedNode()
&& node != parentNode && node != parentNode
&& contains(node, selectionContext().scenePosition()) && contains(node, selectionContext().scenePosition())
&& !node.isRootNode()) { && !node.isRootNode()
&& !ModelNode::isThisOrAncestorLocked(node)) {
selectionContext().setTargetNode(node); selectionContext().setTargetNode(node);
QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node)); QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node));
ActionTemplate *selectionAction = new ActionTemplate(what, &ModelNodeOperations::select); ActionTemplate *selectionAction = new ActionTemplate(what, &ModelNodeOperations::select);
@@ -377,6 +379,9 @@ public:
menu()->addAction(selectionAction); menu()->addAction(selectionAction);
} }
} }
if (menu()->isEmpty())
action()->setEnabled(false);
} }
} }
}; };

View File

@@ -107,6 +107,8 @@ public:
idAliasOff, idAliasOff,
idAliasOn, idAliasOn,
listView, listView,
lockOff,
lockOn,
mergeCells, mergeCells,
minus, minus,
plus, plus,
@@ -129,6 +131,8 @@ public:
undo, undo,
upDownIcon, upDownIcon,
upDownSquare2, upDownSquare2,
visibilityOff,
visibilityOn,
wildcard, wildcard,
zoomAll, zoomAll,
zoomIn, zoomIn,

View File

@@ -75,6 +75,23 @@ ConnectionModel::ConnectionModel(ConnectionView *parent)
connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged); 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() void ConnectionModel::resetModel()
{ {
beginResetModel(); beginResetModel();
@@ -82,7 +99,7 @@ void ConnectionModel::resetModel()
setHorizontalHeaderLabels(QStringList({ tr("Target"), tr("Signal Handler"), tr("Action") })); setHorizontalHeaderLabels(QStringList({ tr("Target"), tr("Signal Handler"), tr("Action") }));
if (connectionView()->isAttached()) { if (connectionView()->isAttached()) {
for (const ModelNode modelNode : connectionView()->allModelNodes()) for (const ModelNode &modelNode : connectionView()->allModelNodes())
addModelNode(modelNode); addModelNode(modelNode);
} }
@@ -94,8 +111,8 @@ void ConnectionModel::resetModel()
SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const
{ {
const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); const int internalId = data(index(rowNumber, TargetModelNodeRow), UserRoles::InternalIdRole).toInt();
const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), UserRoles::TargetPropertyNameRole).toString();
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
if (modelNode.isValid()) if (modelNode.isValid())
@@ -256,8 +273,8 @@ void ConnectionModel::updateTargetNode(int rowNumber)
void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty) void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty)
{ {
item->setData(signalHandlerProperty.parentModelNode().internalId(), Qt::UserRole + 1); item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole);
item->setData(signalHandlerProperty.name(), Qt::UserRole + 2); item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole);
} }
ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const

View File

@@ -48,7 +48,14 @@ public:
TargetPropertyNameRow = 1, TargetPropertyNameRow = 1,
SourceRow = 2 SourceRow = 2
}; };
enum UserRoles {
InternalIdRole = Qt::UserRole + 1,
TargetPropertyNameRole
};
ConnectionModel(ConnectionView *parent = nullptr); ConnectionModel(ConnectionView *parent = nullptr);
Qt::ItemFlags flags(const QModelIndex &modelIndex) const override;
void resetModel(); void resetModel();
SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const; SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const;
ConnectionView *connectionView() const; ConnectionView *connectionView() const;

View File

@@ -36,6 +36,8 @@
#include <variantproperty.h> #include <variantproperty.h>
#include <signalhandlerproperty.h> #include <signalhandlerproperty.h>
#include <QTableView>
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
@@ -162,6 +164,33 @@ void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeL
emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.count() == 1); 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*/) void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, const QList<Import> & /*removedImports*/)
{ {
backendModel()->resetModel(); backendModel()->resetModel();

View File

@@ -69,6 +69,7 @@ public:
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, 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 importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) 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* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &point, const QList<QGraphicsItem*> &itemList)
{ {
FormEditorItem* nearestItem = nullptr; FormEditorItem* nearestItem = nullptr;
foreach (QGraphicsItem *item, itemList) { for (QGraphicsItem *item : itemList) {
FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
if (formEditorItem && formEditorItem->flowHitTest(point)) if (formEditorItem && formEditorItem->flowHitTest(point))
@@ -201,6 +201,9 @@ FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &poi
if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible()) if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible())
continue; continue;
if (formEditorItem && ModelNode::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode()))
continue;
if (!nearestItem) if (!nearestItem)
nearestItem = formEditorItem; nearestItem = formEditorItem;
else if (formEditorItem->selectionWeigth(point, 1) < nearestItem->selectionWeigth(point, 0)) 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()) if (m_dragNode.isValid())
view()->setSelectedModelNode(m_dragNode); view()->setSelectedModelNode(m_dragNode);
m_dragNode = QmlItemNode(); m_dragNode = QmlItemNode();
view()->changeToSelectionTool(); view()->changeToSelectionTool();

View File

@@ -90,10 +90,10 @@ void RubberBandSelectionManipulator::select(SelectionType selectionType)
if (!m_beginFormEditorItem) if (!m_beginFormEditorItem)
return; 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; QList<QmlItemNode> newNodeList;
foreach (QGraphicsItem* item, itemList) for (QGraphicsItem *item : itemList)
{ {
FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item); 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; m_itemList = itemList;
} }

View File

@@ -49,6 +49,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <utils/algorithm.h>
#include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsmodelmanagerinterface.h>
@@ -57,6 +58,7 @@
#include <QDebug> #include <QDebug>
#include <QApplication> #include <QApplication>
#include <QMessageBox>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QRandomGenerator> #include <QRandomGenerator>
@@ -375,9 +377,41 @@ void DesignDocument::deleteSelected()
if (!currentModel()) if (!currentModel())
return; 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](){ rewriterView()->executeInTransaction("DesignDocument::deleteSelected", [this](){
QList<ModelNode> toDelete = view()->selectedModelNodes(); QList<ModelNode> toDelete = view()->selectedModelNodes();
foreach (ModelNode node, toDelete) {
for (ModelNode node : toDelete) {
if (node.isValid() && !node.isRootNode() && QmlObjectNode::isValidQmlObjectNode(node)) if (node.isValid() && !node.isRootNode() && QmlObjectNode::isValidQmlObjectNode(node))
QmlObjectNode(node).destroy(); QmlObjectNode(node).destroy();
} }

View File

@@ -200,10 +200,10 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
if (!modelNode.isValid()) if (!modelNode.isValid())
return QVariant(); return QVariant();
if (role == ItemIsVisibleRole) //independent of column if (role == ItemIsVisibleRole) // independent of column
return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked;
if (index.column() == 0) { if (index.column() == ColumnType::Name) {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
return modelNode.displayName(); return modelNode.displayName();
} else if (role == Qt::DecorationRole) { } else if (role == Qt::DecorationRole) {
@@ -240,18 +240,24 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
} else if (role == ModelNodeRole) { } else if (role == ModelNodeRole) {
return QVariant::fromValue<ModelNode>(modelNode); return QVariant::fromValue<ModelNode>(modelNode);
} }
} else if (index.column() == 1) { //export } else if (index.column() == ColumnType::Alias) { // export
if (role == Qt::CheckStateRole) if (role == Qt::CheckStateRole)
return currentQmlObjectNode.isAliasExported() ? Qt::Checked : Qt::Unchecked; return currentQmlObjectNode.isAliasExported() ? Qt::Checked : Qt::Unchecked;
else if (role == Qt::ToolTipRole) else if (role == Qt::ToolTipRole)
return tr("Toggles whether this item is exported as an " return tr("Toggles whether this item is exported as an "
"alias property of the root item."); "alias property of the root item.");
} else if (index.column() == 2) { //visible } else if (index.column() == ColumnType::Visibility) { // visible
if (role == Qt::CheckStateRole) if (role == Qt::CheckStateRole)
return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked; return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked;
else if (role == Qt::ToolTipRole) else if (role == Qt::ToolTipRole)
return tr("Toggles the visibility of this item in the form editor.\n" return tr("Toggles the visibility of this item in the form editor.\n"
"This is independent of the visibility property in QML."); "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(); return QVariant();
@@ -259,7 +265,16 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
Qt::ItemFlags NavigatorTreeModel::flags(const QModelIndex &index) 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::ItemIsEditable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable
@@ -378,7 +393,7 @@ int NavigatorTreeModel::columnCount(const QModelIndex &parent) const
if (parent.column() > 0) if (parent.column() > 0)
return 0; return 0;
return 3; return ColumnType::Count;
} }
ModelNode NavigatorTreeModel::modelNodeForIndex(const QModelIndex &index) const 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) bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{ {
ModelNode modelNode = modelNodeForIndex(index); 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); QTC_ASSERT(m_view, return false);
m_view->handleChangedExport(modelNode, value.toInt() != 0); 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); QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0);
} else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) {
modelNode.setLocked(value.toInt() != 0);
} }
return true; return true;
@@ -806,7 +823,7 @@ void NavigatorTreeModel::notifyDataChanged(const ModelNode &modelNode)
{ {
const QModelIndex index = indexForModelNode(modelNode); const QModelIndex index = indexForModelNode(modelNode);
const QAbstractItemModel *model = index.model(); 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); emit dataChanged(index, sibling);
} }

View File

@@ -49,6 +49,14 @@ class NavigatorTreeModel : public QAbstractItemModel, public NavigatorModelInter
public: public:
enum ColumnType {
Name = 0,
Alias,
Visibility,
Lock,
Count
};
explicit NavigatorTreeModel(QObject *parent = nullptr); explicit NavigatorTreeModel(QObject *parent = nullptr);
~NavigatorTreeModel() override; ~NavigatorTreeModel() override;

View File

@@ -41,6 +41,7 @@
#include <qmlitemnode.h> #include <qmlitemnode.h>
#include <rewritingexception.h> #include <rewritingexception.h>
#include <nodeinstanceview.h> #include <nodeinstanceview.h>
#include <theme.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -48,6 +49,7 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/icon.h> #include <utils/icon.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <utils/stylehelper.h>
#include <QHeaderView> #include <QHeaderView>
#include <QTimer> #include <QTimer>
@@ -138,13 +140,14 @@ void NavigatorView::modelAttached(Model *model)
QTreeView *treeView = treeWidget(); QTreeView *treeView = treeWidget();
treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); treeView->header()->setSectionResizeMode(NavigatorTreeModel::ColumnType::Name, QHeaderView::Stretch);
treeView->header()->resizeSection(1,26); treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Alias, 26);
treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Visibility, 26);
treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Lock, 26);
treeView->setIndentation(20); treeView->setIndentation(20);
m_currentModelInterface->setFilter(false); m_currentModelInterface->setFilter(false);
QTimer::singleShot(0, this, [this, treeView]() { QTimer::singleShot(0, this, [this, treeView]() {
m_currentModelInterface->setFilter( m_currentModelInterface->setFilter(
DesignerSettings::getValue(DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS).toBool()); 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) void NavigatorView::modelAboutToBeDetached(Model *model)
@@ -304,7 +303,7 @@ void NavigatorView::nodeIdChanged(const ModelNode& modelNode, const QString & /*
m_currentModelInterface->notifyDataChanged(modelNode); 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); 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()); m_currentModelInterface->notifyDataChanged(rootModelNode());
} }
@@ -332,9 +331,12 @@ void NavigatorView::nodeTypeChanged(const ModelNode &modelNode, const TypeName &
} }
void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode, void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode,
const PropertyName & /*name*/, const PropertyName &name,
const QVariant & /*data*/) const QVariant &data)
{ {
Q_UNUSED(name)
Q_UNUSED(data)
m_currentModelInterface->notifyDataChanged(modelNode); m_currentModelInterface->notifyDataChanged(modelNode);
} }
@@ -344,8 +346,8 @@ void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList
m_currentModelInterface->notifyDataChanged(modelNode); m_currentModelInterface->notifyDataChanged(modelNode);
} }
void NavigatorView::nodeOrderChanged(const NodeListProperty & listProperty, void NavigatorView::nodeOrderChanged(const NodeListProperty &listProperty,
const ModelNode & /*node*/, const ModelNode &/*node*/,
int /*oldIndex*/) int /*oldIndex*/)
{ {
m_currentModelInterface->notifyModelNodesMoved(listProperty.directSubNodes()); m_currentModelInterface->notifyModelNodesMoved(listProperty.directSubNodes());
@@ -613,33 +615,50 @@ void NavigatorView::setupWidget()
connect(m_widget.data(), &NavigatorWidget::reverseOrderToggled, this, &NavigatorView::reverseOrderToggled); connect(m_widget.data(), &NavigatorWidget::reverseOrderToggled, this, &NavigatorView::reverseOrderToggled);
#ifndef QMLDESIGNER_TEST #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); auto idDelegate = new NameItemDelegate(this);
IconCheckboxItemDelegate *showDelegate =
new IconCheckboxItemDelegate(this,
Utils::Icons::EYE_OPEN_TOOLBAR.icon(),
Utils::Icons::EYE_CLOSED_TOOLBAR.icon());
IconCheckboxItemDelegate *exportDelegate = IconCheckboxItemDelegate *visibilityDelegate =
new IconCheckboxItemDelegate(this, new IconCheckboxItemDelegate(this, visibilityOnIcon, visibilityOffIcon);
Icons::EXPORT_CHECKED.icon(),
Icons::EXPORT_UNCHECKED.icon()); IconCheckboxItemDelegate *aliasDelegate =
new IconCheckboxItemDelegate(this, aliasOnIcon, aliasOffIcon);
#ifdef _LOCK_ITEMS_
IconCheckboxItemDelegate *lockDelegate = IconCheckboxItemDelegate *lockDelegate =
new IconCheckboxItemDelegate(this, new IconCheckboxItemDelegate(this, lockOnIcon, lockOffIcon);
Utils::Icons::LOCKED_TOOLBAR.icon(),
Utils::Icons::UNLOCKED_TOOLBAR.icon());
#endif
treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Name, idDelegate);
treeWidget()->setItemDelegateForColumn(0, idDelegate); treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Alias, aliasDelegate);
#ifdef _LOCK_ITEMS_ treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Visibility, visibilityDelegate);
treeWidget()->setItemDelegateForColumn(1,lockDelegate); treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Lock, lockDelegate);
treeWidget()->setItemDelegateForColumn(2,showDelegate);
#else
treeWidget()->setItemDelegateForColumn(1, exportDelegate);
treeWidget()->setItemDelegateForColumn(2, showDelegate);
#endif
#endif //QMLDESIGNER_TEST #endif //QMLDESIGNER_TEST
} }

View File

@@ -82,7 +82,7 @@ public:
void propertiesRemoved(const QList<AbstractProperty>& propertyList) override; void propertiesRemoved(const QList<AbstractProperty>& propertyList) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList , 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 auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override;
void instanceErrorChanged(const QVector<ModelNode> &errorNodeList) override; void instanceErrorChanged(const QVector<ModelNode> &errorNodeList) override;

View File

@@ -78,7 +78,7 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view)
#endif #endif
} }
void NavigatorWidget::setTreeModel(QAbstractItemModel* model) void NavigatorWidget::setTreeModel(QAbstractItemModel *model)
{ {
m_treeView->setModel(model); m_treeView->setModel(model);
} }
@@ -92,7 +92,6 @@ QList<QToolButton *> NavigatorWidget::createToolBarWidgets()
{ {
QList<QToolButton *> buttons; QList<QToolButton *> buttons;
auto button = new QToolButton(); auto button = new QToolButton();
button->setIcon(Icons::ARROW_LEFT.icon()); button->setIcon(Icons::ARROW_LEFT.icon());
button->setToolTip(tr("Become last sibling of parent (CTRL + Left).")); button->setToolTip(tr("Become last sibling of parent (CTRL + Left)."));
@@ -180,7 +179,6 @@ void NavigatorWidget::enableNavigator()
m_treeView->setEnabled(true); m_treeView->setEnabled(true);
} }
NavigatorView *NavigatorWidget::navigatorView() const NavigatorView *NavigatorWidget::navigatorView() const
{ {
return m_navigatorView.data(); return m_navigatorView.data();

View File

@@ -30,6 +30,7 @@
#include <QDebug> #include <QDebug>
#include <QRegularExpression> #include <QRegularExpression>
#include <QMessageBox>
#include <cmath> #include <cmath>
#include <memory> #include <memory>
@@ -42,6 +43,7 @@
#include <qmlitemnode.h> #include <qmlitemnode.h>
#include <qmlstate.h> #include <qmlstate.h>
#include <annotationeditor/annotationeditor.h> #include <annotationeditor/annotationeditor.h>
#include <utils/algorithm.h>
namespace QmlDesigner { namespace QmlDesigner {
@@ -92,11 +94,46 @@ void StatesEditorView::removeState(int nodeId)
if (nodeId > 0 && hasModelNodeForInternalId(nodeId)) { if (nodeId > 0 && hasModelNodeForInternalId(nodeId)) {
ModelNode stateNode(modelNodeForInternalId(nodeId)); ModelNode stateNode(modelNodeForInternalId(nodeId));
Q_ASSERT(stateNode.metaInfo().isSubclassOf("QtQuick.State")); 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(); NodeListProperty parentProperty = stateNode.parentProperty().toNodeListProperty();
if (parentProperty.count() <= 1) { if (parentProperty.count() <= 1) {
setCurrentState(baseState()); setCurrentState(baseState());
} else if (parentProperty.isValid()){ } else if (parentProperty.isValid()) {
int index = parentProperty.indexOf(stateNode); int index = parentProperty.indexOf(stateNode);
if (index == 0) if (index == 0)
setCurrentState(parentProperty.at(1)); setCurrentState(parentProperty.at(1));
@@ -104,7 +141,6 @@ void StatesEditorView::removeState(int nodeId)
setCurrentState(parentProperty.at(index - 1)); setCurrentState(parentProperty.at(index - 1));
} }
stateNode.destroy(); stateNode.destroy();
} }
} catch (const RewritingException &e) { } catch (const RewritingException &e) {

View File

@@ -428,6 +428,18 @@ void TimelineGraphicsScene::invalidateKeyframesForTarget(const ModelNode &target
TimelineSectionItem::updateFramesForTarget(child, 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() void TimelineGraphicsScene::invalidateScene()
{ {
ModelNode node = timelineView()->modelNodeForId( ModelNode node = timelineView()->modelNodeForId(

View File

@@ -158,6 +158,7 @@ public:
void invalidateSectionForTarget(const ModelNode &modelNode); void invalidateSectionForTarget(const ModelNode &modelNode);
void invalidateKeyframesForTarget(const ModelNode &modelNode); void invalidateKeyframesForTarget(const ModelNode &modelNode);
void invalidateHeightForTarget(const ModelNode &modelNode);
void invalidateScene(); void invalidateScene();
void invalidateScrollbar() override; void invalidateScrollbar() override;

View File

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

View File

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

View File

@@ -70,6 +70,9 @@ void TimelineMoveTool::mousePressEvent(TimelineMovableAbstractItem *item,
{ {
Q_UNUSED(item) Q_UNUSED(item)
if (currentItem() && currentItem()->isLocked())
return;
if (auto *current = currentItem()->asTimelineKeyframeItem()) { if (auto *current = currentItem()->asTimelineKeyframeItem()) {
const qreal sourceFrame = qRound(current->mapFromSceneToFrame(current->rect().center().x())); const qreal sourceFrame = qRound(current->mapFromSceneToFrame(current->rect().center().x()));
const qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x())); const qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x()));
@@ -85,6 +88,9 @@ void TimelineMoveTool::mouseMoveEvent(TimelineMovableAbstractItem *item,
if (!currentItem()) if (!currentItem())
return; return;
if (currentItem()->isLocked())
return;
if (auto *current = currentItem()->asTimelineKeyframeItem()) { if (auto *current = currentItem()->asTimelineKeyframeItem()) {
// prevent dragging if deselecting a keyframe (Ctrl+click and drag a selected keyframe) // prevent dragging if deselecting a keyframe (Ctrl+click and drag a selected keyframe)
if (!current->highlighted()) 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) void TimelineSectionItem::moveAllFrames(qreal offset)
{ {
if (m_timeline.isValid()) if (m_timeline.isValid())
@@ -313,7 +324,8 @@ void TimelineSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
event->accept(); event->accept();
toggleCollapsed(); if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
toggleCollapsed();
} }
} }
@@ -345,7 +357,8 @@ void TimelineSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
if (m_targetNode.isValid()) if (m_targetNode.isValid())
m_targetNode.view()->setSelectedModelNode(m_targetNode); m_targetNode.view()->setSelectedModelNode(m_targetNode);
} else { } else {
toggleCollapsed(); if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
toggleCollapsed();
} }
update(); update();
} }
@@ -414,6 +427,12 @@ void TimelineSectionItem::updateFrames()
update(); update();
} }
void TimelineSectionItem::updateHeight()
{
invalidateHeight();
update();
}
void TimelineSectionItem::invalidateHeight() void TimelineSectionItem::invalidateHeight()
{ {
int height = 0; int height = 0;
@@ -464,7 +483,8 @@ void TimelineSectionItem::invalidateFrames()
bool TimelineSectionItem::collapsed() const 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() void TimelineSectionItem::createPropertyItems()
@@ -845,6 +865,11 @@ void TimelineBarItem::commitPosition(const QPointF & /*point*/)
m_oldRect = QRectF(); m_oldRect = QRectF();
} }
bool TimelineBarItem::isLocked() const
{
return sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked();
}
void TimelineBarItem::scrollOffsetChanged() void TimelineBarItem::scrollOffsetChanged()
{ {
sectionItem()->invalidateBar(); sectionItem()->invalidateBar();
@@ -904,7 +929,9 @@ void TimelineBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
const auto p = event->pos(); const auto p = event->pos();
QRectF left, right; 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 (left.contains(p) || right.contains(p)) {
if (cursor().shape() != Qt::SizeHorCursor) if (cursor().shape() != Qt::SizeHorCursor)
setCursor(QCursor(Qt::SizeHorCursor)); setCursor(QCursor(Qt::SizeHorCursor));
@@ -920,6 +947,9 @@ void TimelineBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
void TimelineBarItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) void TimelineBarItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
{ {
if (isLocked())
return;
QMenu menu; QMenu menu;
QAction* overrideColor = menu.addAction(tr("Override Color")); QAction* overrideColor = menu.addAction(tr("Override Color"));

View File

@@ -50,6 +50,8 @@ public:
void itemMoved(const QPointF &start, const QPointF &end) override; void itemMoved(const QPointF &start, const QPointF &end) override;
void commitPosition(const QPointF &point) override; void commitPosition(const QPointF &point) override;
bool isLocked() const override;
protected: protected:
void scrollOffsetChanged() override; void scrollOffsetChanged() override;
void paint(QPainter *painter, void paint(QPainter *painter,
@@ -100,6 +102,7 @@ public:
static void updateData(QGraphicsItem *item); static void updateData(QGraphicsItem *item);
static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b); static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b);
static void updateFramesForTarget(QGraphicsItem *item, const ModelNode &target); static void updateFramesForTarget(QGraphicsItem *item, const ModelNode &target);
static void updateHeightForTarget(QGraphicsItem *item, const ModelNode &target);
void moveAllFrames(qreal offset); void moveAllFrames(qreal offset);
void scaleAllFrames(qreal scale); void scaleAllFrames(qreal scale);
@@ -121,6 +124,7 @@ protected:
private: private:
void updateData(); void updateData();
void updateFrames(); void updateFrames();
void updateHeight();
void invalidateHeight(); void invalidateHeight();
void invalidateProperties(); void invalidateProperties();
void invalidateFrames(); void invalidateFrames();

View File

@@ -236,6 +236,18 @@ void TimelineView::selectedNodesChanged(const QList<ModelNode> & /*selectedNodeL
m_timelineWidget->graphicsScene()->update(); 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) void TimelineView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
{ {
for (const auto &property : propertyList) { for (const auto &property : propertyList) {

View File

@@ -62,6 +62,9 @@ public:
PropertyChangeFlags propertyChange) override; PropertyChangeFlags propertyChange) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, 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 propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override; void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
void propertiesRemoved(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(); const QList<QGraphicsItem *> items = m_layout->childItems();
for (auto child : items) for (auto child : items)
TimelineSectionItem::updateDataForTarget(child, target, &found); TransitionEditorSectionItem::updateDataForTarget(child, target, &found);
if (!found) if (!found)
invalidateScene(); invalidateScene();
@@ -227,6 +227,18 @@ void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &
invalidateLayout(); 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() void TransitionEditorGraphicsScene::invalidateScene()
{ {
invalidateScrollbar(); invalidateScrollbar();

View File

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

View File

@@ -196,6 +196,17 @@ void TransitionEditorSectionItem::updateData(QGraphicsItem *item)
sectionItem->updateData(); 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) void TransitionEditorSectionItem::invalidateBar(QGraphicsItem *item)
{ {
if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item)) if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item))
@@ -360,7 +371,8 @@ void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
event->accept(); event->accept();
toggleCollapsed(); if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
toggleCollapsed();
} }
} }
@@ -392,7 +404,8 @@ void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *ev
if (m_targetNode.isValid()) if (m_targetNode.isValid())
m_targetNode.view()->setSelectedModelNode(m_targetNode); m_targetNode.view()->setSelectedModelNode(m_targetNode);
} else { } else {
toggleCollapsed(); if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
toggleCollapsed();
} }
update(); update();
} }
@@ -417,6 +430,12 @@ void TransitionEditorSectionItem::updateData()
update(); update();
} }
void TransitionEditorSectionItem::updateHeight()
{
invalidateHeight();
update();
}
const QList<QGraphicsItem *> TransitionEditorSectionItem::propertyItems() const const QList<QGraphicsItem *> TransitionEditorSectionItem::propertyItems() const
{ {
QList<QGraphicsItem *> list; QList<QGraphicsItem *> list;
@@ -488,7 +507,8 @@ void TransitionEditorSectionItem::invalidateProperties()
bool TransitionEditorSectionItem::collapsed() const 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 qreal TransitionEditorSectionItem::rulerWidth() const
@@ -501,9 +521,9 @@ void TransitionEditorSectionItem::toggleCollapsed()
QTC_ASSERT(m_targetNode.isValid(), return ); QTC_ASSERT(m_targetNode.isValid(), return );
if (collapsed()) if (collapsed())
m_targetNode.setAuxiliaryData("timeline_expanded", true); m_targetNode.setAuxiliaryData("transition_expanded", true);
else else
m_targetNode.removeAuxiliaryData("timeline_expanded"); m_targetNode.removeAuxiliaryData("transition_expanded");
invalidateHeight(); invalidateHeight();
} }
@@ -592,6 +612,11 @@ void TransitionEditorBarItem::commitPosition(const QPointF & /*point*/)
scrollOffsetChanged(); scrollOffsetChanged();
} }
bool TransitionEditorBarItem::isLocked() const
{
return sectionItem() && sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked();
}
void TransitionEditorBarItem::scrollOffsetChanged() void TransitionEditorBarItem::scrollOffsetChanged()
{ {
if (sectionItem()) if (sectionItem())
@@ -637,7 +662,9 @@ void TransitionEditorBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
const auto p = event->pos(); const auto p = event->pos();
QRectF left, right; 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 (left.contains(p) || right.contains(p)) {
if (cursor().shape() != Qt::SizeHorCursor) if (cursor().shape() != Qt::SizeHorCursor)
setCursor(QCursor(Qt::SizeHorCursor)); setCursor(QCursor(Qt::SizeHorCursor));

View File

@@ -54,6 +54,8 @@ public:
void itemMoved(const QPointF &start, const QPointF &end) override; void itemMoved(const QPointF &start, const QPointF &end) override;
void commitPosition(const QPointF &point) override; void commitPosition(const QPointF &point) override;
bool isLocked() const override;
protected: protected:
void scrollOffsetChanged() override; void scrollOffsetChanged() override;
void paint(QPainter *painter, void paint(QPainter *painter,
@@ -106,6 +108,7 @@ public:
static void updateData(QGraphicsItem *item); static void updateData(QGraphicsItem *item);
static void invalidateBar(QGraphicsItem *item); static void invalidateBar(QGraphicsItem *item);
static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b); static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b);
static void updateHeightForTarget(QGraphicsItem *item, const ModelNode &target);
void moveAllDurations(qreal offset); void moveAllDurations(qreal offset);
void scaleAllDurations(qreal scale); void scaleAllDurations(qreal scale);
@@ -125,6 +128,7 @@ protected:
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
private: private:
void updateHeight();
void invalidateHeight(); void invalidateHeight();
void invalidateProperties(); void invalidateProperties();
bool collapsed() const; 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( void TransitionEditorView::propertiesAboutToBeRemoved(
const QList<AbstractProperty> & /*propertyList */) const QList<AbstractProperty> & /*propertyList */)
{ {
@@ -217,7 +229,7 @@ ModelNode TransitionEditorView::addNewTransition()
QStringList newlist = idPropertyList.value(targetId); QStringList newlist = idPropertyList.value(targetId);
for (const QString &str :locList) for (const QString &str :locList)
if (!newlist.contains(str)) if (!newlist.contains(str))
newlist.append(str); newlist.append(str);
idPropertyList.insert(targetId, newlist); idPropertyList.insert(targetId, newlist);
} else { } else {
if (!locList.isEmpty()) if (!locList.isEmpty())

View File

@@ -60,6 +60,9 @@ public:
PropertyChangeFlags propertyChange) override; PropertyChangeFlags propertyChange) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList, 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 propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override; void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
void propertiesRemoved(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 setSelectedModelNodes(const QList<ModelNode> &selectedNodeList);
void setSelectedModelNode(const ModelNode &modelNode); void setSelectedModelNode(const ModelNode &modelNode);
void selectModelNode(const ModelNode &node); void selectModelNode(const ModelNode &node);
void deselectModelNode(const ModelNode &node); void deselectModelNode(const ModelNode &node);
void clearSelectedModelNodes(); void clearSelectedModelNodes();

View File

@@ -65,7 +65,9 @@ QMLDESIGNERCORE_EXPORT QList<Internal::InternalNodePointer> toInternalNodeList(c
using PropertyListType = QList<QPair<PropertyName, QVariant> >; 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);
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 setGlobalStatus(const GlobalAnnotationStatus &status);
void removeGlobalStatus(); void removeGlobalStatus();
bool locked() const;
void setLocked(bool value);
static bool isThisOrAncestorLocked(const ModelNode &node);
qint32 internalId() const; qint32 internalId() const;
void setNodeSource(const QString&); void setNodeSource(const QString&);
@@ -241,6 +248,8 @@ public:
private: // functions private: // functions
Internal::InternalNodePointer internalNode() const; Internal::InternalNodePointer internalNode() const;
void removeLocked();
bool hasLocked() const;
private: // variables private: // variables
Internal::InternalNodePointer m_internalNode; Internal::InternalNodePointer m_internalNode;

View File

@@ -534,11 +534,11 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
const PropertyName &name, const PropertyName &name,
const QVariant &value) 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"))) { || name.endsWith(PropertyName("@NodeInstance"))) {
if (hasInstanceForModelNode(node)) { if (hasInstanceForModelNode(node)) {
NodeInstance instance = instanceForModelNode(node); NodeInstance instance = instanceForModelNode(node);
if (value.isValid() || name == "invisible") { if (value.isValid() || name == "invisible" || name == "locked") {
PropertyValueContainer container{instance.instanceId(), name, value, TypeName()}; PropertyValueContainer container{instance.instanceId(), name, value, TypeName()};
m_nodeInstanceServer->changeAuxiliaryValues({{container}}); m_nodeInstanceServer->changeAuxiliaryValues({{container}});
} else { } else {

View File

@@ -35,6 +35,7 @@
#ifndef QMLDESIGNER_TEST #ifndef QMLDESIGNER_TEST
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <viewmanager.h> #include <viewmanager.h>
#include <nodeabstractproperty.h>
#endif #endif
#include <coreplugin/helpmanager.h> #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> toModelNodeList(const QList<Internal::InternalNode::Pointer> &nodeList, AbstractView *view)
{ {
QList<ModelNode> newNodeList; QList<ModelNode> newNodeList;
foreach (const Internal::InternalNode::Pointer &node, nodeList) for (const Internal::InternalNode::Pointer &node : nodeList)
newNodeList.append(ModelNode(node, view->model(), view)); newNodeList.append(ModelNode(node, view->model(), view));
return newNodeList; 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> toInternalNodeList(const QList<ModelNode> &nodeList)
{ {
QList<Internal::InternalNode::Pointer> newNodeList; QList<Internal::InternalNode::Pointer> newNodeList;
foreach (const ModelNode &node, nodeList) for (const ModelNode &node : nodeList)
newNodeList.append(node.internalNode()); newNodeList.append(node.internalNode());
return newNodeList; 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 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) 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) void AbstractView::setSelectedModelNode(const ModelNode &modelNode)
{ {
if (ModelNode::isThisOrAncestorLocked(modelNode)) {
clearSelectedModelNodes();
return;
}
setSelectedModelNodes({modelNode}); 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) void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
{ {
model()->d->setScriptFunctions(internalNode(), scriptFunctionList); model()->d->setScriptFunctions(internalNode(), scriptFunctionList);