forked from qt-creator/qt-creator
QmlJSEditor: Use QmlOutline model also in combo box
The drop down combo box and the Outline in the sidebar now share the same model.
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include "qmljshighlighter.h"
|
||||
#include "qmljseditorplugin.h"
|
||||
#include "qmljsmodelmanager.h"
|
||||
#include "qmloutlinemodel.h"
|
||||
|
||||
#include <qmljs/qmljsindenter.h>
|
||||
#include <qmljs/qmljsinterpreter.h>
|
||||
@@ -70,9 +71,11 @@
|
||||
|
||||
#include <QtGui/QMenu>
|
||||
#include <QtGui/QComboBox>
|
||||
#include <QtGui/QHeaderView>
|
||||
#include <QtGui/QInputDialog>
|
||||
#include <QtGui/QMainWindow>
|
||||
#include <QtGui/QToolBar>
|
||||
#include <QtGui/QTreeView>
|
||||
|
||||
enum {
|
||||
UPDATE_DOCUMENT_DEFAULT_INTERVAL = 50,
|
||||
@@ -612,6 +615,7 @@ QString QmlJSEditorEditable::preferredMode() const
|
||||
QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
|
||||
TextEditor::BaseTextEditor(parent),
|
||||
m_methodCombo(0),
|
||||
m_outlineModel(new QmlOutlineModel(this)),
|
||||
m_modelManager(0),
|
||||
m_contextPane(0)
|
||||
{
|
||||
@@ -688,6 +692,16 @@ bool QmlJSTextEditor::isOutdated() const
|
||||
return false;
|
||||
}
|
||||
|
||||
QmlOutlineModel *QmlJSTextEditor::outlineModel() const
|
||||
{
|
||||
return m_outlineModel;
|
||||
}
|
||||
|
||||
QModelIndex QmlJSTextEditor::outlineModelIndex() const
|
||||
{
|
||||
return m_outlineModelIndex;
|
||||
}
|
||||
|
||||
Core::IEditor *QmlJSEditorEditable::duplicate(QWidget *parent)
|
||||
{
|
||||
QmlJSTextEditor *newEditor = new QmlJSTextEditor(parent);
|
||||
@@ -794,33 +808,37 @@ void QmlJSTextEditor::modificationChanged(bool changed)
|
||||
m_modelManager->fileChangedOnDisk(file()->fileName());
|
||||
}
|
||||
|
||||
void QmlJSTextEditor::jumpToMethod(int index)
|
||||
void QmlJSTextEditor::jumpToMethod(int /*index*/)
|
||||
{
|
||||
if (index > 0 && index <= m_semanticInfo.declarations.size()) { // indexes are 1-based
|
||||
Declaration d = m_semanticInfo.declarations.at(index - 1);
|
||||
gotoLine(d.startLine, d.startColumn - 1);
|
||||
setFocus();
|
||||
}
|
||||
QModelIndex index = m_methodCombo->view()->currentIndex();
|
||||
AST::SourceLocation location = index.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
|
||||
|
||||
QTextCursor cursor = textCursor();
|
||||
cursor.setPosition(location.offset);
|
||||
setTextCursor(cursor);
|
||||
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void QmlJSTextEditor::updateMethodBoxIndex()
|
||||
{
|
||||
int line = 0, column = 0;
|
||||
convertPosition(position(), &line, &column);
|
||||
m_outlineModelIndex = indexForPosition(position());
|
||||
emit outlineModelIndexChanged(m_outlineModelIndex);
|
||||
|
||||
int currentSymbolIndex = 0;
|
||||
QModelIndex comboIndex = m_outlineModelIndex;
|
||||
|
||||
int index = 0;
|
||||
while (index < m_semanticInfo.declarations.size()) {
|
||||
const Declaration &d = m_semanticInfo.declarations.at(index++);
|
||||
if (comboIndex.isValid()) {
|
||||
bool blocked = m_methodCombo->blockSignals(true);
|
||||
|
||||
if (line < d.startLine)
|
||||
break;
|
||||
else
|
||||
currentSymbolIndex = index;
|
||||
// There is no direct way to select a non-root item
|
||||
m_methodCombo->setRootModelIndex(comboIndex.parent());
|
||||
m_methodCombo->setCurrentIndex(comboIndex.row());
|
||||
m_methodCombo->setRootModelIndex(QModelIndex());
|
||||
|
||||
updateMethodBoxToolTip();
|
||||
m_methodCombo->blockSignals(blocked);
|
||||
}
|
||||
|
||||
m_methodCombo->setCurrentIndex(currentSymbolIndex);
|
||||
updateUses();
|
||||
}
|
||||
|
||||
@@ -983,6 +1001,14 @@ void QmlJSTextEditor::createToolBar(QmlJSEditorEditable *editable)
|
||||
{
|
||||
m_methodCombo = new QComboBox;
|
||||
m_methodCombo->setMinimumContentsLength(22);
|
||||
m_methodCombo->setModel(m_outlineModel);
|
||||
|
||||
QTreeView *treeView = new QTreeView;
|
||||
treeView->header()->hide();
|
||||
treeView->setItemsExpandable(false);
|
||||
m_methodCombo->setView(treeView);
|
||||
treeView->expandAll();
|
||||
|
||||
//m_methodCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
|
||||
// Make the combo box prefer to expand
|
||||
@@ -1332,15 +1358,14 @@ void QmlJSTextEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
|
||||
FindDeclarations findDeclarations;
|
||||
m_semanticInfo.declarations = findDeclarations(doc->ast());
|
||||
|
||||
QStringList items;
|
||||
items.append(tr("<Select Symbol>"));
|
||||
|
||||
foreach (Declaration decl, m_semanticInfo.declarations)
|
||||
items.append(decl.text);
|
||||
|
||||
m_methodCombo->clear();
|
||||
m_methodCombo->addItems(items);
|
||||
m_outlineModel->update(doc);
|
||||
updateMethodBoxIndex();
|
||||
|
||||
QTreeView *treeView = static_cast<QTreeView*>(m_methodCombo->view());
|
||||
treeView->expandAll();
|
||||
// ComboBox only let's you select top level indexes for a QAbstractItemModel!
|
||||
// therefore we've to fake a treeview by listview + indentation
|
||||
|
||||
if (m_contextPane) {
|
||||
Node *newNode = m_semanticInfo.declaringMember(position());
|
||||
if (newNode) {
|
||||
@@ -1355,8 +1380,6 @@ void QmlJSTextEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
|
||||
appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
|
||||
appendExtraSelectionsForMessages(&selections, m_semanticInfo.semanticMessages, document());
|
||||
setExtraSelections(CodeWarningsSelection, selections);
|
||||
|
||||
emit semanticInfoUpdated(semanticInfo);
|
||||
}
|
||||
|
||||
void QmlJSTextEditor::onCursorPositionChanged()
|
||||
@@ -1372,6 +1395,30 @@ void QmlJSTextEditor::onCursorPositionChanged()
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex QmlJSTextEditor::indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex) const
|
||||
{
|
||||
QModelIndex lastIndex = rootIndex;
|
||||
|
||||
|
||||
const int rowCount = m_outlineModel->rowCount(rootIndex);
|
||||
for (int i = 0; i < rowCount; ++i) {
|
||||
QModelIndex childIndex = m_outlineModel->index(i, 0, rootIndex);
|
||||
AST::SourceLocation location = childIndex.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
|
||||
|
||||
if ((cursorPosition >= location.offset)
|
||||
&& (cursorPosition <= location.offset + location.length)) {
|
||||
lastIndex = childIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastIndex != rootIndex) {
|
||||
// recurse
|
||||
lastIndex = indexForPosition(cursorPosition, lastIndex);
|
||||
}
|
||||
return lastIndex;
|
||||
}
|
||||
|
||||
SemanticHighlighter::Source QmlJSTextEditor::currentSource(bool force)
|
||||
{
|
||||
int line = 0, column = 0;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <texteditor/basetexteditor.h>
|
||||
|
||||
#include <QtCore/QWaitCondition>
|
||||
#include <QtCore/QModelIndex>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
@@ -58,6 +59,7 @@ class Highlighter;
|
||||
namespace Internal {
|
||||
|
||||
class QmlJSTextEditor;
|
||||
class QmlOutlineModel;
|
||||
|
||||
class QmlJSEditorEditable : public TextEditor::BaseTextEditorEditable
|
||||
{
|
||||
@@ -214,12 +216,15 @@ public:
|
||||
int documentRevision() const;
|
||||
bool isOutdated() const;
|
||||
|
||||
QmlOutlineModel *outlineModel() const;
|
||||
QModelIndex outlineModelIndex() const;
|
||||
|
||||
public slots:
|
||||
void followSymbolUnderCursor();
|
||||
virtual void setFontSettings(const TextEditor::FontSettings &);
|
||||
|
||||
signals:
|
||||
void semanticInfoUpdated(const QmlJSEditor::Internal::SemanticInfo &semanticInfo);
|
||||
void outlineModelIndexChanged(const QModelIndex &index);
|
||||
|
||||
private slots:
|
||||
void onDocumentUpdated(QmlJS::Document::Ptr doc);
|
||||
@@ -266,6 +271,7 @@ private:
|
||||
QString wordUnderCursor() const;
|
||||
|
||||
SemanticHighlighter::Source currentSource(bool force = false);
|
||||
QModelIndex indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex = QModelIndex()) const;
|
||||
|
||||
const Core::Context m_context;
|
||||
|
||||
@@ -273,6 +279,8 @@ private:
|
||||
QTimer *m_updateUsesTimer;
|
||||
QTimer *m_semanticRehighlightTimer;
|
||||
QComboBox *m_methodCombo;
|
||||
QmlOutlineModel *m_outlineModel;
|
||||
QModelIndex m_outlineModelIndex;
|
||||
QmlJS::ModelManagerInterface *m_modelManager;
|
||||
QTextCharFormat m_occurrencesFormat;
|
||||
QTextCharFormat m_occurrencesUnusedFormat;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <coreplugin/ifile.h>
|
||||
#include <QtGui/QVBoxLayout>
|
||||
|
||||
#include <QDebug>
|
||||
using namespace QmlJS;
|
||||
|
||||
enum {
|
||||
@@ -29,8 +30,7 @@ QmlJSOutlineTreeView::QmlJSOutlineTreeView(QWidget *parent) :
|
||||
|
||||
QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent) :
|
||||
TextEditor::IOutlineWidget(parent),
|
||||
m_treeView(new QmlJSOutlineTreeView()),
|
||||
m_model(new QmlOutlineModel),
|
||||
m_treeView(new QmlJSOutlineTreeView(this)),
|
||||
m_enableCursorSync(true),
|
||||
m_blockCursorSync(false)
|
||||
{
|
||||
@@ -41,91 +41,40 @@ QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent) :
|
||||
layout->addWidget(m_treeView);
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
m_treeView->setModel(m_model);
|
||||
|
||||
connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||
this, SLOT(updateSelectionInText(QItemSelection)));
|
||||
}
|
||||
|
||||
void QmlJSOutlineWidget::setEditor(QmlJSTextEditor *editor)
|
||||
{
|
||||
m_editor = editor;
|
||||
|
||||
connect(m_editor.data(), SIGNAL(semanticInfoUpdated(QmlJSEditor::Internal::SemanticInfo)),
|
||||
this, SLOT(updateOutline(QmlJSEditor::Internal::SemanticInfo)));
|
||||
connect(m_editor.data(), SIGNAL(cursorPositionChanged()),
|
||||
this, SLOT(updateSelectionInTree()));
|
||||
m_treeView->setModel(m_editor.data()->outlineModel());
|
||||
connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||
this, SLOT(updateSelectionInText(QItemSelection)));
|
||||
|
||||
updateOutline(m_editor.data()->semanticInfo());
|
||||
connect(m_editor.data(), SIGNAL(outlineModelIndexChanged(QModelIndex)),
|
||||
this, SLOT(updateSelectionInTree(QModelIndex)));
|
||||
connect(m_editor.data()->outlineModel(), SIGNAL(updated()),
|
||||
this, SLOT(modelUpdated()));
|
||||
}
|
||||
|
||||
void QmlJSOutlineWidget::setCursorSynchronization(bool syncWithCursor)
|
||||
{
|
||||
m_enableCursorSync = syncWithCursor;
|
||||
if (m_enableCursorSync)
|
||||
updateSelectionInTree();
|
||||
updateSelectionInTree(m_editor.data()->outlineModelIndex());
|
||||
}
|
||||
|
||||
void QmlJSOutlineWidget::updateOutline(const QmlJSEditor::Internal::SemanticInfo &semanticInfo)
|
||||
void QmlJSOutlineWidget::modelUpdated()
|
||||
{
|
||||
Document::Ptr doc = semanticInfo.document;
|
||||
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_editor
|
||||
|| m_editor.data()->file()->fileName() != doc->fileName()
|
||||
|| m_editor.data()->documentRevision() != doc->editorRevision()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc->ast()
|
||||
&& m_model) {
|
||||
|
||||
// got a correctly parsed (or recovered) file.
|
||||
|
||||
if (QmlOutlineModel *qmlModel = qobject_cast<QmlOutlineModel*>(m_model)) {
|
||||
qmlModel->update(doc);
|
||||
}
|
||||
} else {
|
||||
// TODO: Maybe disable view?
|
||||
}
|
||||
|
||||
m_treeView->expandAll();
|
||||
updateSelectionInTree(m_editor.data()->outlineModelIndex());
|
||||
}
|
||||
|
||||
QModelIndex QmlJSOutlineWidget::indexForPosition(const QModelIndex &rootIndex, int cursorPosition)
|
||||
{
|
||||
if (!rootIndex.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
AST::SourceLocation location = rootIndex.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
|
||||
|
||||
if (!offsetInsideLocation(cursorPosition, location)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
const int rowCount = rootIndex.model()->rowCount(rootIndex);
|
||||
for (int i = 0; i < rowCount; ++i) {
|
||||
QModelIndex childIndex = rootIndex.child(i, 0);
|
||||
QModelIndex resultIndex = indexForPosition(childIndex, cursorPosition);
|
||||
if (resultIndex.isValid())
|
||||
return resultIndex;
|
||||
}
|
||||
|
||||
return rootIndex;
|
||||
}
|
||||
|
||||
void QmlJSOutlineWidget::updateSelectionInTree()
|
||||
void QmlJSOutlineWidget::updateSelectionInTree(const QModelIndex &index)
|
||||
{
|
||||
if (!syncCursor())
|
||||
return;
|
||||
|
||||
int absoluteCursorPos = m_editor.data()->textCursor().position();
|
||||
QModelIndex index = indexForPosition(m_model->index(0, 0), absoluteCursorPos);
|
||||
|
||||
m_blockCursorSync = true;
|
||||
m_treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
|
||||
m_treeView->scrollTo(index);
|
||||
@@ -150,12 +99,6 @@ void QmlJSOutlineWidget::updateSelectionInText(const QItemSelection &selection)
|
||||
}
|
||||
}
|
||||
|
||||
bool QmlJSOutlineWidget::offsetInsideLocation(quint32 offset, const QmlJS::AST::SourceLocation &location)
|
||||
{
|
||||
return ((offset >= location.offset)
|
||||
&& (offset <= location.offset + location.length));
|
||||
}
|
||||
|
||||
bool QmlJSOutlineWidget::syncCursor()
|
||||
{
|
||||
return m_enableCursorSync && !m_blockCursorSync;
|
||||
|
||||
@@ -37,18 +37,15 @@ public:
|
||||
virtual void setCursorSynchronization(bool syncWithCursor);
|
||||
|
||||
private slots:
|
||||
void updateOutline(const QmlJSEditor::Internal::SemanticInfo &semanticInfo);
|
||||
void updateSelectionInTree();
|
||||
void modelUpdated();
|
||||
void updateSelectionInTree(const QModelIndex &index);
|
||||
void updateSelectionInText(const QItemSelection &selection);
|
||||
|
||||
private:
|
||||
QModelIndex indexForPosition(const QModelIndex &rootIndex, int cursorPosition);
|
||||
bool offsetInsideLocation(quint32 offset, const QmlJS::AST::SourceLocation &location);
|
||||
bool syncCursor();
|
||||
|
||||
private:
|
||||
QmlJSOutlineTreeView *m_treeView;
|
||||
QAbstractItemModel *m_model;
|
||||
QWeakPointer<QmlJSTextEditor> m_editor;
|
||||
|
||||
bool m_enableCursorSync;
|
||||
|
||||
@@ -124,6 +124,8 @@ void QmlOutlineModel::update(QmlJS::Document::Ptr doc)
|
||||
|
||||
QmlOutlineModelSync syncModel(this);
|
||||
syncModel(doc);
|
||||
|
||||
emit updated();
|
||||
}
|
||||
|
||||
QModelIndex QmlOutlineModel::enterElement(const QString &type, const AST::SourceLocation &sourceLocation)
|
||||
|
||||
@@ -27,6 +27,9 @@ public:
|
||||
QModelIndex enterProperty(const QString &name, const QmlJS::AST::SourceLocation &location);
|
||||
void leaveProperty();
|
||||
|
||||
signals:
|
||||
void updated();
|
||||
|
||||
private:
|
||||
QStandardItem *enterNode(const QmlJS::AST::SourceLocation &location);
|
||||
void leaveNode();
|
||||
|
||||
Reference in New Issue
Block a user