From ba876ffd480511d85731d3dc49b0718ba65dc633 Mon Sep 17 00:00:00 2001 From: Leandro Melo <leandro.melo@nokia.com> Date: Wed, 1 Sep 2010 12:08:38 +0200 Subject: [PATCH] Improve editor's tooltip. Wrapping hack should no longer be necessary. Also some less significant improvements. --- src/libs/utils/htmldocextractor.cpp | 80 +++++-------------- src/libs/utils/htmldocextractor.h | 11 +-- src/plugins/cppeditor/cpphoverhandler.cpp | 12 +-- src/plugins/cppeditor/cpphoverhandler.h | 2 +- src/plugins/texteditor/basehoverhandler.cpp | 17 ++-- src/plugins/texteditor/basehoverhandler.h | 2 +- src/plugins/texteditor/helpitem.cpp | 4 +- src/plugins/texteditor/texteditor.pro | 5 +- src/plugins/texteditor/tooltip/reuse.h | 62 ++++++++++++++ src/plugins/texteditor/tooltip/tipfactory.cpp | 4 - src/plugins/texteditor/tooltip/tips.cpp | 36 +++++++-- src/plugins/texteditor/tooltip/tips.h | 6 +- src/plugins/texteditor/tooltip/tooltip.cpp | 35 ++++---- src/plugins/texteditor/tooltip/tooltip.h | 5 +- 14 files changed, 155 insertions(+), 126 deletions(-) create mode 100644 src/plugins/texteditor/tooltip/reuse.h diff --git a/src/libs/utils/htmldocextractor.cpp b/src/libs/utils/htmldocextractor.cpp index 2f6481379b5..0877be15474 100644 --- a/src/libs/utils/htmldocextractor.cpp +++ b/src/libs/utils/htmldocextractor.cpp @@ -45,21 +45,12 @@ namespace { } HtmlDocExtractor::HtmlDocExtractor() : - m_lengthReference(-1), - m_truncateAtParagraph(false), m_formatContents(true), - m_extendedExtraction(false) + m_mode(FirstParagraph) {} -void HtmlDocExtractor::extractFirstParagraphOnly() -{ m_extendedExtraction = false; } - -void HtmlDocExtractor::extractExtendedContents(const int length, const bool truncateAtParagraph) -{ - m_lengthReference = length; - m_truncateAtParagraph = truncateAtParagraph; - m_extendedExtraction = true; -} +void HtmlDocExtractor::setMode(Mode mode) +{ m_mode = mode; } void HtmlDocExtractor::applyFormatting(const bool format) { m_formatContents = format; } @@ -67,11 +58,8 @@ void HtmlDocExtractor::applyFormatting(const bool format) QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QString &mark) const { QString contents = getContentsByMarks(html, mark + QLatin1String("-brief"), mark); - if (!contents.isEmpty() && m_formatContents) { + if (!contents.isEmpty() && m_formatContents) contents.remove(QLatin1String("<a href=\"#details\">More...</a>")); - contents.prepend(QLatin1String("<nobr>")); - contents.append(QLatin1String("</nobr>")); - } processOutput(&contents); return contents; @@ -80,7 +68,7 @@ QString HtmlDocExtractor::getClassOrNamespaceBrief(const QString &html, const QS QString HtmlDocExtractor::getClassOrNamespaceDescription(const QString &html, const QString &mark) const { - if (!m_extendedExtraction) + if (m_mode == FirstParagraph) return getClassOrNamespaceBrief(html, mark); QString contents = getContentsByMarks(html, mark + QLatin1String("-description"), mark); @@ -191,11 +179,20 @@ void HtmlDocExtractor::processOutput(QString *html) const if (html->isEmpty()) return; - if (!m_extendedExtraction) { - int paragraph = html->indexOf(QLatin1String("</p>")); - if (paragraph != -1) { - paragraph += 4; - html->truncate(paragraph); + if (m_mode == FirstParagraph) { + int index = html->indexOf(QLatin1String("</p>")); + if (index > 0) { + if (html->at(index - 1) == QLatin1Char('.')) { + index += 4; + html->truncate(index); + } else { + // <p>Paragraphs similar to this. Example:</p> + index = html->lastIndexOf(QLatin1Char('.'), index); + if (index > 0) { + html->truncate(index); + html->append(QLatin1String(".</p>")); + } + } } else { // Some enumerations don't have paragraphs and just a table with the items. In such // cases the the html is cleared to avoid showing more that desired. @@ -216,45 +213,6 @@ void HtmlDocExtractor::processOutput(QString *html) const stripHeadings(html); stripImagens(html); stripEmptyParagraphs(html); - - if (!html->startsWith(QLatin1String("<nobr>"))) { - if (!m_extendedExtraction) { - if (!html->endsWith(QLatin1String(".</p>"))) { - // <p>For paragraphs similar to this. Example:</p> - const int lastDot = html->lastIndexOf(QLatin1Char('.')); - if (lastDot != -1) { - html->truncate(lastDot); - html->append(QLatin1String(".</p>")); - } - } - } - - const int noBreakLimit = 140; - const int paragraph = html->indexOf(QLatin1String("<p>")); - if (paragraph > 0 && paragraph <= noBreakLimit) { - html->insert(paragraph, QLatin1String("</nobr>")); - html->prepend(QLatin1String("<nobr>")); - } - } - } - - if (m_extendedExtraction && m_lengthReference > -1 && html->length() > m_lengthReference) { - if (m_truncateAtParagraph) { - const int nextBegin = html->indexOf(QLatin1String("<p>"), m_lengthReference); - QRegExp exp = createMinimalExp(QLatin1String("</p>|<br />")); - const int previousEnd = html->lastIndexOf(exp, m_lengthReference); - if (nextBegin != -1 && previousEnd != -1) - html->truncate(qMin(nextBegin, previousEnd + exp.matchedLength())); - else if (nextBegin != -1 || previousEnd != -1) - html->truncate((nextBegin != -1? nextBegin : previousEnd + exp.matchedLength())); - } else { - html->truncate(m_lengthReference); - } - if (m_formatContents) { - if (html->endsWith(QLatin1String("<br />"))) - html->chop(6); - html->append(QLatin1String("<p>...</p>")); - } } } diff --git a/src/libs/utils/htmldocextractor.h b/src/libs/utils/htmldocextractor.h index a9acf79a5fd..8efa69b25e3 100644 --- a/src/libs/utils/htmldocextractor.h +++ b/src/libs/utils/htmldocextractor.h @@ -41,9 +41,12 @@ class QTCREATOR_UTILS_EXPORT HtmlDocExtractor public: HtmlDocExtractor(); - void extractFirstParagraphOnly(); - void extractExtendedContents(const int lengthReference, const bool truncateAtParagraph); + enum Mode { + FirstParagraph, + Extended + }; + void setMode(Mode mode); void applyFormatting(const bool format); QString getClassOrNamespaceBrief(const QString &html, const QString &mark) const; @@ -80,10 +83,8 @@ private: static void replaceTablesForSimpleLines(QString *html); static void replaceListsForSimpleLines(QString *html); - int m_lengthReference; - bool m_truncateAtParagraph; bool m_formatContents; - bool m_extendedExtraction; + Mode m_mode; }; } // namespace Utils diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp index cebf33c43e8..7f7346a83b2 100644 --- a/src/plugins/cppeditor/cpphoverhandler.cpp +++ b/src/plugins/cppeditor/cpphoverhandler.cpp @@ -106,11 +106,10 @@ void CppHoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos) } } -void CppHoverHandler::decorateToolTip(TextEditor::ITextEditor *editor) +void CppHoverHandler::decorateToolTip() { - CPPEditor *cppEditor = qobject_cast<CPPEditor *>(editor->widget()); - if (!cppEditor) - return; + if (Qt::mightBeRichText(toolTip())) + setToolTip(Qt::escape(toolTip())); const TextEditor::HelpItem &help = lastHelpItemIdentified(); if (help.isValid()) { @@ -120,11 +119,6 @@ void CppHoverHandler::decorateToolTip(TextEditor::ITextEditor *editor) appendToolTip(contents); else setToolTip(contents); - } else { - QString tip = Qt::escape(toolTip()); - tip.prepend(QLatin1String("<nobr>")); - tip.append(QLatin1String("</nobr>")); - setToolTip(tip); } addF1ToToolTip(); } diff --git a/src/plugins/cppeditor/cpphoverhandler.h b/src/plugins/cppeditor/cpphoverhandler.h index 5cce9980583..fe851176a12 100644 --- a/src/plugins/cppeditor/cpphoverhandler.h +++ b/src/plugins/cppeditor/cpphoverhandler.h @@ -55,7 +55,7 @@ public: private: virtual bool acceptEditor(Core::IEditor *editor); virtual void identifyMatch(TextEditor::ITextEditor *editor, int pos); - virtual void decorateToolTip(TextEditor::ITextEditor *editor); + virtual void decorateToolTip(); }; } // namespace Internal diff --git a/src/plugins/texteditor/basehoverhandler.cpp b/src/plugins/texteditor/basehoverhandler.cpp index ceaa26f55f2..89694bfc8fd 100644 --- a/src/plugins/texteditor/basehoverhandler.cpp +++ b/src/plugins/texteditor/basehoverhandler.cpp @@ -142,25 +142,18 @@ void BaseHoverHandler::process(ITextEditor *editor, int pos) { clear(); identifyMatch(editor, pos); - decorateToolTip(editor); + decorateToolTip(); } -void BaseHoverHandler::decorateToolTip(ITextEditor *editor) +void BaseHoverHandler::decorateToolTip() { - BaseTextEditor *baseEditor = baseTextEditor(editor); - if (!baseEditor) - return; + if (Qt::mightBeRichText(toolTip())) + setToolTip(Qt::escape(toolTip())); if (lastHelpItemIdentified().isValid()) { const QString &contents = lastHelpItemIdentified().extractContent(false); - if (!contents.isEmpty()) { + if (!contents.isEmpty()) appendToolTip(contents); - } else { - QString tip = Qt::escape(toolTip()); - tip.prepend(QLatin1String("<nobr>")); - tip.append(QLatin1String("</nobr>")); - setToolTip(tip); - } addF1ToToolTip(); } } diff --git a/src/plugins/texteditor/basehoverhandler.h b/src/plugins/texteditor/basehoverhandler.h index b0a627c7cfd..f5a52c2b10b 100644 --- a/src/plugins/texteditor/basehoverhandler.h +++ b/src/plugins/texteditor/basehoverhandler.h @@ -80,7 +80,7 @@ private: virtual bool acceptEditor(Core::IEditor *editor) = 0; virtual void identifyMatch(ITextEditor *editor, int pos) = 0; - virtual void decorateToolTip(ITextEditor *editor); + virtual void decorateToolTip(); virtual void operateTooltip(ITextEditor *editor, const QPoint &point); QString m_toolTip; diff --git a/src/plugins/texteditor/helpitem.cpp b/src/plugins/texteditor/helpitem.cpp index b4b766e52fc..33badec1c01 100644 --- a/src/plugins/texteditor/helpitem.cpp +++ b/src/plugins/texteditor/helpitem.cpp @@ -81,9 +81,9 @@ QString HelpItem::extractContent(bool extended) const { Utils::HtmlDocExtractor htmlExtractor; if (extended) - htmlExtractor.extractExtendedContents(1500, true); + htmlExtractor.setMode(Utils::HtmlDocExtractor::Extended); else - htmlExtractor.extractFirstParagraphOnly(); + htmlExtractor.setMode(Utils::HtmlDocExtractor::FirstParagraph); QString contents; QMap<QString, QUrl> helpLinks = Core::HelpManager::instance()->linksForIdentifier(m_helpId); diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index b513c7de8c2..e917e279e71 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -67,8 +67,8 @@ SOURCES += texteditorplugin.cpp \ tooltip/tooltip.cpp \ tooltip/tips.cpp \ tooltip/tipcontents.cpp \ - basehoverhandler.cpp \ tooltip/tipfactory.cpp \ + basehoverhandler.cpp \ helpitem.cpp HEADERS += texteditorplugin.h \ @@ -138,9 +138,10 @@ HEADERS += texteditorplugin.h \ tooltip/tooltip.h \ tooltip/tips.h \ tooltip/tipcontents.h \ - basehoverhandler.h \ + tooltip/reuse.h \ tooltip/effects.h \ tooltip/tipfactory.h \ + basehoverhandler.h \ helpitem.h FORMS += behaviorsettingspage.ui \ diff --git a/src/plugins/texteditor/tooltip/reuse.h b/src/plugins/texteditor/tooltip/reuse.h new file mode 100644 index 00000000000..0888486488a --- /dev/null +++ b/src/plugins/texteditor/tooltip/reuse.h @@ -0,0 +1,62 @@ +/************************************************************************** +** +** 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 TOOLTIPREUSE_H +#define TOOLTIPREUSE_H + +#include <QtCore/QPoint> +#include <QtCore/QRect> +#include <QtGui/QWidget> +#include <QtGui/QApplication> +#include <QtGui/QDesktopWidget> + +namespace TextEditor { +namespace Internal { + +inline int screenNumber(const QPoint &pos, QWidget *w) +{ + if (QApplication::desktop()->isVirtualDesktop()) + return QApplication::desktop()->screenNumber(pos); + else + return QApplication::desktop()->screenNumber(w); +} + +inline QRect screenGeometry(const QPoint &pos, QWidget *w) +{ +#ifdef Q_WS_MAC + return QApplication::desktop()->availableGeometry(screenNumber(pos, w)); +#else + return QApplication::desktop()->screenGeometry(screenNumber(pos, w)); +#endif +} + +} // namespace Internal +} // namespace TextEditor + +#endif // TOOLTIPREUSE_H diff --git a/src/plugins/texteditor/tooltip/tipfactory.cpp b/src/plugins/texteditor/tooltip/tipfactory.cpp index 435abbe6a91..4add0a024cf 100644 --- a/src/plugins/texteditor/tooltip/tipfactory.cpp +++ b/src/plugins/texteditor/tooltip/tipfactory.cpp @@ -47,9 +47,5 @@ Internal::QTipLabel *TipFactory::createTip(const TipContent &content, QWidget *w tip = new TextTip(w); else if (content.typeId() == ColorContent::COLOR_CONTENT_ID) tip = new ColorTip(w); - - if (tip) - tip->setContent(content); - return tip; } diff --git a/src/plugins/texteditor/tooltip/tips.cpp b/src/plugins/texteditor/tooltip/tips.cpp index 3492e5a2022..26b1d8ac3bc 100644 --- a/src/plugins/texteditor/tooltip/tips.cpp +++ b/src/plugins/texteditor/tooltip/tips.cpp @@ -29,6 +29,7 @@ #include "tips.h" #include "tipcontents.h" +#include "reuse.h" #include <QtCore/QRect> #include <QtGui/QColor> @@ -40,6 +41,8 @@ #include <QtGui/QTextDocument> #include <QtGui/QStylePainter> #include <QtGui/QStyleOptionFrame> +#include <QtGui/QResizeEvent> +#include <QtGui/QPaintEvent> namespace TextEditor { namespace Internal { @@ -75,7 +78,6 @@ void QTipLabel::setContent(const TipContent &content) if (m_tipContent) delete m_tipContent; m_tipContent = content.clone(); - configure(); } const TipContent &QTipLabel::content() const @@ -90,8 +92,11 @@ ColorTip::ColorTip(QWidget *parent) : QTipLabel(parent) ColorTip::~ColorTip() {} -void ColorTip::configure() +void ColorTip::configure(const QPoint &pos, QWidget *w) { + Q_UNUSED(pos) + Q_UNUSED(w) + update(); } @@ -138,17 +143,32 @@ TextTip::TextTip(QWidget *parent) : QTipLabel(parent) TextTip::~TextTip() {} -void TextTip::configure() +void TextTip::configure(const QPoint &pos, QWidget *w) { const QString &text = static_cast<const TextContent &>(content()).text(); - setWordWrap(Qt::mightBeRichText(text)); setText(text); - QFontMetrics fm(font()); - QSize extra(1, 0); + // Make it look good with the default ToolTip font on Mac, which has a small descent. + QFontMetrics fm(font()); + int extraHeight = 0; if (fm.descent() == 2 && fm.ascent() >= 11) - ++extra.rheight(); - resize(sizeHint() + extra); + ++extraHeight; + + // Try to find a nice width without unnecessary wrapping. + setWordWrap(false); + int tipWidth = sizeHint().width(); + const int screenWidth = screenGeometry(pos, w).width(); + const int maxDesiredWidth = int(screenWidth * .5); + if (tipWidth > maxDesiredWidth) { + setWordWrap(true); + tipWidth = sizeHint().width(); + // If the width is still too large (maybe due to some extremely long word which prevents + // wrapping), the tip is truncated according to the screen. + if (tipWidth > screenWidth) + tipWidth = screenWidth - 10; + } + + resize(tipWidth, heightForWidth(tipWidth) + extraHeight); } bool TextTip::handleContentReplacement(const TipContent &content) const diff --git a/src/plugins/texteditor/tooltip/tips.h b/src/plugins/texteditor/tooltip/tips.h index 806124be306..6b6c0c0e346 100644 --- a/src/plugins/texteditor/tooltip/tips.h +++ b/src/plugins/texteditor/tooltip/tips.h @@ -56,7 +56,7 @@ public: void setContent(const TextEditor::TipContent &content); const TextEditor::TipContent &content() const; - virtual void configure() = 0; + virtual void configure(const QPoint &pos, QWidget *w) = 0; virtual bool handleContentReplacement(const TextEditor::TipContent &content) const = 0; private: @@ -70,7 +70,7 @@ public: ColorTip(QWidget *parent); virtual ~ColorTip(); - virtual void configure(); + virtual void configure(const QPoint &pos, QWidget *w); virtual bool handleContentReplacement(const TipContent &content) const; private: @@ -86,7 +86,7 @@ public: TextTip(QWidget *parent); virtual ~TextTip(); - virtual void configure(); + virtual void configure(const QPoint &pos, QWidget *w); virtual bool handleContentReplacement(const TipContent &content) const; private: diff --git a/src/plugins/texteditor/tooltip/tooltip.cpp b/src/plugins/texteditor/tooltip/tooltip.cpp index c5f34107cb6..751e2bd7680 100644 --- a/src/plugins/texteditor/tooltip/tooltip.cpp +++ b/src/plugins/texteditor/tooltip/tooltip.cpp @@ -32,11 +32,11 @@ #include "tipcontents.h" #include "tipfactory.h" #include "effects.h" +#include "reuse.h" #include <QtCore/QString> #include <QtGui/QColor> #include <QtGui/QApplication> -#include <QtGui/QDesktopWidget> #include <QtGui/QKeyEvent> #include <QtGui/QMouseEvent> @@ -67,7 +67,8 @@ void ToolTip::show(const QPoint &pos, const TipContent &content, QWidget *w, con #ifndef Q_WS_WIN m_tip = m_tipFactory->createTip(content, w); #else - m_tip = m_tipFactory->createTip(content, QApplication::desktop()->screen(tipScreen(pos,w))); + m_tip = m_tipFactory->createTip( + content, QApplication::desktop()->screen(Internal::screenNumber(pos, w))); #endif setUp(pos, content, w, rect); qApp->installEventFilter(this); @@ -96,7 +97,6 @@ bool ToolTip::acceptShow(const TipContent &content, localPos = w->mapFromGlobal(pos); if (tipChanged(localPos, content, w)) { setUp(pos, content, w, rect); - m_tip->setContent(content); } return false; } @@ -117,8 +117,12 @@ bool ToolTip::validateContent(const TipContent &content) void ToolTip::setUp(const QPoint &pos, const TipContent &content, QWidget *w, const QRect &rect) { + m_tip->setContent(content); + m_tip->configure(pos, w); + placeTip(pos, w); setTipRect(w, rect); + if (m_hideDelayTimer.isActive()) m_hideDelayTimer.stop(); m_showTimer.start(content.showTime()); @@ -187,12 +191,7 @@ void ToolTip::hideTipImmediately() 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 - + QRect screen = Internal::screenGeometry(pos, w); QPoint p = pos; p += QPoint(2, #ifdef Q_WS_WIN @@ -218,14 +217,6 @@ void ToolTip::placeTip(const QPoint &pos, QWidget *w) 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) { switch (event->type()) { @@ -266,3 +257,13 @@ bool ToolTip::eventFilter(QObject *o, QEvent *event) } return false; } + +QFont ToolTip::font() const +{ + return QApplication::font("QTipLabel"); +} + +void ToolTip::setFont(const QFont &font) +{ + QApplication::setFont(font, "QTipLabel"); +} diff --git a/src/plugins/texteditor/tooltip/tooltip.h b/src/plugins/texteditor/tooltip/tooltip.h index 512ba610cf1..28997b60bbd 100644 --- a/src/plugins/texteditor/tooltip/tooltip.h +++ b/src/plugins/texteditor/tooltip/tooltip.h @@ -36,6 +36,7 @@ #include <QtCore/QObject> #include <QtCore/QTimer> #include <QtCore/QRect> +#include <QtGui/QFont> /* * In its current form QToolTip is not extensible. So this is an attempt to provide a more @@ -76,6 +77,9 @@ public: void hide(); bool isVisible() const; + QFont font() const; + void setFont(const QFont &font); + virtual bool eventFilter(QObject *o, QEvent *event); private slots: @@ -88,7 +92,6 @@ private: bool tipChanged(const QPoint &pos, const TipContent &content, QWidget *w) const; void setTipRect(QWidget *w, const QRect &rect); void placeTip(const QPoint &pos, QWidget *w); - int tipScreen(const QPoint &pos, QWidget *w) const; void showTip(); void hideTipWithDelay(); -- GitLab