21
21
#include "psitooltip.h"
24
23
#include <QApplication>
25
24
#include <QDesktopWidget>
27
25
#include <QKeyEvent>
31
#include <QStyleOption>
32
#include <QStylePainter>
34
28
#include <QToolTip>
35
#include <QAbstractTextDocumentLayout>
38
29
#include "private/qeffects_p.h"
40
#include "psirichtext.h"
42
//----------------------------------------------------------------------------
44
//----------------------------------------------------------------------------
46
class PsiTipLabel : public QFrame
50
PsiTipLabel(const QString& text, QWidget* parent);
52
static PsiTipLabel *instance;
54
// int heightForWidth(int w) const;
55
QSize sizeHint() const;
56
QSize minimumSizeHint() const;
57
bool eventFilter(QObject *, QEvent *);
59
QString theText() const;
61
QBasicTimer hideTimer, deleteTimer;
65
void enterEvent(QEvent*){ hideTip(); }
66
void timerEvent(QTimerEvent *e);
67
void paintEvent(QPaintEvent *e);
68
// QSize sizeForWidth(int w) const;
78
PsiTipLabel *PsiTipLabel::instance = 0;
80
PsiTipLabel::PsiTipLabel(const QString& text, QWidget* parent)
81
: QFrame(parent, Qt::ToolTip)
86
margin = 1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this);
87
setFrameStyle(QFrame::NoFrame);
89
// doc = new QTextDocument(this);
90
// QTextDocumentLayout is private in Qt4
91
// and it's impossible to set wrapping mode directly.
92
// So we create this QTextEdit instance and use its QTextDocument,
93
// just because QTextEdit can set the wrapping mode.
94
// Yes, this is crazy...
95
QTextEdit *edit = new QTextEdit(this);
97
edit->setWordWrapMode(QTextOption::WordWrap);
98
doc = edit->document();
99
doc->setUndoRedoEnabled(false);
100
doc->setDefaultFont(font());
106
if (Qt::mightBeRichText(theText_)) {
108
PsiRichText::install(doc);
109
PsiRichText::setText(doc, theText_);
112
doc->setPlainText(theText_);
116
qApp->installEventFilter(this);
117
hideTimer.start(10000, this);
118
setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / 255.0);
119
// No resources for this yet (unlike on Windows).
120
//QPalette pal(Qt::black, QColor(255,255,220),
121
// QColor(96,96,96), Qt::black, Qt::black,
122
// Qt::black, QColor(255,255,220));
123
setPalette(QToolTip::palette());
126
QString PsiTipLabel::theText() const
132
QSize PsiTipLabel::sizeForWidth(int w) const
136
int hextra = 2 * margin;
144
PsiRichText::ensureTextLayouted(doc, w);
145
const qreal oldTextWidth = doc->textWidth();
148
br = QRect(QPoint(0, 0), doc->size().toSize());
149
doc->setTextWidth(oldTextWidth);
151
QFontMetrics fm(font());
152
QSize extra(hextra + 1, vextra);
154
// Make it look good with the default ToolTip font on Mac, which has a small descent.
155
if (fm.descent() == 2 && fm.ascent() >= 11)
158
const QSize contentsSize(br.width() + hextra, br.height() + vextra);
162
QSize PsiTipLabel::sizeHint() const
164
QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
166
doc->rootFrame()->setFrameFormat(fmt);
167
// PsiRichText::ensureTextLayouted(doc, -1);
170
// br = QRect(QPoint(0, 0), doc->size().toSize());
171
// this way helps to fight empty space on the right:
172
QSize docSize = QSize(doc->idealWidth(), doc->size().toSize().height());
174
QFontMetrics fm(font());
175
QSize extra(2*margin + 2, 2*margin + 1); // "+" for tip's frame
176
// Make it look good with the default ToolTip font on Mac, which has a small descent.
177
if (fm.descent() == 2 && fm.ascent() >= 11)
180
return docSize + extra;
183
QSize PsiTipLabel::minimumSizeHint() const
186
// qWarning("PsiTipLabel::minimumSizeHint");
188
// QSize sh = sizeForWidth(-1);
189
// QSize msh(-1, -1);
191
// msh.rheight() = sizeForWidth(QWIDGETSIZE_MAX).height(); // height for one line
192
// msh.rwidth() = sizeForWidth(0).width(); // wrap ? size of biggest word : min doc size
193
// if (sh.height() < msh.height())
194
// msh.rheight() = sh.height();
199
void PsiTipLabel::paintEvent(QPaintEvent *)
201
QStylePainter p(this);
202
QStyleOptionFrame opt;
204
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
207
// stolen from QLabel::paintEvent
208
QPainter painter(this);
210
QRect cr = contentsRect();
211
cr.adjust(margin, margin, -margin, -margin);
213
PsiRichText::ensureTextLayouted(doc, width() - 2*margin);
214
QAbstractTextDocumentLayout *layout = doc->documentLayout();
215
// QRect lr = rect();
218
QAbstractTextDocumentLayout::PaintContext context;
220
// Adjust the palette
221
context.palette = palette();
222
if (foregroundRole() != QPalette::Text && isEnabled())
223
context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole()));
226
painter.translate(lr.x() + 1, lr.y() + 1);
227
painter.setClipRect(lr.translated(-lr.x() - 1, -lr.y() - 1));
228
layout->draw(&painter, context);
232
PsiTipLabel::~PsiTipLabel()
237
void PsiTipLabel::hideTip()
240
// timer based deletion to prevent animation
241
deleteTimer.start(250, this);
244
void PsiTipLabel::timerEvent(QTimerEvent *e)
246
if (e->timerId() == hideTimer.timerId())
248
else if (e->timerId() == deleteTimer.timerId())
252
bool PsiTipLabel::eventFilter(QObject *, QEvent *e)
255
case QEvent::KeyPress:
256
case QEvent::KeyRelease: {
257
int key = static_cast<QKeyEvent *>(e)->key();
258
Qt::KeyboardModifiers mody = static_cast<QKeyEvent *>(e)->modifiers();
260
if ((mody & Qt::KeyboardModifierMask)
261
|| (key == Qt::Key_Shift || key == Qt::Key_Control
262
|| key == Qt::Key_Alt || key == Qt::Key_Meta))
266
case QEvent::WindowActivate:
267
case QEvent::WindowDeactivate:
268
case QEvent::MouseButtonPress:
269
case QEvent::MouseButtonRelease:
270
case QEvent::MouseButtonDblClick:
271
case QEvent::FocusIn:
272
case QEvent::FocusOut:
31
#include "psitiplabel.h"
281
33
//----------------------------------------------------------------------------
282
34
// PsiToolTipHandler
298
50
void install(QWidget *widget)
300
widget->installEventFilter(this);
52
watchedWidgets_[widget] = true;
53
connect(widget, SIGNAL(destroyed(QObject*)), SLOT(widgetDestroyed(QObject*)));
57
void widgetDestroyed(QObject* obj)
59
QWidget* widget = static_cast<QWidget*>(obj);
60
watchedWidgets_.remove(widget);
64
QHash<QWidget*, bool> watchedWidgets_;
304
66
PsiToolTipHandler()
69
qApp->installEventFilter(this);
308
72
bool eventFilter(QObject *obj, QEvent *event)
310
74
if (event->type() == QEvent::ToolTip) {
311
QWidget *widget = dynamic_cast<QWidget *>(obj);
312
if (widget->isActiveWindow()) {
75
QWidget *widget = static_cast<QWidget *>(obj);
76
if (watchedWidgets_.contains(widget) &&
77
(widget->isActiveWindow() ||
78
widget->window()->testAttribute(Qt::WA_AlwaysShowToolTips))) {
313
79
QPoint pos = dynamic_cast<QHelpEvent *>(event)->globalPos();
314
80
PsiToolTip::showText(pos, widget->toolTip(), widget);
315
81
event->setAccepted(true);
325
91
// ToolTipPosition
326
92
//----------------------------------------------------------------------------
328
class ToolTipPosition
335
ToolTipPosition(const QPoint &_pos, QWidget *_w)
341
int getScreenNumber()
343
if (QApplication::desktop()->isVirtualDesktop())
344
return QApplication::desktop()->screenNumber(pos);
346
return QApplication::desktop()->screenNumber(w);
349
QPoint calculateTipPosition(QWidget *label)
94
ToolTipPosition::ToolTipPosition(const QPoint& _pos, const QWidget* _w)
100
int ToolTipPosition::getScreenNumber() const
102
if (QApplication::desktop()->isVirtualDesktop())
103
return QApplication::desktop()->screenNumber(pos);
105
return QApplication::desktop()->screenNumber(w);
108
QRect ToolTipPosition::screenRect() const
352
QRect screen = QApplication::desktop()->availableGeometry(getScreenNumber());
111
return QApplication::desktop()->availableGeometry(getScreenNumber());
354
QRect screen = QApplication::desktop()->screenGeometry(getScreenNumber());
113
return QApplication::desktop()->screenGeometry(getScreenNumber());
117
QPoint ToolTipPosition::calculateTipPosition(const QWidget* label) const
119
QRect screen = screenRect();
366
if (p.x() + label->width() > screen.x() + screen.width())
367
p.rx() -= 4 + label->width();
368
if (p.y() + label->height() > screen.y() + screen.height())
369
p.ry() -= 24 + label->height();
370
if (p.y() < screen.y())
372
if (p.x() + label->width() > screen.x() + screen.width())
373
p.setX(screen.x() + screen.width() - label->width());
374
if (p.x() < screen.x())
376
if (p.y() + label->height() > screen.y() + screen.height())
377
p.setY(screen.y() + screen.height() - label->height());
130
if (p.x() + label->width() > screen.x() + screen.width())
131
p.rx() -= 4 + label->width();
132
if (p.y() + label->height() > screen.y() + screen.height())
133
p.ry() -= 24 + label->height();
134
if (p.y() < screen.y())
136
if (p.x() + label->width() > screen.x() + screen.width())
137
p.setX(screen.x() + screen.width() - label->width());
138
if (p.x() < screen.x())
140
if (p.y() + label->height() > screen.y() + screen.height())
141
p.setY(screen.y() + screen.height() - label->height());
383
146
//----------------------------------------------------------------------------
385
148
//----------------------------------------------------------------------------
150
PsiToolTip::PsiToolTip()
151
: QObject(QCoreApplication::instance())
388
155
* QTipLabel's font is being determined at run-time. However QTipLabel's and
389
156
* QToolTip's font is the same, so we install our PsiTextLabel's font to be
405
172
* optional widget argument, \a w, is used to determine the
406
173
* appropriate screen on multi-head systems.
408
void PsiToolTip::showText(const QPoint &pos, const QString &text, QWidget *w)
175
void PsiToolTip::doShowText(const QPoint &pos, const QString &text, const QWidget *w)
410
if (text.isEmpty()) {
411
if (PsiTipLabel::instance)
412
PsiTipLabel::instance->hideTip();
177
if (text.isEmpty() || (w && !w->underMouse())) {
178
if (PsiTipLabel::instance())
179
PsiTipLabel::instance()->hideTip();
416
if (!w->underMouse())
183
QPointer<ToolTipPosition> calc(createTipPosition(pos, w));
185
if (PsiTipLabel::instance() && moveAndUpdateTipLabel(PsiTipLabel::instance(), text)) {
186
updateTipLabel(PsiTipLabel::instance(), text);
419
ToolTipPosition calc(pos, w);
420
if (PsiTipLabel::instance && PsiTipLabel::instance->theText() == text) {
421
188
// fancy moving tooltip effect
422
PsiTipLabel::instance->move(calc.calculateTipPosition(PsiTipLabel::instance));
189
PsiTipLabel::instance()->move(calc->calculateTipPosition(PsiTipLabel::instance()));
426
bool preventAnimation = (PsiTipLabel::instance != 0);
193
bool preventAnimation = (PsiTipLabel::instance() != 0);
428
195
installPsiToolTipFont();
429
QFrame *label = new PsiTipLabel(text, QApplication::desktop()->screen(calc.getScreenNumber()));
430
label->move(calc.calculateTipPosition(label));
196
QFrame *label = createTipLabel(text, QApplication::desktop()->screen(calc->getScreenNumber()));
197
label->move(calc->calculateTipPosition(label));
432
199
if ( QApplication::isEffectEnabled(Qt::UI_AnimateTooltip) == false || preventAnimation)