summaryrefslogtreecommitdiff
path: root/src/plugins/glsleditor
diff options
context:
space:
mode:
authorRoberto Raggi <roberto.raggi@nokia.com>2010-11-30 15:42:18 +0100
committerRoberto Raggi <roberto.raggi@nokia.com>2010-11-30 15:43:17 +0100
commitad5476ff5415846eb4928fc547aef36af2fbb71b (patch)
treecea99db3ce1e2ac4d061f49b36de67a11e64f5ed /src/plugins/glsleditor
parent20e749df0820e1d8c469a322605ffb69c2e6ad90 (diff)
downloadqt-creator-ad5476ff5415846eb4928fc547aef36af2fbb71b.tar.gz
Show the signatures in a tooltip when completing functions.
Diffstat (limited to 'src/plugins/glsleditor')
-rw-r--r--src/plugins/glsleditor/glslcodecompletion.cpp332
-rw-r--r--src/plugins/glsleditor/glslcodecompletion.h4
2 files changed, 314 insertions, 22 deletions
diff --git a/src/plugins/glsleditor/glslcodecompletion.cpp b/src/plugins/glsleditor/glslcodecompletion.cpp
index 7ab46dea58..e85c834821 100644
--- a/src/plugins/glsleditor/glslcodecompletion.cpp
+++ b/src/plugins/glsleditor/glslcodecompletion.cpp
@@ -31,13 +31,21 @@
#include "glsleditorplugin.h"
#include <glsl/glslengine.h>
#include <glsl/glslengine.h>
+#include <glsl/glsllexer.h>
#include <glsl/glslparser.h>
#include <glsl/glslsemantic.h>
+#include <glsl/glslsymbols.h>
#include <glsl/glslastdump.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <texteditor/completionsettings.h>
+#include <utils/faketooltip.h>
#include <QtGui/QIcon>
#include <QtGui/QPainter>
+#include <QtGui/QLabel>
+#include <QtGui/QToolButton>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QApplication>
+#include <QtGui/QDesktopWidget>
#include <QtCore/QDebug>
using namespace GLSLEditor;
@@ -243,6 +251,270 @@ static const char *glsl_keywords[] =
0
};
+namespace GLSLEditor {
+namespace Internal {
+class FunctionArgumentWidget : public QLabel
+{
+ Q_OBJECT
+
+public:
+ FunctionArgumentWidget();
+ void showFunctionHint(QVector<GLSL::Function *> functionSymbols,
+ int startPosition);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *e);
+
+private slots:
+ void nextPage();
+ void previousPage();
+
+private:
+ void updateArgumentHighlight();
+ void updateHintText();
+ void placeInsideScreen();
+
+ GLSL::Function *currentFunction() const
+ { return m_items.at(m_current); }
+
+ int m_startpos;
+ int m_currentarg;
+ int m_current;
+ bool m_escapePressed;
+
+ TextEditor::ITextEditor *m_editor;
+
+ QWidget *m_pager;
+ QLabel *m_numberLabel;
+ Utils::FakeToolTip *m_popupFrame;
+ QVector<GLSL::Function *> m_items;
+};
+
+
+FunctionArgumentWidget::FunctionArgumentWidget():
+ m_startpos(-1),
+ m_current(0),
+ m_escapePressed(false)
+{
+ QObject *editorObject = Core::EditorManager::instance()->currentEditor();
+ m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject);
+
+ m_popupFrame = new Utils::FakeToolTip(m_editor->widget());
+
+ QToolButton *downArrow = new QToolButton;
+ downArrow->setArrowType(Qt::DownArrow);
+ downArrow->setFixedSize(16, 16);
+ downArrow->setAutoRaise(true);
+
+ QToolButton *upArrow = new QToolButton;
+ upArrow->setArrowType(Qt::UpArrow);
+ upArrow->setFixedSize(16, 16);
+ upArrow->setAutoRaise(true);
+
+ setParent(m_popupFrame);
+ setFocusPolicy(Qt::NoFocus);
+
+ m_pager = new QWidget;
+ QHBoxLayout *hbox = new QHBoxLayout(m_pager);
+ hbox->setMargin(0);
+ hbox->setSpacing(0);
+ hbox->addWidget(upArrow);
+ m_numberLabel = new QLabel;
+ hbox->addWidget(m_numberLabel);
+ hbox->addWidget(downArrow);
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setMargin(0);
+ layout->setSpacing(0);
+ layout->addWidget(m_pager);
+ layout->addWidget(this);
+ m_popupFrame->setLayout(layout);
+
+ connect(upArrow, SIGNAL(clicked()), SLOT(previousPage()));
+ connect(downArrow, SIGNAL(clicked()), SLOT(nextPage()));
+
+ setTextFormat(Qt::RichText);
+
+ qApp->installEventFilter(this);
+}
+
+void FunctionArgumentWidget::showFunctionHint(QVector<GLSL::Function *> functionSymbols,
+ int startPosition)
+{
+ Q_ASSERT(!functionSymbols.isEmpty());
+
+ if (m_startpos == startPosition)
+ return;
+
+ m_pager->setVisible(functionSymbols.size() > 1);
+
+ m_items = functionSymbols;
+ m_startpos = startPosition;
+ m_current = 0;
+ m_escapePressed = false;
+
+ // update the text
+ m_currentarg = -1;
+ updateArgumentHighlight();
+
+ m_popupFrame->show();
+}
+
+void FunctionArgumentWidget::nextPage()
+{
+ m_current = (m_current + 1) % m_items.size();
+ updateHintText();
+}
+
+void FunctionArgumentWidget::previousPage()
+{
+ if (m_current == 0)
+ m_current = m_items.size() - 1;
+ else
+ --m_current;
+
+ updateHintText();
+}
+
+void FunctionArgumentWidget::updateArgumentHighlight()
+{
+ int curpos = m_editor->position();
+ if (curpos < m_startpos) {
+ m_popupFrame->close();
+ return;
+ }
+
+ const QByteArray str = m_editor->textAt(m_startpos, curpos - m_startpos).toLatin1();
+
+ int argnr = 0;
+ int parcount = 0;
+ GLSL::Lexer lexer(0, str.constData(), str.length());
+ GLSL::Token tk;
+ QList<GLSL::Token> tokens;
+ do {
+ lexer.yylex(&tk);
+ tokens.append(tk);
+ } while (tk.isNot(GLSL::Parser::EOF_SYMBOL));
+ for (int i = 0; i < tokens.count(); ++i) {
+ const GLSL::Token &tk = tokens.at(i);
+ if (tk.is(GLSL::Parser::T_LEFT_PAREN))
+ ++parcount;
+ else if (tk.is(GLSL::Parser::T_RIGHT_PAREN))
+ --parcount;
+ else if (! parcount && tk.is(GLSL::Parser::T_COMMA))
+ ++argnr;
+ }
+
+ if (m_currentarg != argnr) {
+ m_currentarg = argnr;
+ updateHintText();
+ }
+
+ if (parcount < 0)
+ m_popupFrame->close();
+}
+
+bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::ShortcutOverride:
+ if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
+ m_escapePressed = true;
+ }
+ break;
+ case QEvent::KeyPress:
+ if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
+ m_escapePressed = true;
+ }
+ if (m_items.size() > 1) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(e);
+ if (ke->key() == Qt::Key_Up) {
+ previousPage();
+ return true;
+ } else if (ke->key() == Qt::Key_Down) {
+ nextPage();
+ return true;
+ }
+ return false;
+ }
+ break;
+ case QEvent::KeyRelease:
+ if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_escapePressed) {
+ m_popupFrame->close();
+ return false;
+ }
+ updateArgumentHighlight();
+ break;
+ case QEvent::WindowDeactivate:
+ case QEvent::FocusOut:
+ if (obj != m_editor->widget())
+ break;
+ m_popupFrame->close();
+ break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::Wheel: {
+ QWidget *widget = qobject_cast<QWidget *>(obj);
+ if (! (widget == this || m_popupFrame->isAncestorOf(widget))) {
+ m_popupFrame->close();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+void FunctionArgumentWidget::updateHintText()
+{
+ setText(currentFunction()->toString());
+
+ m_numberLabel->setText(tr("%1 of %2").arg(m_current + 1).arg(m_items.size()));
+
+ placeInsideScreen();
+}
+
+void FunctionArgumentWidget::placeInsideScreen()
+{
+ const QDesktopWidget *desktop = QApplication::desktop();
+#ifdef Q_WS_MAC
+ const QRect screen = desktop->availableGeometry(desktop->screenNumber(m_editor->widget()));
+#else
+ const QRect screen = desktop->screenGeometry(desktop->screenNumber(m_editor->widget()));
+#endif
+
+ m_pager->setFixedWidth(m_pager->minimumSizeHint().width());
+
+ setWordWrap(false);
+ const int maxDesiredWidth = screen.width() - 10;
+ const QSize minHint = m_popupFrame->minimumSizeHint();
+ if (minHint.width() > maxDesiredWidth) {
+ setWordWrap(true);
+ m_popupFrame->setFixedWidth(maxDesiredWidth);
+ const int extra =
+ m_popupFrame->contentsMargins().bottom() + m_popupFrame->contentsMargins().top();
+ m_popupFrame->setFixedHeight(heightForWidth(maxDesiredWidth - m_pager->width()) + extra);
+ } else {
+ m_popupFrame->setFixedSize(minHint);
+ }
+
+ const QSize sz = m_popupFrame->size();
+ QPoint pos = m_editor->cursorRect(m_startpos).topLeft();
+ pos.setY(pos.y() - sz.height() - 1);
+
+ if (pos.x() + sz.width() > screen.right())
+ pos.setX(screen.right() - sz.width());
+
+ m_popupFrame->move(pos);
+}
+
+} // Internal
+} // GLSLEditor
+
+
+
CodeCompletion::CodeCompletion(QObject *parent)
: ICompletionCollector(parent),
m_editor(0),
@@ -332,7 +604,8 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
QStringList members;
QStringList specialMembers;
- if (ch == QLatin1Char('.')) {
+ if (ch == QLatin1Char('.') || ch == QLatin1Char('(')) {
+ const bool memberCompletion = (ch == QLatin1Char('.'));
QTextCursor tc(edit->document());
tc.setPosition(pos);
@@ -353,32 +626,46 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
#endif
if (Document::Ptr doc = edit->glslDocument()) {
- // TODO: ### find the enclosing scope in the previously parsed `doc'.
- // let's use the global scope for now. This should be good enough
- // to get some basic completion for the global variables.
GLSL::Scope *currentScope = doc->scopeAt(pos);
GLSL::Semantic sem;
GLSL::Semantic::ExprResult exprTy = sem.expression(expr, currentScope, doc->engine());
if (exprTy.type) {
- if (const GLSL::VectorType *vecTy = exprTy.type->asVectorType()) {
- members = vecTy->members();
-
- // Sort the most relevant swizzle orderings to the top.
- specialMembers += QLatin1String("xy");
- specialMembers += QLatin1String("xyz");
- specialMembers += QLatin1String("xyzw");
- specialMembers += QLatin1String("rgb");
- specialMembers += QLatin1String("rgba");
- specialMembers += QLatin1String("st");
- specialMembers += QLatin1String("stp");
- specialMembers += QLatin1String("stpq");
-
- } else if (const GLSL::Struct *structTy = exprTy.type->asStructType()) {
- members = structTy->members();
-
- } else {
- // some other type
+ if (memberCompletion) {
+ if (const GLSL::VectorType *vecTy = exprTy.type->asVectorType()) {
+ members = vecTy->members();
+
+ // Sort the most relevant swizzle orderings to the top.
+ specialMembers += QLatin1String("xy");
+ specialMembers += QLatin1String("xyz");
+ specialMembers += QLatin1String("xyzw");
+ specialMembers += QLatin1String("rgb");
+ specialMembers += QLatin1String("rgba");
+ specialMembers += QLatin1String("st");
+ specialMembers += QLatin1String("stp");
+ specialMembers += QLatin1String("stpq");
+
+ } else if (const GLSL::Struct *structTy = exprTy.type->asStructType()) {
+ members = structTy->members();
+
+ } else {
+ // some other type
+ }
+ } else { // function completion
+ QVector<GLSL::Function *> signatures;
+ if (const GLSL::Function *funTy = exprTy.type->asFunctionType())
+ signatures.append(const_cast<GLSL::Function *>(funTy)); // ### get rid of the const_cast
+ else if (const GLSL::OverloadSet *overload = exprTy.type->asOverloadSetType())
+ signatures = overload->functions();
+
+ if (! signatures.isEmpty()) {
+ // Recreate if necessary
+ if (!m_functionArgumentWidget)
+ m_functionArgumentWidget = new FunctionArgumentWidget;
+
+ m_functionArgumentWidget->showFunctionHint(signatures, pos + 1);
+ return false;
+ }
}
} else {
// undefined
@@ -503,3 +790,4 @@ void CodeCompletion::cleanup()
m_startPosition = -1;
}
+#include "glslcodecompletion.moc"
diff --git a/src/plugins/glsleditor/glslcodecompletion.h b/src/plugins/glsleditor/glslcodecompletion.h
index 11b8f02a45..d3b110f131 100644
--- a/src/plugins/glsleditor/glslcodecompletion.h
+++ b/src/plugins/glsleditor/glslcodecompletion.h
@@ -30,10 +30,13 @@
#define GLSLCODECOMPLETION_H
#include <texteditor/icompletioncollector.h>
+#include <QtCore/QPointer>
namespace GLSLEditor {
namespace Internal {
+class FunctionArgumentWidget;
+
class CodeCompletion: public TextEditor::ICompletionCollector
{
Q_OBJECT
@@ -100,6 +103,7 @@ private:
TextEditor::ITextEditable *m_editor;
int m_startPosition;
bool m_restartCompletion;
+ QPointer<FunctionArgumentWidget> m_functionArgumentWidget;
static bool glslCompletionItemLessThan(const TextEditor::CompletionItem &l, const TextEditor::CompletionItem &r);
};