]> git.lyx.org Git - features.git/commitdiff
Line breaks in tooltips
authorGuillaume Munch <gm@lyx.org>
Sun, 5 Jun 2016 19:35:35 +0000 (21:35 +0200)
committerGuillaume Munch <gm@lyx.org>
Sun, 3 Jul 2016 15:28:45 +0000 (17:28 +0200)
* New function formatToolTip(QString):

Format text for display as a ToolTip, breaking at lines of a certain
width. Note: this function is expensive. Better call it in a delayed manner,
i.e. not to fill in a model (see for instance the function
ToolTipFormatter::eventFilter).

* Install a global event filter that formats tooltips on-the-fly

Inspired from
https://github.com/bitcoin/bitcoin/pull/1090/commits/3793fa09ff920fc720dfad3738f105d2c9563662
but much improved.

When is formatToolTip called automatically? Whenever the tooltip is not already
rich text beginning with <html>, and is defined by the following functions:
 * QWidget::setToolTip(),
 * QAbstractItemModel::setData(..., Qt::ToolTipRole),
 * Inset::toolTip() (added in one of the subsequent patches)

In other words, tooltips can use Qt html and the tooltip will still be correctly
broken. Moreover, it is possible to specify an entirely custom tooltip (not
subject to automatic formatting) by giving it in its entirety, i.e. starting
with <html>.

src/frontends/qt4/GuiApplication.cpp
src/frontends/qt4/Makefile.am
src/frontends/qt4/ToolTipFormatter.cpp [new file with mode: 0644]
src/frontends/qt4/ToolTipFormatter.h [new file with mode: 0644]
src/frontends/qt4/qt_helpers.cpp
src/frontends/qt4/qt_helpers.h

index 074492b3d3c1a7bb06d747c91c16750bb5e3c373..a8611f784dc49e265a783b50cd46af95e55f21d5 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "GuiApplication.h"
 
+#include "ToolTipFormatter.h"
 #include "ColorCache.h"
 #include "ColorSet.h"
 #include "GuiClipboard.h"
@@ -1081,6 +1082,9 @@ GuiApplication::GuiApplication(int & argc, char ** argv)
        // This is clearly not enough in a time where we use threads for
        // document preview and/or export. 20 should be OK.
        QThreadPool::globalInstance()->setMaxThreadCount(20);
+
+       // make sure tooltips are formatted
+       installEventFilter(new ToolTipFormatter(this));
 }
 
 
index 103105e95c868861893340e1d7b91288f426c8f9..190e8881ee7905c3ea9ac27134ac0aee84be81f8 100644 (file)
@@ -153,6 +153,7 @@ SOURCEFILES = \
        TocModel.cpp \
        TocWidget.cpp \
        Toolbars.cpp \
+       ToolTipFormatter.cpp \
        Validator.cpp 
 
 NOMOCHEADER = \
@@ -259,6 +260,7 @@ MOCHEADER = \
        PanelStack.h \
        TocModel.h \
        TocWidget.h \
+       ToolTipFormatter.h \
        Validator.h
 
 UIFILES = \
diff --git a/src/frontends/qt4/ToolTipFormatter.cpp b/src/frontends/qt4/ToolTipFormatter.cpp
new file mode 100644 (file)
index 0000000..d29012c
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * \file ToolTipFormatter.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Guillaume Munch
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "ToolTipFormatter.h"
+#include "qt_helpers.h"
+
+#include <QAbstractItemView>
+#include <QTextDocument>
+#include <QTextLayout>
+#include <QToolTip>
+
+
+//#include "support/debug.h"
+//#include "support/lstrings.h"
+
+
+namespace lyx {
+namespace frontend {
+
+
+ToolTipFormatter::ToolTipFormatter(QObject * parent) : QObject(parent) {}
+
+
+bool ToolTipFormatter::eventFilter(QObject * o, QEvent * e)
+{
+       if (e->type() != QEvent::ToolTip)
+               return false;
+
+       // Format the tooltip of the widget being considered.
+       QWidget * w = qobject_cast<QWidget *>(o);
+       if (!w)
+               return false;
+       // Unchanged if empty or already formatted
+       w->setToolTip(formatToolTip(w->toolTip()));
+
+       // Now, if the tooltip is for an item in a QListView or a QTreeView,
+       // then the widget above was probably not the one with the tooltip.
+       // Check if the parent is a QAbstractItemView.
+       QAbstractItemView * iv = qobject_cast<QAbstractItemView *>(w->parent());
+       if (!iv)
+               return false;
+       // In this case, the item is retrieved from the position of the QHelpEvent
+       // on the screen.
+       QPoint pos = static_cast<QHelpEvent *>(e)->pos();
+       QModelIndex item = iv->indexAt(pos);
+       QVariant data = iv->model()->data(item, Qt::ToolTipRole);
+       if (data.isValid() && data.typeName() == toqstr("QString"))
+               // Unchanged if empty or already formatted
+               iv->model()->setData(item, formatToolTip(data.toString()),
+                                    Qt::ToolTipRole);
+       // We must let the tooltip event reach its destination.
+       return false;
+}
+
+
+} // namespace frontend
+} // namespace lyx
+
+#include "moc_ToolTipFormatter.cpp"
diff --git a/src/frontends/qt4/ToolTipFormatter.h b/src/frontends/qt4/ToolTipFormatter.h
new file mode 100644 (file)
index 0000000..7b943ff
--- /dev/null
@@ -0,0 +1,37 @@
+// -*- C++ -*-
+/**
+ * \file ToolTipFormatter.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Guillaume Munch
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef TOOLTIPFORMATTER_H
+#define TOOLTIPFORMATTER_H
+
+#include <QObject>
+
+class QEvent;
+
+namespace lyx {
+namespace frontend {
+
+
+/// This event filter intercepts ToolTip events, to format any tooltip
+/// appropriately before display.
+class ToolTipFormatter : public QObject {
+       Q_OBJECT
+public:
+       ToolTipFormatter(QObject * parent);
+protected:
+       bool eventFilter(QObject * o, QEvent * e);
+};
+
+
+} // namespace frontend
+} // namespace lyx
+
+#endif // TOOLTIPFORMATTER_H
index 5515af57c511a51b996b54d51160a934fbe58e28..a2c72172e1ec73caeb001eaddd9f2af7c5e62192 100644 (file)
@@ -42,6 +42,9 @@
 #include <QLocale>
 #include <QPalette>
 #include <QSet>
+#include <QTextLayout>
+#include <QTextDocument>
+#include <QToolTip>
 
 #include <algorithm>
 #include <fstream>
@@ -647,4 +650,37 @@ QString guiName(string const & type, BufferParams const & bp)
 }
 
 
+QString formatToolTip(QString text, int em)
+{
+       // 1. QTooltip activates word wrapping only if mightBeRichText()
+       //    is true. So we convert the text to rich text.
+       //
+       // 2. The default width is way too small. Setting the width is tricky; first
+       //    one has to compute the ideal width, and then force it with special
+       //    html markup.
+
+       // do nothing if empty or already formatted
+       if (text.isEmpty() || text.startsWith(QString("<html>")))
+               return text;
+       // Convert to rich text if it is not already
+       if (!Qt::mightBeRichText(text))
+               text = Qt::convertFromPlainText(text, Qt::WhiteSpaceNormal);
+       // Compute desired width in pixels
+       QFont const font = QToolTip::font();
+       int const px_width = em * QFontMetrics(font).width("M");
+       // Determine the ideal width of the tooltip
+    QTextDocument td("");
+    td.setHtml(text);
+    td.setDefaultFont(QToolTip::font());
+    td.setTextWidth(px_width);
+    double best_width = td.idealWidth();
+       // Set the line wrapping with appropriate width
+       return QString("<html><body><table><tr>"
+                      "<td align=justify width=%1>%2</td>"
+                      "</tr></table></body></html>")
+               .arg(QString::number(int(best_width) + 1), text);
+       return text;
+}
+
+
 } // namespace lyx
index d4afc78a338b7e615729a6debd1a377a4d5391e4..9f239a8131ff594e9a59b92f5c88cddfc9a0347e 100644 (file)
@@ -195,6 +195,24 @@ QString changeExtension(QString const & oldname, QString const & ext);
 /// parameter.
 QString guiName(std::string const & type, BufferParams const & bp);
 
+/// Format \param text for display as a ToolTip, breaking at lines of \param
+/// width ems. Note: this function is expensive. Better call it in a delayed
+/// manner, i.e. not to fill in a model (see for instance the function
+/// ToolTipFormatter::eventFilter).
+///
+/// When is it called automatically? Whenever the tooltip is not already rich
+/// text beginning with <html>, and is defined by the following functions:
+///  - QWidget::setToolTip(),
+///  - QAbstractItemModel::setData(..., Qt::ToolTipRole),
+///  - Inset::toolTip()
+///
+/// In other words, tooltips can use Qt html, and the tooltip will still be
+/// correctly broken. Moreover, it is possible to specify an entirely custom
+/// tooltip (not subject to automatic formatting) by giving it in its entirety,
+/// i.e. starting with <html>.
+QString formatToolTip(QString text, int width = 30);
+
+
 } // namespace lyx
 
 #endif // QTHELPERS_H