2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2011-02-18 11:47:15 +01:00
|
|
|
|
2014-08-20 01:47:42 +02:00
|
|
|
#include "qmljseditor.h"
|
2022-08-25 12:46:47 +02:00
|
|
|
#include "qmljseditortr.h"
|
|
|
|
|
#include "qmljsoutline.h"
|
2010-08-10 11:59:02 +02:00
|
|
|
#include "qmljsoutlinetreeview.h"
|
2022-08-25 12:46:47 +02:00
|
|
|
#include "qmloutlinemodel.h"
|
2010-07-08 11:32:45 +02:00
|
|
|
|
2014-07-31 18:15:19 +02:00
|
|
|
#include <coreplugin/find/itemviewfind.h>
|
2010-07-16 11:04:20 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2012-02-14 16:43:51 +01:00
|
|
|
#include <coreplugin/idocument.h>
|
2010-07-14 12:21:23 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2010-07-16 11:04:20 +02:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QAction>
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
#include <QTextBlock>
|
2010-07-08 11:32:45 +02:00
|
|
|
|
|
|
|
|
using namespace QmlJS;
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
debug = false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
namespace QmlJSEditor {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
QmlJSOutlineFilterModel::QmlJSOutlineFilterModel(QObject *parent) :
|
|
|
|
|
QSortFilterProxyModel(parent)
|
|
|
|
|
{
|
2010-07-21 14:48:54 +02:00
|
|
|
setDynamicSortFilter(true);
|
2010-07-14 16:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-27 10:09:35 +02:00
|
|
|
Qt::ItemFlags QmlJSOutlineFilterModel::flags(const QModelIndex &index) const
|
|
|
|
|
{
|
|
|
|
|
Qt::ItemFlags f = sourceModel()->flags(index);
|
|
|
|
|
if (m_sorted)
|
|
|
|
|
f.setFlag(Qt::ItemIsDropEnabled, false);
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
bool QmlJSOutlineFilterModel::filterAcceptsRow(int sourceRow,
|
|
|
|
|
const QModelIndex &sourceParent) const
|
|
|
|
|
{
|
|
|
|
|
if (m_filterBindings) {
|
|
|
|
|
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
|
2023-11-06 15:51:31 +01:00
|
|
|
while (sourceIndex.isValid()) {
|
|
|
|
|
if (sourceIndex.data(QmlOutlineModel::ItemTypeRole)
|
|
|
|
|
== QmlOutlineModel::NonElementBindingType) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
sourceIndex = sourceIndex.parent();
|
|
|
|
|
}
|
2010-07-14 16:46:54 +02:00
|
|
|
}
|
|
|
|
|
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-27 10:09:35 +02:00
|
|
|
bool QmlJSOutlineFilterModel::lessThan(const QModelIndex &sourceLeft,
|
|
|
|
|
const QModelIndex &sourceRight) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_sorted)
|
|
|
|
|
return sourceLeft.row() > sourceRight.row();
|
|
|
|
|
|
|
|
|
|
return sourceLeft.data().toString() > sourceRight.data().toString();
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-12 14:37:52 +02:00
|
|
|
QVariant QmlJSOutlineFilterModel::data(const QModelIndex &index, int role) const
|
|
|
|
|
{
|
|
|
|
|
if (role == QmlOutlineModel::AnnotationRole) {
|
|
|
|
|
// Don't show element id etc behind element if the property is also visible
|
|
|
|
|
if (!filterBindings()
|
|
|
|
|
&& index.data(QmlOutlineModel::ItemTypeRole) == QmlOutlineModel::ElementType) {
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QSortFilterProxyModel::data(index, role);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-29 14:41:42 +02:00
|
|
|
Qt::DropActions QmlJSOutlineFilterModel::supportedDragActions() const
|
|
|
|
|
{
|
|
|
|
|
return sourceModel()->supportedDragActions();
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
bool QmlJSOutlineFilterModel::filterBindings() const
|
|
|
|
|
{
|
|
|
|
|
return m_filterBindings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlJSOutlineFilterModel::setFilterBindings(bool filterBindings)
|
|
|
|
|
{
|
2010-07-16 11:04:20 +02:00
|
|
|
m_filterBindings = filterBindings;
|
|
|
|
|
invalidateFilter();
|
2010-07-14 16:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-27 10:09:35 +02:00
|
|
|
void QmlJSOutlineFilterModel::setSorted(bool sorted)
|
|
|
|
|
{
|
|
|
|
|
m_sorted = sorted;
|
|
|
|
|
invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-31 12:19:48 +02:00
|
|
|
QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent)
|
|
|
|
|
: TextEditor::IOutlineWidget(parent)
|
|
|
|
|
, m_treeView(new QmlJSOutlineTreeView(this))
|
|
|
|
|
, m_filterModel(new QmlJSOutlineFilterModel(this))
|
2010-07-08 11:32:45 +02:00
|
|
|
{
|
2010-07-16 11:04:20 +02:00
|
|
|
m_filterModel->setFilterBindings(false);
|
|
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
m_treeView->setModel(m_filterModel);
|
2020-05-27 10:09:35 +02:00
|
|
|
m_treeView->setSortingEnabled(true);
|
|
|
|
|
|
2014-04-09 17:04:50 +02:00
|
|
|
setFocusProxy(m_treeView);
|
2010-07-14 16:46:54 +02:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
auto layout = new QVBoxLayout;
|
2010-07-08 11:32:45 +02:00
|
|
|
|
2019-08-29 10:36:01 +02:00
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
2010-07-08 11:32:45 +02:00
|
|
|
layout->setSpacing(0);
|
2014-07-31 18:15:19 +02:00
|
|
|
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_treeView));
|
2010-07-08 11:32:45 +02:00
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
m_showBindingsAction = new QAction(this);
|
2022-08-25 12:46:47 +02:00
|
|
|
m_showBindingsAction->setText(Tr::tr("Show All Bindings"));
|
2010-07-14 16:46:54 +02:00
|
|
|
m_showBindingsAction->setCheckable(true);
|
|
|
|
|
m_showBindingsAction->setChecked(true);
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_showBindingsAction, &QAction::toggled, this, &QmlJSOutlineWidget::setShowBindings);
|
2010-07-14 16:46:54 +02:00
|
|
|
|
2010-07-08 11:32:45 +02:00
|
|
|
setLayout(layout);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSOutlineWidget::setEditor(QmlJSEditorWidget *editor)
|
2010-07-08 11:32:45 +02:00
|
|
|
{
|
|
|
|
|
m_editor = editor;
|
|
|
|
|
|
2014-02-07 13:45:51 +01:00
|
|
|
m_filterModel->setSourceModel(m_editor->qmlJsEditorDocument()->outlineModel());
|
2017-04-06 17:20:00 +02:00
|
|
|
m_treeView->expandAll();
|
2017-07-10 19:10:37 +02:00
|
|
|
connect(m_editor->qmlJsEditorDocument()->outlineModel(), &QAbstractItemModel::modelAboutToBeReset, m_treeView, [this]() {
|
|
|
|
|
if (m_treeView->selectionModel())
|
2017-06-29 17:48:19 +02:00
|
|
|
m_treeView->selectionModel()->blockSignals(true);
|
|
|
|
|
});
|
2017-07-10 19:10:37 +02:00
|
|
|
connect(m_editor->qmlJsEditorDocument()->outlineModel(), &QAbstractItemModel::modelReset, m_treeView, [this]() {
|
|
|
|
|
if (m_treeView->selectionModel())
|
2017-06-29 17:48:19 +02:00
|
|
|
m_treeView->selectionModel()->blockSignals(false);
|
|
|
|
|
});
|
2010-07-14 13:01:39 +02:00
|
|
|
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
|
|
|
this, &QmlJSOutlineWidget::updateSelectionInText);
|
2010-07-08 11:32:45 +02:00
|
|
|
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_treeView, &QAbstractItemView::activated,
|
|
|
|
|
this, &QmlJSOutlineWidget::focusEditor);
|
2010-12-20 09:44:54 +01:00
|
|
|
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_editor, &QmlJSEditorWidget::outlineModelIndexChanged,
|
|
|
|
|
this, &QmlJSOutlineWidget::updateSelectionInTree);
|
2023-09-19 17:37:06 +02:00
|
|
|
connect(m_editor->qmlJsEditorDocument()->outlineModel(),
|
|
|
|
|
&QmlOutlineModel::updated,
|
|
|
|
|
this,
|
|
|
|
|
[treeView = QPointer(m_treeView), editor = QPointer(m_editor)]() {
|
|
|
|
|
if (treeView)
|
|
|
|
|
treeView->expandAll();
|
|
|
|
|
if (editor)
|
|
|
|
|
editor->updateOutlineIndexNow();
|
|
|
|
|
});
|
2010-07-08 11:32:45 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
QList<QAction*> QmlJSOutlineWidget::filterMenuActions() const
|
|
|
|
|
{
|
2017-04-06 17:17:09 +02:00
|
|
|
return {m_showBindingsAction};
|
2010-07-14 16:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-08 11:32:45 +02:00
|
|
|
void QmlJSOutlineWidget::setCursorSynchronization(bool syncWithCursor)
|
|
|
|
|
{
|
|
|
|
|
m_enableCursorSync = syncWithCursor;
|
2017-04-06 17:22:52 +02:00
|
|
|
m_editor->updateOutlineIndexNow();
|
2010-07-08 11:32:45 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-27 10:09:35 +02:00
|
|
|
void QmlJSOutlineWidget::setSorted(bool sorted)
|
|
|
|
|
{
|
|
|
|
|
m_sorted = sorted;
|
|
|
|
|
m_filterModel->setSorted(m_sorted);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-09 11:12:17 +01:00
|
|
|
void QmlJSOutlineWidget::restoreSettings(const QVariantMap &map)
|
2010-07-16 11:04:20 +02:00
|
|
|
{
|
2014-12-09 11:12:17 +01:00
|
|
|
bool showBindings = map.value(QString::fromLatin1("QmlJSOutline.ShowBindings"), true).toBool();
|
2010-07-16 11:04:20 +02:00
|
|
|
m_showBindingsAction->setChecked(showBindings);
|
2020-05-27 10:09:35 +02:00
|
|
|
setSorted(map.value(QString("QmlJSOutline.Sort"), false).toBool());
|
2010-07-16 11:04:20 +02:00
|
|
|
}
|
|
|
|
|
|
2014-12-09 11:12:17 +01:00
|
|
|
QVariantMap QmlJSOutlineWidget::settings() const
|
2010-07-16 11:04:20 +02:00
|
|
|
{
|
2020-05-27 10:09:35 +02:00
|
|
|
return {
|
|
|
|
|
{QString("QmlJSOutline.ShowBindings"), m_showBindingsAction->isChecked()},
|
|
|
|
|
{QString("QmlJSOutline.Sort"), m_sorted}
|
|
|
|
|
};
|
2010-07-16 11:04:20 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
void QmlJSOutlineWidget::updateSelectionInTree(const QModelIndex &index)
|
2010-07-08 11:32:45 +02:00
|
|
|
{
|
|
|
|
|
if (!syncCursor())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_blockCursorSync = true;
|
2010-09-03 12:40:39 +02:00
|
|
|
|
|
|
|
|
QModelIndex baseIndex = index;
|
|
|
|
|
QModelIndex filterIndex = m_filterModel->mapFromSource(baseIndex);
|
|
|
|
|
while (baseIndex.isValid() && !filterIndex.isValid()) { // Search for ancestor index actually shown
|
|
|
|
|
baseIndex = baseIndex.parent();
|
|
|
|
|
filterIndex = m_filterModel->mapFromSource(baseIndex);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-09 17:04:50 +02:00
|
|
|
m_treeView->setCurrentIndex(filterIndex);
|
2010-09-03 12:40:39 +02:00
|
|
|
m_treeView->scrollTo(filterIndex);
|
2010-07-08 11:32:45 +02:00
|
|
|
m_blockCursorSync = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlJSOutlineWidget::updateSelectionInText(const QItemSelection &selection)
|
|
|
|
|
{
|
|
|
|
|
if (!syncCursor())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!selection.indexes().isEmpty()) {
|
|
|
|
|
QModelIndex index = selection.indexes().first();
|
2010-08-25 13:21:38 +02:00
|
|
|
|
2010-12-20 09:44:54 +01:00
|
|
|
updateTextCursor(index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlJSOutlineWidget::updateTextCursor(const QModelIndex &index)
|
|
|
|
|
{
|
2017-06-29 17:48:19 +02:00
|
|
|
const auto update = [this](const QModelIndex &index) {
|
|
|
|
|
if (!m_editor->isOutlineCursorChangesBlocked()) {
|
|
|
|
|
QModelIndex sourceIndex = m_filterModel->mapToSource(index);
|
|
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
SourceLocation location
|
2017-06-29 17:48:19 +02:00
|
|
|
= m_editor->qmlJsEditorDocument()->outlineModel()->sourceLocation(sourceIndex);
|
|
|
|
|
|
|
|
|
|
if (!location.isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const QTextBlock lastBlock = m_editor->document()->lastBlock();
|
|
|
|
|
const uint textLength = lastBlock.position() + lastBlock.length();
|
|
|
|
|
if (location.offset >= textLength)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Core::EditorManager::cutForwardNavigationHistory();
|
|
|
|
|
Core::EditorManager::addCurrentPositionToNavigationHistory();
|
|
|
|
|
|
|
|
|
|
QTextCursor textCursor = m_editor->textCursor();
|
|
|
|
|
|
|
|
|
|
textCursor.setPosition(location.offset);
|
|
|
|
|
m_editor->setTextCursor(textCursor);
|
|
|
|
|
m_editor->centerCursor();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
m_blockCursorSync = true;
|
|
|
|
|
update(index);
|
|
|
|
|
m_blockCursorSync = false;
|
2010-07-08 11:32:45 +02:00
|
|
|
}
|
|
|
|
|
|
2014-04-09 17:04:50 +02:00
|
|
|
void QmlJSOutlineWidget::focusEditor()
|
|
|
|
|
{
|
|
|
|
|
m_editor->setFocus();
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-14 16:46:54 +02:00
|
|
|
void QmlJSOutlineWidget::setShowBindings(bool showBindings)
|
|
|
|
|
{
|
|
|
|
|
m_filterModel->setFilterBindings(!showBindings);
|
2017-04-06 17:20:00 +02:00
|
|
|
m_treeView->expandAll();
|
2017-04-06 17:22:52 +02:00
|
|
|
m_editor->updateOutlineIndexNow();
|
2010-07-14 16:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2010-07-08 11:32:45 +02:00
|
|
|
bool QmlJSOutlineWidget::syncCursor()
|
|
|
|
|
{
|
|
|
|
|
return m_enableCursorSync && !m_blockCursorSync;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QmlJSOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) const
|
|
|
|
|
{
|
2013-06-03 19:31:32 +02:00
|
|
|
if (qobject_cast<QmlJSEditor*>(editor))
|
2010-07-08 11:32:45 +02:00
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextEditor::IOutlineWidget *QmlJSOutlineWidgetFactory::createWidget(Core::IEditor *editor)
|
|
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
auto widget = new QmlJSOutlineWidget;
|
2010-07-08 11:32:45 +02:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
auto qmlJSEditable = qobject_cast<const QmlJSEditor*>(editor);
|
|
|
|
|
auto qmlJSEditor = qobject_cast<QmlJSEditorWidget*>(qmlJSEditable->widget());
|
2010-07-08 11:32:45 +02:00
|
|
|
Q_ASSERT(qmlJSEditor);
|
|
|
|
|
|
|
|
|
|
widget->setEditor(qmlJSEditor);
|
|
|
|
|
|
|
|
|
|
return widget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace QmlJSEditor
|