Skip to content
Snippets Groups Projects
Commit 94264617 authored by Kai Koehne's avatar Kai Koehne
Browse files

QmlJSEditor: Use QmlOutline model also in combo box

The drop down combo box and the Outline in the sidebar now share
the same model.
parent 9d803829
No related branches found
No related tags found
No related merge requests found
......@@ -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>"));
m_outlineModel->update(doc);
updateMethodBoxIndex();
foreach (Declaration decl, m_semanticInfo.declarations)
items.append(decl.text);
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
m_methodCombo->clear();
m_methodCombo->addItems(items);
updateMethodBoxIndex();
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();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment