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