From: Guillaume Munch Date: Sun, 5 Jun 2016 19:35:35 +0000 (+0200) Subject: Line breaks in tooltips X-Git-Tag: 2.3.0alpha1~1355 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=059ca0f691d567171e7772c1d56a7f96e7867339;p=features.git Line breaks in tooltips * 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 , 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 . --- diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index 074492b3d3..a8611f784d 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -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)); } diff --git a/src/frontends/qt4/Makefile.am b/src/frontends/qt4/Makefile.am index 103105e95c..190e8881ee 100644 --- a/src/frontends/qt4/Makefile.am +++ b/src/frontends/qt4/Makefile.am @@ -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 index 0000000000..d29012ca9b --- /dev/null +++ b/src/frontends/qt4/ToolTipFormatter.cpp @@ -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 + +#include "ToolTipFormatter.h" +#include "qt_helpers.h" + +#include +#include +#include +#include + + +//#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(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(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(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 index 0000000000..7b943ff83e --- /dev/null +++ b/src/frontends/qt4/ToolTipFormatter.h @@ -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 + +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 diff --git a/src/frontends/qt4/qt_helpers.cpp b/src/frontends/qt4/qt_helpers.cpp index 5515af57c5..a2c72172e1 100644 --- a/src/frontends/qt4/qt_helpers.cpp +++ b/src/frontends/qt4/qt_helpers.cpp @@ -42,6 +42,9 @@ #include #include #include +#include +#include +#include #include #include @@ -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(""))) + 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("" + "" + "
%2
") + .arg(QString::number(int(best_width) + 1), text); + return text; +} + + } // namespace lyx diff --git a/src/frontends/qt4/qt_helpers.h b/src/frontends/qt4/qt_helpers.h index d4afc78a33..9f239a8131 100644 --- a/src/frontends/qt4/qt_helpers.h +++ b/src/frontends/qt4/qt_helpers.h @@ -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 , 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 . +QString formatToolTip(QString text, int width = 30); + + } // namespace lyx #endif // QTHELPERS_H