1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquicktooltip_p.h"
5#include "qquickpopup_p_p.h"
6#include "qquickpopupitem_p_p.h"
7#include "qquickcontrol_p_p.h"
8
9#include <QtCore/qbasictimer.h>
10#include <QtQml/qqmlinfo.h>
11#include <QtQml/qqmlengine.h>
12#include <QtQml/qqmlcontext.h>
13#include <QtQml/qqmlcomponent.h>
14#include <QtQuick/qquickwindow.h>
15
16QT_BEGIN_NAMESPACE
17
18/*!
19 \qmltype ToolTip
20 \inherits Popup
21//! \nativetype QQuickToolTip
22 \inqmlmodule QtQuick.Controls
23 \since 5.7
24 \ingroup qtquickcontrols-popups
25 \brief Provides tool tips for any control.
26
27 A tool tip is a short piece of text that informs the user of a control's
28 function. It is typically placed above or below the parent control. The
29 tip text can be any \l{Rich Text Processing}{rich text} formatted string.
30
31 \image qtquickcontrols-tooltip.png
32
33 \section2 Attached Tool Tips
34
35 The most straight-forward way to setup tool tips for controls is to
36 specify \l text and \l {visible}{visibility} via attached properties.
37 The following example illustrates this approach:
38
39 \snippet qtquickcontrols-tooltip.qml 1
40
41 Under normal circumstances, there is only one tool tip visible at a time.
42 In order to save resources, all items that use the ToolTip attached property
43 share the same visual tool tip label instance. Even though the visuals are
44 shared, \c text, \c timeout and \c delay are stored individually for each item
45 that uses the respective attached property. However, multiple items cannot
46 make the shared tool tip visible at the same time. The shared tool tip is only
47 shown for the last item that made it visible. The position of the shared tool
48 tip is determined by the framework.
49
50 \include qquicktooltip.qdocinc customize-note
51
52 \section2 Delay and Timeout
53
54 Tool tips are typically transient in a sense that they are shown as a
55 result of a certain external event or user interaction, and they usually
56 hide after a certain timeout. It is possible to control the delay when
57 a tool tip is shown, and the timeout when it is hidden. This makes it
58 possible to implement varying strategies for showing and hiding tool tips.
59
60 For example, on touch screens, it is a common pattern to show a tool tip
61 as a result of pressing and holding down a button. The following example
62 demonstrates how to delay showing a tool tip until the press-and-hold
63 interval is reached. In this example, the tool tip hides as soon as the
64 button is released.
65
66 \snippet qtquickcontrols-tooltip-pressandhold.qml 1
67
68 With pointer devices, however, it might be desired to show a tool tip as
69 a result of hovering a button for a while. The following example presents
70 how to show a tool tip after hovering a button for a second, and hide it
71 after a timeout of five seconds.
72
73 \snippet qtquickcontrols-tooltip-hover.qml 1
74
75 \section2 Custom Tool Tips
76
77 Should one need more fine-grained control over the tool tip position, or
78 multiple simultaneous tool tip instances are needed, it is also possible
79 to create local tool tip instances. This way, it is possible to
80 \l {Customizing ToolTip}{customize} the tool tip, and the whole \l Popup
81 API is available. The following example presents a tool tip that presents
82 the value of a slider when the handle is dragged.
83
84 \image qtquickcontrols-tooltip-slider.png
85
86 \snippet qtquickcontrols-tooltip-slider.qml 1
87
88 \sa {Customizing ToolTip}, {Popup Controls},
89 {QtQuick.Controls::Popup::closePolicy}{closePolicy}
90*/
91
92class QQuickToolTipPrivate : public QQuickPopupPrivate
93{
94 Q_DECLARE_PUBLIC(QQuickToolTip)
95
96public:
97 void startDelay();
98 void stopDelay();
99
100 void startTimeout();
101 void stopTimeout();
102
103 void opened() override;
104
105 Qt::WindowFlags popupWindowType() const override;
106
107 QPalette defaultPalette() const override { return QQuickTheme::palette(scope: QQuickTheme::ToolTip); }
108
109 int delay = 0;
110 int timeout = -1;
111 QString text;
112 QBasicTimer delayTimer;
113 QBasicTimer timeoutTimer;
114};
115
116void QQuickToolTipPrivate::startDelay()
117{
118 Q_Q(QQuickToolTip);
119 if (delay > 0)
120 delayTimer.start(msec: delay, obj: q);
121}
122
123void QQuickToolTipPrivate::stopDelay()
124{
125 delayTimer.stop();
126}
127
128void QQuickToolTipPrivate::startTimeout()
129{
130 Q_Q(QQuickToolTip);
131 if (timeout > 0)
132 timeoutTimer.start(msec: timeout, obj: q);
133}
134
135void QQuickToolTipPrivate::stopTimeout()
136{
137 timeoutTimer.stop();
138}
139
140void QQuickToolTipPrivate::opened()
141{
142 QQuickPopupPrivate::opened();
143 startTimeout();
144}
145
146Qt::WindowFlags QQuickToolTipPrivate::popupWindowType() const
147{
148 return Qt::ToolTip;
149}
150
151QQuickToolTip::QQuickToolTip(QQuickItem *parent)
152 : QQuickPopup(*(new QQuickToolTipPrivate), parent)
153{
154 Q_D(QQuickToolTip);
155 d->allowVerticalFlip = true;
156 d->allowHorizontalFlip = true;
157 d->popupItem->setHoverEnabled(false); // QTBUG-63644
158}
159
160/*!
161 \qmlproperty string QtQuick.Controls::ToolTip::text
162
163 This property holds the text shown on the tool tip.
164*/
165QString QQuickToolTip::text() const
166{
167 Q_D(const QQuickToolTip);
168 return d->text;
169}
170
171void QQuickToolTip::setText(const QString &text)
172{
173 Q_D(QQuickToolTip);
174 if (d->text == text)
175 return;
176
177 d->text = text;
178 maybeSetAccessibleName(name: text);
179 emit textChanged();
180}
181
182/*!
183 \qmlproperty int QtQuick.Controls::ToolTip::delay
184
185 This property holds the delay (milliseconds) after which the tool tip is
186 shown. A tooltip with a negative delay is shown immediately. The default
187 value is \c 0.
188
189 \sa {Delay and Timeout}
190*/
191int QQuickToolTip::delay() const
192{
193 Q_D(const QQuickToolTip);
194 return d->delay;
195}
196
197void QQuickToolTip::setDelay(int delay)
198{
199 Q_D(QQuickToolTip);
200 if (d->delay == delay)
201 return;
202
203 d->delay = delay;
204 emit delayChanged();
205}
206
207/*!
208 \qmlproperty int QtQuick.Controls::ToolTip::timeout
209
210 This property holds the timeout (milliseconds) after which the tool tip is
211 hidden. A tooltip with a negative timeout does not hide automatically. The
212 default value is \c -1.
213
214 \sa {Delay and Timeout}
215*/
216int QQuickToolTip::timeout() const
217{
218 Q_D(const QQuickToolTip);
219 return d->timeout;
220}
221
222void QQuickToolTip::setTimeout(int timeout)
223{
224 Q_D(QQuickToolTip);
225 if (d->timeout == timeout)
226 return;
227
228 d->timeout = timeout;
229
230 if (timeout <= 0)
231 d->stopTimeout();
232 else if (isOpened())
233 d->startTimeout();
234
235 emit timeoutChanged();
236}
237
238void QQuickToolTip::setVisible(bool visible)
239{
240 Q_D(QQuickToolTip);
241 if (visible) {
242 if (!d->visible) {
243 // We are being made visible, and we weren't before.
244 if (d->delay > 0) {
245 d->startDelay();
246 return;
247 }
248 }
249 } else {
250 d->stopDelay();
251 }
252 QQuickPopup::setVisible(visible);
253}
254
255QQuickToolTipAttached *QQuickToolTip::qmlAttachedProperties(QObject *object)
256{
257 QQuickItem *item = qobject_cast<QQuickItem *>(o: object);
258 if (!item)
259 qmlWarning(me: object) << "ToolTip must be attached to an Item";
260
261 return new QQuickToolTipAttached(object);
262}
263
264/*!
265 \since QtQuick.Controls 2.5 (Qt 5.12)
266 \qmlmethod void QtQuick.Controls::ToolTip::show(string text, int timeout)
267
268 This method shows the \a text as a tooltip, which times out in
269 \a timeout (milliseconds).
270*/
271void QQuickToolTip::show(const QString &text, int ms)
272{
273 if (ms >= 0)
274 setTimeout(ms);
275 setText(text);
276 open();
277}
278
279/*!
280 \since QtQuick.Controls 2.5 (Qt 5.12)
281 \qmlmethod void QtQuick.Controls::ToolTip::hide()
282
283 This method hides the tooltip.
284*/
285void QQuickToolTip::hide()
286{
287 close();
288}
289
290QFont QQuickToolTip::defaultFont() const
291{
292 return QQuickTheme::font(scope: QQuickTheme::ToolTip);
293}
294
295void QQuickToolTip::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
296{
297 Q_D(QQuickToolTip);
298 QQuickPopup::itemChange(change, data);
299 if (change == QQuickItem::ItemVisibleHasChanged) {
300 if (!data.boolValue)
301 d->stopTimeout();
302
303 QQuickToolTipAttached *attached = qobject_cast<QQuickToolTipAttached *>(object: qmlAttachedPropertiesObject<QQuickToolTip>(obj: d->parentItem, create: false));
304 if (attached)
305 emit attached->visibleChanged();
306 }
307}
308
309void QQuickToolTip::timerEvent(QTimerEvent *event)
310{
311 Q_D(QQuickToolTip);
312 if (event->timerId() == d->timeoutTimer.timerId()) {
313 d->stopTimeout();
314 QQuickPopup::setVisible(false);
315 return;
316 }
317 if (event->timerId() == d->delayTimer.timerId()) {
318 d->stopDelay();
319 QQuickPopup::setVisible(true);
320 return;
321 }
322 QQuickPopup::timerEvent(event);
323}
324
325#if QT_CONFIG(accessibility)
326QAccessible::Role QQuickToolTip::accessibleRole() const
327{
328 return QAccessible::ToolTip;
329}
330
331void QQuickToolTip::accessibilityActiveChanged(bool active)
332{
333 Q_D(QQuickToolTip);
334 QQuickPopup::accessibilityActiveChanged(active);
335
336 if (active)
337 maybeSetAccessibleName(name: d->text);
338}
339#endif
340
341class QQuickToolTipAttachedPrivate : public QObjectPrivate
342{
343 Q_DECLARE_PUBLIC(QQuickToolTipAttached)
344
345public:
346 QQuickToolTip *instance(bool create) const;
347
348 int delay = 0;
349 int timeout = -1;
350 QString text;
351};
352
353QQuickToolTip *QQuickToolTipAttachedPrivate::instance(bool create) const
354{
355 QQmlEngine *engine = qmlEngine(parent);
356 if (!engine)
357 return nullptr;
358
359 static const char *name = "_q_QQuickToolTip";
360
361 QQuickToolTip *tip = engine->property(name).value<QQuickToolTip *>();
362 if (!tip && create) {
363 // TODO: a cleaner way to create the instance? QQml(Meta)Type?
364 QQmlComponent component(engine);
365 component.setData("import QtQuick.Controls; ToolTip { }", baseUrl: QUrl());
366
367 QObject *object = component.create();
368 if (object)
369 object->setParent(engine);
370
371 tip = qobject_cast<QQuickToolTip *>(object);
372 if (!tip)
373 delete object;
374 else
375 engine->setProperty(name, value: QVariant::fromValue(value: object));
376 }
377 return tip;
378}
379
380QQuickToolTipAttached::QQuickToolTipAttached(QObject *parent)
381 : QObject(*(new QQuickToolTipAttachedPrivate), parent)
382{
383}
384
385/*!
386 \qmlattachedproperty string QtQuick.Controls::ToolTip::text
387
388 This attached property holds the text of the shared tool tip.
389 The property can be attached to any item.
390
391 \sa {Attached Tool Tips}
392*/
393QString QQuickToolTipAttached::text() const
394{
395 Q_D(const QQuickToolTipAttached);
396 return d->text;
397}
398
399void QQuickToolTipAttached::setText(const QString &text)
400{
401 Q_D(QQuickToolTipAttached);
402 if (d->text == text)
403 return;
404
405 d->text = text;
406 emit textChanged();
407
408 if (isVisible())
409 d->instance(create: true)->setText(text);
410}
411
412/*!
413 \qmlattachedproperty int QtQuick.Controls::ToolTip::delay
414
415 This attached property holds the delay (milliseconds) of the shared tool tip.
416 The property can be attached to any item.
417
418 \sa {Attached Tool Tips}, {Delay and Timeout}
419*/
420int QQuickToolTipAttached::delay() const
421{
422 Q_D(const QQuickToolTipAttached);
423 return d->delay;
424}
425
426void QQuickToolTipAttached::setDelay(int delay)
427{
428 Q_D(QQuickToolTipAttached);
429 if (d->delay == delay)
430 return;
431
432 d->delay = delay;
433 emit delayChanged();
434
435 if (isVisible())
436 d->instance(create: true)->setDelay(delay);
437}
438
439/*!
440 \qmlattachedproperty int QtQuick.Controls::ToolTip::timeout
441
442 This attached property holds the timeout (milliseconds) of the shared tool tip.
443 The property can be attached to any item.
444
445 \sa {Attached Tool Tips}, {Delay and Timeout}
446*/
447int QQuickToolTipAttached::timeout() const
448{
449 Q_D(const QQuickToolTipAttached);
450 return d->timeout;
451}
452
453void QQuickToolTipAttached::setTimeout(int timeout)
454{
455 Q_D(QQuickToolTipAttached);
456 if (d->timeout == timeout)
457 return;
458
459 d->timeout = timeout;
460 emit timeoutChanged();
461
462 if (isVisible())
463 d->instance(create: true)->setTimeout(timeout);
464}
465
466/*!
467 \qmlattachedproperty bool QtQuick.Controls::ToolTip::visible
468
469 This attached property holds whether the shared tool tip is visible.
470 The property can be attached to any item.
471
472 \sa {Attached Tool Tips}
473*/
474bool QQuickToolTipAttached::isVisible() const
475{
476 Q_D(const QQuickToolTipAttached);
477 QQuickToolTip *tip = d->instance(create: false);
478 if (!tip)
479 return false;
480
481 return tip->isVisible() && tip->parentItem() == parent();
482}
483
484void QQuickToolTipAttached::setVisible(bool visible)
485{
486 Q_D(QQuickToolTipAttached);
487 if (visible)
488 show(text: d->text);
489 else
490 hide();
491}
492
493/*!
494 \qmlattachedproperty ToolTip QtQuick.Controls::ToolTip::toolTip
495
496 This attached property holds the shared tool tip instance. The property
497 can be attached to any item.
498
499 \sa {Attached Tool Tips}
500*/
501QQuickToolTip *QQuickToolTipAttached::toolTip() const
502{
503 Q_D(const QQuickToolTipAttached);
504 return d->instance(create: true);
505}
506
507/*!
508 \qmlattachedmethod void QtQuick.Controls::ToolTip::show(string text, int timeout = -1)
509
510 This attached method shows the shared tooltip with \a text and \a timeout (milliseconds).
511 The method can be attached to any item.
512
513 \sa {Attached Tool Tips}
514*/
515void QQuickToolTipAttached::show(const QString &text, int ms)
516{
517 Q_D(QQuickToolTipAttached);
518 QQuickToolTip *tip = d->instance(create: true);
519 if (!tip)
520 return;
521
522 tip->resetWidth();
523 tip->resetHeight();
524 tip->setParentItem(qobject_cast<QQuickItem *>(o: parent()));
525 tip->setDelay(d->delay);
526 tip->setTimeout(ms >= 0 ? ms : d->timeout);
527 tip->show(text);
528}
529
530/*!
531 \qmlattachedmethod void QtQuick.Controls::ToolTip::hide()
532
533 This attached method hides the shared tooltip. The method can be attached to any item.
534
535 \sa {Attached Tool Tips}
536*/
537void QQuickToolTipAttached::hide()
538{
539 Q_D(QQuickToolTipAttached);
540 QQuickToolTip *tip = d->instance(create: false);
541 if (!tip)
542 return;
543 // check the parent item to prevent unexpectedly closing tooltip by new created invisible tooltip
544 if (parent() == tip->parentItem())
545 tip->close();
546}
547
548QT_END_NAMESPACE
549
550#include "moc_qquicktooltip_p.cpp"
551

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/quicktemplates/qquicktooltip.cpp