From 1530273671aa46f5a362a3ddf4fa9875bd9e13a4 Mon Sep 17 00:00:00 2001 From: Leandro Melo <leandro.melo@nokia.com> Date: Mon, 2 Aug 2010 14:50:38 +0200 Subject: [PATCH] Color tip for QML; Start of a bit more extensible tooltip class. --- src/plugins/cppeditor/cpphoverhandler.cpp | 8 +- src/plugins/qmljseditor/qmljshoverhandler.cpp | 247 +++++++++++++----- src/plugins/qmljseditor/qmljshoverhandler.h | 38 ++- src/plugins/texteditor/texteditor.pro | 16 +- .../texteditor/tooltip/tipcontents.cpp | 76 ++++++ src/plugins/texteditor/tooltip/tipcontents.h | 72 +++++ src/plugins/texteditor/tooltip/tips.cpp | 100 +++++++ src/plugins/texteditor/tooltip/tips.h | 81 ++++++ src/plugins/texteditor/tooltip/tooltip.cpp | 233 +++++++++++++++++ src/plugins/texteditor/tooltip/tooltip.h | 101 +++++++ 10 files changed, 880 insertions(+), 92 deletions(-) create mode 100644 src/plugins/texteditor/tooltip/tipcontents.cpp create mode 100644 src/plugins/texteditor/tooltip/tipcontents.h create mode 100644 src/plugins/texteditor/tooltip/tips.cpp create mode 100644 src/plugins/texteditor/tooltip/tips.h create mode 100644 src/plugins/texteditor/tooltip/tooltip.cpp create mode 100644 src/plugins/texteditor/tooltip/tooltip.h diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp index 3a9ad4b093e..baf9d688540 100644 --- a/src/plugins/cppeditor/cpphoverhandler.cpp +++ b/src/plugins/cppeditor/cpphoverhandler.cpp @@ -39,6 +39,7 @@ #include <texteditor/itexteditor.h> #include <texteditor/basetexteditor.h> #include <texteditor/displaysettings.h> +#include <texteditor/tooltip/tooltip.h> #include <debugger/debuggerconstants.h> #include <FullySpecifiedType.h> @@ -57,7 +58,6 @@ #include <QtCore/QFileInfo> #include <QtCore/QtAlgorithms> #include <QtCore/QStringBuilder> -#include <QtGui/QToolTip> #include <QtGui/QTextCursor> #include <algorithm> @@ -159,7 +159,7 @@ void CppHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int p // If the tooltip is visible and there is a help match, this match is used to update the help // id. Otherwise, the identification process happens. - if (!QToolTip::isVisible() || m_matchingHelpCandidate == -1) + if (!TextEditor::ToolTip::instance()->isVisible() || m_matchingHelpCandidate == -1) identifyMatch(editor, pos); if (m_matchingHelpCandidate != -1) @@ -185,7 +185,7 @@ void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint identifyMatch(editor, pos); if (m_toolTip.isEmpty()) { - QToolTip::hideText(); + TextEditor::ToolTip::instance()->hide(); } else { if (!m_classHierarchy.isEmpty()) generateDiagramTooltip(baseEditor->displaySettings().m_extendTooltips); @@ -203,7 +203,7 @@ void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint #endif ); - QToolTip::showText(pnt, m_toolTip); + TextEditor::ToolTip::instance()->showText(pnt, m_toolTip, editor->widget()); } } diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index 519eec21904..aa7e22d60fe 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -37,29 +37,55 @@ #include <coreplugin/editormanager/editormanager.h> #include <debugger/debuggerconstants.h> #include <extensionsystem/pluginmanager.h> -#include <qmljs/qmljslookupcontext.h> #include <qmljs/qmljsinterpreter.h> #include <qmljs/parser/qmljsast_p.h> +#include <qmljs/parser/qmljsastfwd_p.h> +#include <qmljs/qmljscheck.h> #include <texteditor/itexteditor.h> #include <texteditor/basetexteditor.h> - -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QFileInfo> -#include <QtCore/QSettings> -#include <QtGui/QToolTip> -#include <QtGui/QTextCursor> -#include <QtGui/QTextBlock> +#include <texteditor/tooltip/tooltip.h> using namespace Core; using namespace QmlJS; using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; -HoverHandler::HoverHandler(QObject *parent) - : QObject(parent) +namespace { + + QString textAt(const Document::Ptr doc, + const AST::SourceLocation &from, + const AST::SourceLocation &to) + { + return doc->source().mid(from.offset, to.end() - from.begin()); + } + + AST::UiObjectInitializer *nodeInitializer(AST::Node *node) + { + AST::UiObjectInitializer *initializer = 0; + if (const AST::UiObjectBinding *binding = AST::cast<const AST::UiObjectBinding *>(node)) + initializer = binding->initializer; + else if (const AST::UiObjectDefinition *definition = + AST::cast<const AST::UiObjectDefinition *>(node)) + initializer = definition->initializer; + return initializer; + } + + template <class T> + bool posIsInSource(const unsigned pos, T *node) + { + if (node && + pos >= node->firstSourceLocation().begin() && pos < node->lastSourceLocation().end()) { + return true; + } + return false; + } +} + +HoverHandler::HoverHandler(QObject *parent) : + QObject(parent), m_modelManager(0), m_matchingHelpCandidate(-1) { - m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<QmlJS::ModelManagerInterface>(); + m_modelManager = + ExtensionSystem::PluginManager::instance()->getObject<QmlJS::ModelManagerInterface>(); // Listen for editor opened events in order to connect to tooltip/helpid requests connect(ICore::instance()->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), @@ -81,21 +107,23 @@ void HoverHandler::editorOpened(IEditor *editor) void HoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos) { - if (! editor) + if (!editor) return; ICore *core = ICore::instance(); - const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE); - + const int dbgcontext = + core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE); if (core->hasContext(dbgcontext)) return; - updateHelpIdAndTooltip(editor, pos); + editor->setContextHelpId(QString()); + + identifyMatch(editor, pos); if (m_toolTip.isEmpty()) - QToolTip::hideText(); + TextEditor::ToolTip::instance()->hide(); else { - const QPoint pnt = point - QPoint(0, + const QPoint &pnt = point - QPoint(0, #ifdef Q_WS_WIN 24 #else @@ -103,88 +131,166 @@ void HoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &po #endif ); - QToolTip::showText(pnt, m_toolTip); + if (m_colorTip.isValid()) { + TextEditor::ToolTip::instance()->showColor(pnt, m_colorTip, editor->widget()); + } else { + m_toolTip = Qt::escape(m_toolTip); + if (m_matchingHelpCandidate != -1) { + m_toolTip = QString::fromUtf8( + "<table><tr><td valign=middle><nobr>%1</td><td>" + "<img src=\":/cppeditor/images/f1.png\"></td></tr></table>").arg(m_toolTip); + } else { + m_toolTip = QString::fromUtf8("<nobr>%1</nobr>").arg(m_toolTip); + } + TextEditor::ToolTip::instance()->showText(pnt, m_toolTip, editor->widget()); + } } } void HoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos) { - updateHelpIdAndTooltip(editor, pos); + // If the tooltip is visible and there is a help match, use it to update the help id. + // Otherwise, identify the match. + if (!TextEditor::ToolTip::instance()->isVisible() || m_matchingHelpCandidate == -1) + identifyMatch(editor, pos); + + if (m_matchingHelpCandidate != -1) + editor->setContextHelpId(m_helpCandidates.at(m_matchingHelpCandidate)); + else + editor->setContextHelpId(QString()); } -void HoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos) +void HoverHandler::resetMatchings() { - m_helpId.clear(); + m_matchingHelpCandidate = -1; + m_helpCandidates.clear(); m_toolTip.clear(); + m_colorTip = QColor(); +} + +void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) +{ + resetMatchings(); if (!m_modelManager) return; - QmlJSTextEditor *edit = qobject_cast<QmlJSTextEditor *>(editor->widget()); - if (!edit) + QmlJSTextEditor *qmlEditor = qobject_cast<QmlJSTextEditor *>(editor->widget()); + if (!qmlEditor) return; - const SemanticInfo semanticInfo = edit->semanticInfo(); + if (!matchDiagnosticMessage(qmlEditor, pos)) { + const SemanticInfo &semanticInfo = qmlEditor->semanticInfo(); + if (semanticInfo.revision() != qmlEditor->editorRevision()) + return; - if (semanticInfo.revision() != edit->editorRevision()) - return; + QList<AST::Node *> astPath = semanticInfo.astPath(pos); + if (astPath.isEmpty()) + return; - const Snapshot snapshot = semanticInfo.snapshot; - const Document::Ptr qmlDocument = semanticInfo.document; + const Snapshot &snapshot = semanticInfo.snapshot; + const Document::Ptr qmlDocument = semanticInfo.document; + LookupContext::Ptr lookupContext = LookupContext::create(qmlDocument, snapshot, astPath); - // We only want to show F1 if the tooltip matches the help id - bool showF1 = true; + if (!matchColorItem(lookupContext, qmlDocument, astPath, pos)) + handleOrdinaryMatch(lookupContext, semanticInfo.nodeUnderCursor(pos)); + } - foreach (const QTextEdit::ExtraSelection &sel, edit->extraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection)) { + evaluateHelpCandidates(); +} + +bool HoverHandler::matchDiagnosticMessage(QmlJSTextEditor *qmlEditor, int pos) +{ + foreach (const QTextEdit::ExtraSelection &sel, + qmlEditor->extraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection)) { if (pos >= sel.cursor.selectionStart() && pos <= sel.cursor.selectionEnd()) { - showF1 = false; m_toolTip = sel.format.toolTip(); + return true; } } + return false; +} - QString symbolName = QLatin1String("<unknown>"); - if (m_helpId.isEmpty() && m_toolTip.isEmpty()) { - AST::Node *node = semanticInfo.nodeUnderCursor(pos); - if (node && !(AST::cast<AST::StringLiteral *>(node) != 0 || AST::cast<AST::NumericLiteral *>(node) != 0)) { - QList<AST::Node *> astPath = semanticInfo.astPath(pos); - - LookupContext::Ptr lookupContext = LookupContext::create(qmlDocument, snapshot, astPath); - const Interpreter::Value *value = lookupContext->evaluate(node); - - QStringList baseClasses; - m_toolTip = prettyPrint(value, lookupContext->context(), &baseClasses); +bool HoverHandler::matchColorItem(const LookupContext::Ptr &lookupContext, + const Document::Ptr &qmlDocument, + const QList<AST::Node *> &astPath, + unsigned pos) +{ + AST::UiObjectInitializer *initializer = nodeInitializer(astPath.last()); + if (!initializer) + return false; + + AST::UiObjectMember *member = 0; + for (AST::UiObjectMemberList *list = initializer->members; list; list = list->next) { + if (posIsInSource(pos, list->member)) { + member = list->member; + break; + } + } + if (!member) + return false; + + QString color; + const Interpreter::Value *value = 0; + if (const AST::UiScriptBinding *binding = AST::cast<const AST::UiScriptBinding *>(member)) { + if (binding->qualifiedId && posIsInSource(pos, binding->statement)) { + value = lookupContext->evaluate(binding->qualifiedId); + if (value && value->asColorValue()) { + color = textAt(qmlDocument, + binding->statement->firstSourceLocation(), + binding->statement->lastSourceLocation()); + } + } + } else if (const AST::UiPublicMember *publicMember = + AST::cast<const AST::UiPublicMember *>(member)) { + if (publicMember->name && posIsInSource(pos, publicMember->expression)) { + value = lookupContext->context()->lookup(publicMember->name->asString()); + if (const Interpreter::Reference *ref = value->asReference()) + value = lookupContext->context()->lookupReference(ref); + color = textAt(qmlDocument, + publicMember->expression->firstSourceLocation(), + publicMember->expression->lastSourceLocation()); + } + } - foreach (const QString &baseClass, baseClasses) { - QString helpId = QLatin1String("QML."); - helpId += baseClass; + if (!color.isEmpty()) { + color.remove(QLatin1Char('\'')); + color.remove(QLatin1Char('\"')); + color.remove(QLatin1Char(';')); - if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) { - m_helpId = helpId; - break; - } - } + m_colorTip = QmlJS::toQColor(color); + if (m_colorTip.isValid()) { + m_toolTip = color; + return true; } } + return false; +} - if (!m_toolTip.isEmpty()) - m_toolTip = Qt::escape(m_toolTip); +void HoverHandler::handleOrdinaryMatch(const LookupContext::Ptr &lookupContext, AST::Node *node) +{ + if (node && !(AST::cast<AST::StringLiteral *>(node) != 0 || + AST::cast<AST::NumericLiteral *>(node) != 0)) { + const Interpreter::Value *value = lookupContext->evaluate(node); + m_toolTip = prettyPrint(value, lookupContext->context()); + } +} - if (!m_helpId.isEmpty()) { - if (showF1) { - m_toolTip = QString::fromUtf8("<table><tr><td valign=middle><nobr>%1</td>" - "<td><img src=\":/cppeditor/images/f1.png\"></td></tr></table>") - .arg(m_toolTip); +void HoverHandler::evaluateHelpCandidates() +{ + for (int i = 0; i < m_helpCandidates.size(); ++i) { + QString helpId = m_helpCandidates.at(i); + helpId.prepend(QLatin1String("QML.")); + if (!Core::HelpManager::instance()->linksForIdentifier(helpId).isEmpty()) { + m_matchingHelpCandidate = i; + m_helpCandidates[i] = helpId; + break; } - editor->setContextHelpId(m_helpId); - } else if (!m_toolTip.isEmpty()) { - m_toolTip = QString::fromUtf8("<nobr>%1").arg(m_toolTip); - } else if (!m_helpId.isEmpty()) { - m_toolTip = QString::fromUtf8("<nobr>No help available for \"%1\"").arg(symbolName); } } -QString HoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Context *context, - QStringList *baseClasses) const +QString HoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, + QmlJS::Interpreter::Context *context) { if (! value) return QString(); @@ -194,14 +300,15 @@ QString HoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS: const QString className = objectValue->className(); if (! className.isEmpty()) - baseClasses->append(className); + m_helpCandidates.append(className); objectValue = objectValue->prototype(context); } while (objectValue); - if (! baseClasses->isEmpty()) - return baseClasses->first(); - } else if (const Interpreter::QmlEnumValue *enumValue = dynamic_cast<const Interpreter::QmlEnumValue *>(value)) { + if (! m_helpCandidates.isEmpty()) + return m_helpCandidates.first(); + } else if (const Interpreter::QmlEnumValue *enumValue = + dynamic_cast<const Interpreter::QmlEnumValue *>(value)) { return enumValue->name(); } diff --git a/src/plugins/qmljseditor/qmljshoverhandler.h b/src/plugins/qmljseditor/qmljshoverhandler.h index 29fbd20ba00..49f763deef2 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.h +++ b/src/plugins/qmljseditor/qmljshoverhandler.h @@ -31,25 +31,20 @@ #define QMLJSHOVERHANDLER_H #include <qmljs/qmljsmodelmanagerinterface.h> +#include <qmljs/qmljslookupcontext.h> + #include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtGui/QColor> QT_BEGIN_NAMESPACE class QPoint; -class QStringList; QT_END_NAMESPACE namespace Core { class IEditor; } -namespace QmlJS { - namespace Interpreter { - class Engine; - class Context; - class Value; - } -} - namespace TextEditor { class ITextEditor; } @@ -57,6 +52,9 @@ class ITextEditor; namespace QmlJSEditor { namespace Internal { +class SemanticInfo; +class QmlJSTextEditor; + class HoverHandler : public QObject { Q_OBJECT @@ -72,14 +70,26 @@ private slots: void editorOpened(Core::IEditor *editor); private: - void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos); - QString prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Context *context, - QStringList *baseClasses) const; + void resetMatchings(); + void identifyMatch(TextEditor::ITextEditor *editor, int pos); + bool matchDiagnosticMessage(QmlJSTextEditor *qmlEditor, int pos); + bool matchColorItem(const QmlJS::LookupContext::Ptr &lookupContext, + const QmlJS::Document::Ptr &qmlDocument, + const QList<QmlJS::AST::Node *> &astPath, + unsigned pos); + void handleOrdinaryMatch(const QmlJS::LookupContext::Ptr &lookupContext, + QmlJS::AST::Node *node); + + void evaluateHelpCandidates(); + + QString prettyPrint(const QmlJS::Interpreter::Value *value, + QmlJS::Interpreter::Context *context); -private: QmlJS::ModelManagerInterface *m_modelManager; - QString m_helpId; + int m_matchingHelpCandidate; + QStringList m_helpCandidates; QString m_toolTip; + QColor m_colorTip; }; } // namespace Internal diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 55cb8c92c6c..14314433f46 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -4,8 +4,10 @@ DEFINES += TEXTEDITOR_LIBRARY QT += xml network include(../../qtcreatorplugin.pri) include(texteditor_dependencies.pri) -INCLUDEPATH += generichighlighter -DEPENDPATH += generichighlighter +INCLUDEPATH += generichighlighter \ + tooltip +DEPENDPATH += generichighlighter \ + tooltip SOURCES += texteditorplugin.cpp \ textfilewizard.cpp \ plaintexteditor.cpp \ @@ -62,7 +64,10 @@ SOURCES += texteditorplugin.cpp \ generichighlighter/definitiondownloader.cpp \ refactoringchanges.cpp \ refactoroverlay.cpp \ - outlinefactory.cpp + outlinefactory.cpp \ + tooltip/tooltip.cpp \ + tooltip/tips.cpp \ + tooltip/tipcontents.cpp HEADERS += texteditorplugin.h \ textfilewizard.h \ @@ -128,7 +133,10 @@ HEADERS += texteditorplugin.h \ refactoringchanges.h \ refactoroverlay.h \ outlinefactory.h \ - ioutlinewidget.h + ioutlinewidget.h \ + tooltip/tooltip.h \ + tooltip/tips.h \ + tooltip/tipcontents.h FORMS += behaviorsettingspage.ui \ displaysettingspage.ui \ diff --git a/src/plugins/texteditor/tooltip/tipcontents.cpp b/src/plugins/texteditor/tooltip/tipcontents.cpp new file mode 100644 index 00000000000..3854f52bbd9 --- /dev/null +++ b/src/plugins/texteditor/tooltip/tipcontents.cpp @@ -0,0 +1,76 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "tipcontents.h" + +using namespace TextEditor; +using namespace Internal; + +TipContent::TipContent() +{} + +TipContent::~TipContent() +{} + +QColorContent::QColorContent(const QColor &color) : m_color(color) +{} + +QColorContent::~QColorContent() +{} + +bool TipContent::equals(const QColorContent *colorContent) const +{ + Q_UNUSED(colorContent) + return false; +} + +bool QColorContent::isValid() const +{ + return m_color.isValid(); +} + +int QColorContent::showTime() const +{ + return 4000; +} + +bool QColorContent::equals(const TipContent *tipContent) const +{ + return tipContent->equals(this); +} + +bool QColorContent::equals(const QColorContent *colorContent) const +{ + return m_color == colorContent->color(); +} + +const QColor &QColorContent::color() const +{ + return m_color; +} diff --git a/src/plugins/texteditor/tooltip/tipcontents.h b/src/plugins/texteditor/tooltip/tipcontents.h new file mode 100644 index 00000000000..2bae742fd44 --- /dev/null +++ b/src/plugins/texteditor/tooltip/tipcontents.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TIPCONTENTS_H +#define TIPCONTENTS_H + +#include <QtGui/QColor> + +namespace TextEditor { +namespace Internal { + +class QColorContent; + +class TipContent +{ +public: + TipContent(); + virtual ~TipContent(); + + virtual bool isValid() const = 0; + virtual int showTime() const = 0; + virtual bool equals(const TipContent *tipContent) const = 0; + virtual bool equals(const QColorContent *colorContent) const; +}; + +class QColorContent : public TipContent +{ +public: + QColorContent(const QColor &color); + virtual ~QColorContent(); + + virtual bool isValid() const; + virtual int showTime() const; + virtual bool equals(const TipContent *tipContent) const; + virtual bool equals(const QColorContent *colorContent) const; + + const QColor &color() const; + +private: + QColor m_color; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // TIPCONTENTS_H diff --git a/src/plugins/texteditor/tooltip/tips.cpp b/src/plugins/texteditor/tooltip/tips.cpp new file mode 100644 index 00000000000..2825f1c9554 --- /dev/null +++ b/src/plugins/texteditor/tooltip/tips.cpp @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "tips.h" +#include "tipcontents.h" + +#include <QtCore/QRect> +#include <QtGui/QColor> +#include <QtGui/QPainter> +#include <QtGui/QPixmap> + +using namespace TextEditor; +using namespace Internal; + +namespace { + // @todo: Reuse... + QPixmap tilePixMap(int size) + { + const int checkerbordSize= size; + QPixmap tilePixmap(checkerbordSize * 2, checkerbordSize * 2); + tilePixmap.fill(Qt::white); + QPainter tilePainter(&tilePixmap); + QColor color(220, 220, 220); + tilePainter.fillRect(0, 0, checkerbordSize, checkerbordSize, color); + tilePainter.fillRect(checkerbordSize, checkerbordSize, checkerbordSize, checkerbordSize, color); + return tilePixmap; + } +} + +Tip::Tip(QWidget *parent) : QFrame(parent) +{ + setWindowFlags(Qt::ToolTip); + setAutoFillBackground(true); + ensurePolished(); +} + +Tip::~Tip() +{} + +void Tip::setContent(const QSharedPointer<TipContent> &content) +{ + m_content = content; + configure(); +} + +const QSharedPointer<TipContent> &Tip::content() const +{ return m_content; } + +ColorTip::ColorTip(QWidget *parent) : Tip(parent) +{ + setFrameStyle(QFrame::Box); + resize(QSize(40, 40)); + + m_tilePixMap = tilePixMap(9); +} + +ColorTip::~ColorTip() +{} + +void ColorTip::configure() +{ + update(); +} + +void ColorTip::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + QRect r(1, 1, width() - 2, height() - 2); + painter.drawTiledPixmap(r, m_tilePixMap); + painter.setBrush(static_cast<QColorContent *>(content().data())->color()); + painter.drawRect(rect()); + + QFrame::paintEvent(event); +} diff --git a/src/plugins/texteditor/tooltip/tips.h b/src/plugins/texteditor/tooltip/tips.h new file mode 100644 index 00000000000..b7dc9d3af41 --- /dev/null +++ b/src/plugins/texteditor/tooltip/tips.h @@ -0,0 +1,81 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TIPS_H +#define TIPS_H + +#include <QtCore/QSharedPointer> +#include <QtGui/QFrame> +#include <QtGui/QPixmap> + +QT_BEGIN_NAMESPACE +class QColor; +QT_END_NAMESPACE + +namespace TextEditor { +namespace Internal { + +class TipContent; + +class Tip : public QFrame +{ + Q_OBJECT +protected: + Tip(QWidget *parent); + +public: + virtual ~Tip(); + + void setContent(const QSharedPointer<TipContent> &content); + const QSharedPointer<TipContent> &content() const; + +private: + virtual void configure() = 0; + + QSharedPointer<TipContent> m_content; +}; + +class ColorTip : public Tip +{ + Q_OBJECT +public: + ColorTip(QWidget *parent); + virtual ~ColorTip(); + +private: + virtual void configure(); + virtual void paintEvent(QPaintEvent *event); + + QPixmap m_tilePixMap; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // TIPS_H diff --git a/src/plugins/texteditor/tooltip/tooltip.cpp b/src/plugins/texteditor/tooltip/tooltip.cpp new file mode 100644 index 00000000000..52c14789717 --- /dev/null +++ b/src/plugins/texteditor/tooltip/tooltip.cpp @@ -0,0 +1,233 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "tooltip.h" +#include "tips.h" +#include "tipcontents.h" + +#include <QtCore/QString> +#include <QtGui/QColor> +#include <QtGui/QApplication> +#include <QtGui/QDesktopWidget> +#include <QtGui/QToolTip> +#include <QtGui/QKeyEvent> + +using namespace TextEditor; +using namespace Internal; + +ToolTip::ToolTip() : m_tip(0), m_widget(0) +{ + connect(&m_showTimer, SIGNAL(timeout()), this, SLOT(hideTipImmediately())); + connect(&m_hideDelayTimer, SIGNAL(timeout()), this, SLOT(hideTipImmediately())); +} + +ToolTip::~ToolTip() +{ m_tip = 0; } + +ToolTip *ToolTip::instance() +{ + static ToolTip tooltip; + return &tooltip; +} + +void ToolTip::showText(const QPoint &pos, const QString &text, QWidget *w) +{ + hideTipImmediately(); + QToolTip::showText(pos, text, w); +} + +void ToolTip::showColor(const QPoint &pos, const QColor &color, QWidget *w) +{ + hideQtTooltip(); + QSharedPointer<TipContent> colorContent(new QColorContent(color)); + if (acceptShow(colorContent, pos, w) && colorContent->isValid()) { +#ifndef Q_WS_WIN + m_tip = new ColorTip(w); +#else + m_tip = new ColorTip(QApplication::desktop()->screen(tipScreen(pos, w))); +#endif + setUp(colorContent, pos, w); + qApp->installEventFilter(this); + showTip(); + } +} + +bool ToolTip::isVisible() const +{ + return QToolTip::isVisible() || (m_tip && m_tip->isVisible()); +} + +bool ToolTip::acceptShow(const QSharedPointer<TipContent> &content, const QPoint &pos, QWidget *w) +{ + if (m_tip && m_tip->isVisible()) { + if (!content->isValid()) { + hideTipWithDelay(); + return false; + } else { + // Reuse current tip. + QPoint localPos = pos; + if (w) + localPos = w->mapFromGlobal(pos); + if (requiresSetUp(content, w)) + setUp(content, pos, w); + return false; + } + } + return true; +} + +void ToolTip::setUp(const QSharedPointer<TipContent> &content, const QPoint &pos, QWidget *w) +{ + m_tip->setContent(content); + placeTip(pos, w); + m_widget = w; + m_showTimer.start(content->showTime()); +} + +bool ToolTip::requiresSetUp(const QSharedPointer<TipContent> &tipContent, QWidget *w) const +{ + if (!m_tip->content()->equals(tipContent.data())) + return true; + if (m_widget != w) + return true; + return false; +} + +void ToolTip::showTip() +{ + m_tip->show(); +} + +void ToolTip::hide() +{ + hideQtTooltip(); + hideTipWithDelay(); +} + +void ToolTip::hideTipWithDelay() +{ + if (!m_hideDelayTimer.isActive()) + m_hideDelayTimer.start(300); +} + +void ToolTip::hideTipImmediately() +{ + if (m_tip) { + m_tip->close(); + m_tip->deleteLater(); + m_tip = 0; + } + m_showTimer.stop(); + m_hideDelayTimer.stop(); + qApp->removeEventFilter(this); +} + +void ToolTip::hideQtTooltip() +{ + if (QToolTip::isVisible()) + QToolTip::hideText(); +} + +void ToolTip::placeTip(const QPoint &pos, QWidget *w) +{ +#ifdef Q_WS_MAC + QRect screen = QApplication::desktop()->availableGeometry(tipScreen(pos, w)); +#else + QRect screen = QApplication::desktop()->screenGeometry(tipScreen(pos, w)); +#endif + + QPoint p = pos; + p += QPoint(2, +#ifdef Q_WS_WIN + 21 +#else + 16 +#endif + ); + + if (p.x() + m_tip->width() > screen.x() + screen.width()) + p.rx() -= 4 + m_tip->width(); + if (p.y() + m_tip->height() > screen.y() + screen.height()) + p.ry() -= 24 + m_tip->height(); + if (p.y() < screen.y()) + p.setY(screen.y()); + if (p.x() + m_tip->width() > screen.x() + screen.width()) + p.setX(screen.x() + screen.width() - m_tip->width()); + if (p.x() < screen.x()) + p.setX(screen.x()); + if (p.y() + m_tip->height() > screen.y() + screen.height()) + p.setY(screen.y() + screen.height() - m_tip->height()); + + m_tip->move(p); +} + +int ToolTip::tipScreen(const QPoint &pos, QWidget *w) const +{ + if (QApplication::desktop()->isVirtualDesktop()) + return QApplication::desktop()->screenNumber(pos); + else + return QApplication::desktop()->screenNumber(w); +} + +bool ToolTip::eventFilter(QObject *o, QEvent *event) +{ + Q_UNUSED(o) + + switch (event->type()) { +#ifdef Q_WS_MAC + case QEvent::KeyPress: + case QEvent::KeyRelease: { + int key = static_cast<QKeyEvent *>(event)->key(); + Qt::KeyboardModifiers mody = static_cast<QKeyEvent *>(event)->modifiers(); + if (!(mody & Qt::KeyboardModifierMask) + && key != Qt::Key_Shift && key != Qt::Key_Control + && key != Qt::Key_Alt && key != Qt::Key_Meta) + hideTipWithDelay(); + break; + } +#endif + case QEvent::Leave: + hideTipWithDelay(); + break; + case QEvent::WindowActivate: + case QEvent::WindowDeactivate: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::FocusIn: + case QEvent::FocusOut: + case QEvent::Wheel: + hideTipImmediately(); + break; + + default: + break; + } + return false; +} diff --git a/src/plugins/texteditor/tooltip/tooltip.h b/src/plugins/texteditor/tooltip/tooltip.h new file mode 100644 index 00000000000..dd93c888ed4 --- /dev/null +++ b/src/plugins/texteditor/tooltip/tooltip.h @@ -0,0 +1,101 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TOOLTIP_H +#define TOOLTIP_H + +#include "texteditor/texteditor_global.h" + +#include <QtCore/QSharedPointer> +#include <QtCore/QObject> +#include <QtCore/QTimer> + +QT_BEGIN_NAMESPACE +class QPoint; +class QString; +class QColor; +class QWidget; +QT_END_NAMESPACE + +namespace TextEditor { + +namespace Internal { +class Tip; +class TipContent; +} + +/* + * This class contains some code duplicated from QTooltip. It would be good to make that reusable. + */ + +class TEXTEDITOR_EXPORT ToolTip : public QObject +{ + Q_OBJECT +private: + ToolTip(); + +public: + virtual ~ToolTip(); + + static ToolTip *instance(); + + void showText(const QPoint &pos, const QString &text, QWidget *w = 0); + void showColor(const QPoint &pos, const QColor &color, QWidget *w = 0); + void hide(); + bool isVisible() const; + + virtual bool eventFilter(QObject *o, QEvent *event); + +private: + bool acceptShow(const QSharedPointer<Internal::TipContent> &content, + const QPoint &pos, + QWidget *w); + void setUp(const QSharedPointer<Internal::TipContent> &content, + const QPoint &pos, + QWidget *w); + bool requiresSetUp(const QSharedPointer<Internal::TipContent> &content, QWidget *w) const; + void placeTip(const QPoint &pos, QWidget *w); + int tipScreen(const QPoint &pos, QWidget *w) const; + void showTip(); + void hideTipWithDelay(); + void hideQtTooltip(); + +private slots: + void hideTipImmediately(); + +private: + Internal::Tip *m_tip; + QWidget *m_widget; + QTimer m_showTimer; + QTimer m_hideDelayTimer; +}; + +} // namespace TextEditor + +#endif // TOOLTIP_H -- GitLab