File indexing completed on 2025-04-27 03:58:31

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-23-03
0007  * Description : A tool tip widget which follows cursor movements.
0008  *               Tool tip content is displayed without delay.
0009  *
0010  * SPDX-FileCopyrightText: 2007-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2009-2010 by Andi Clemens <andi dot clemens at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dcursortracker.h"
0018 
0019 // Qt includes
0020 
0021 #include <QStyle>
0022 #include <QStylePainter>
0023 #include <QStyleOptionFrame>
0024 #include <QFrame>
0025 #include <QMouseEvent>
0026 #include <QPointer>
0027 #include <QTimer>
0028 #include <QToolTip>
0029 
0030 namespace Digikam
0031 {
0032 
0033 class Q_DECL_HIDDEN DCursorTracker::Private
0034 {
0035 
0036 public:
0037 
0038     explicit Private()
0039       : alignment    (Qt::AlignCenter),
0040         enable       (true),
0041         keepOpen     (false),
0042         autoHideTimer(nullptr),
0043         parent       (nullptr)
0044     {
0045     }
0046 
0047     Qt::Alignment     alignment;
0048     bool              enable;
0049     bool              keepOpen;
0050     QTimer*           autoHideTimer;
0051     QPointer<QWidget> parent;
0052 };
0053 
0054 DCursorTracker::DCursorTracker(const QString& txt, QWidget* const parent, Qt::Alignment align)
0055     : QLabel(txt, parent, Qt::ToolTip | Qt::BypassGraphicsProxyWidget),
0056       d(new Private)
0057 {
0058     setBackgroundRole(QPalette::ToolTipBase);
0059     setPalette(QToolTip::palette());
0060     ensurePolished();
0061     const int fwidth = 1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, this);
0062     setContentsMargins(fwidth, fwidth, fwidth, fwidth);
0063     setFrameStyle(QFrame::NoFrame);
0064     setAlignment(Qt::AlignLeft | Qt::AlignTop);
0065     setIndent(1);
0066     setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, nullptr, this) / 255.0);
0067 
0068     d->alignment = align;
0069     d->parent    = parent;
0070     d->parent->setMouseTracking(true);
0071     d->parent->installEventFilter(this);
0072 
0073     d->autoHideTimer = new QTimer(this);
0074     d->autoHideTimer->setSingleShot(true);
0075 
0076     connect(d->autoHideTimer, SIGNAL(timeout()),
0077             this, SLOT(slotAutoHide()));
0078 }
0079 
0080 DCursorTracker::~DCursorTracker()
0081 {
0082     delete d;
0083 }
0084 
0085 /**
0086  * Overload to make sure the widget size is correct
0087  */
0088 void DCursorTracker::setText(const QString& txt)
0089 {
0090     QLabel::setText(txt);
0091     adjustSize();
0092 }
0093 
0094 void DCursorTracker::setEnable(bool b)
0095 {
0096     d->enable = b;
0097 }
0098 
0099 void DCursorTracker::setKeepOpen(bool b)
0100 {
0101     d->keepOpen = b;
0102 }
0103 
0104 void DCursorTracker::setTrackerAlignment(Qt::Alignment alignment)
0105 {
0106     d->alignment = alignment;
0107 }
0108 
0109 void DCursorTracker::triggerAutoShow(int timeout)
0110 {
0111     if (canBeDisplayed())
0112     {
0113         show();
0114         moveToParent(d->parent);
0115         d->autoHideTimer->start(timeout);
0116     }
0117 }
0118 
0119 void DCursorTracker::refresh()
0120 {
0121     moveToParent(d->parent);
0122 }
0123 
0124 void DCursorTracker::slotAutoHide()
0125 {
0126     hide();
0127 }
0128 
0129 bool DCursorTracker::eventFilter(QObject* object, QEvent* e)
0130 {
0131     QWidget* const widget = static_cast<QWidget*>(object);
0132 
0133     switch (e->type())
0134     {
0135         case QEvent::MouseMove:
0136         {
0137             QMouseEvent* event = static_cast<QMouseEvent*>(e);
0138 
0139             if (canBeDisplayed() && (widget->rect().contains(event->pos()) ||
0140                                      (event->buttons() & Qt::LeftButton)))
0141             {
0142                 show();
0143                 moveToParent(widget);
0144             }
0145             else if (!d->keepOpen)
0146             {
0147                 hide();
0148             }
0149 
0150             break;
0151         }
0152 
0153         case QEvent::Leave:
0154         {
0155             if (!d->keepOpen)
0156             {
0157                 hide();
0158             }
0159 
0160             break;
0161         }
0162 
0163         default:
0164             break;
0165     }
0166 
0167     return false;
0168 }
0169 
0170 void DCursorTracker::moveToParent(QWidget* const parent)
0171 {
0172     if (!parent)
0173     {
0174         return;
0175     }
0176 
0177     switch (d->alignment)
0178     {
0179         case Qt::AlignLeft:
0180         {
0181             QPoint p = parent->mapToGlobal(QPoint(0, 0));
0182             int y    = p.y() - height();
0183             move(p.x(), (y < 0) ? (p.y() + parent->height()) : y);
0184             break;
0185         }
0186 
0187         case Qt::AlignRight:
0188         {
0189             QPoint p = parent->mapToGlobal(QPoint(parent->width(), 0));
0190             int y    = p.y() - height();
0191             move(p.x()-width(), (y < 0) ? (p.y() + parent->height()) : y);
0192             break;
0193         }
0194 
0195         case Qt::AlignCenter:
0196         default:
0197         {
0198             QPoint p = parent->mapToGlobal(QPoint(parent->width()/2, 0));
0199             int y    = p.y() - height();
0200             move(p.x()-width()/2, (y < 0) ? (p.y() + parent->height()) : y);
0201             break;
0202         }
0203     }
0204 }
0205 
0206 void DCursorTracker::paintEvent(QPaintEvent* e)
0207 {
0208     QStylePainter p(this);
0209     QStyleOptionFrame opt;
0210     opt.initFrom(this);
0211     p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
0212     p.end();
0213 
0214     QLabel::paintEvent(e);
0215 }
0216 
0217 bool DCursorTracker::canBeDisplayed()
0218 {
0219     return (d->enable && d->parent->isVisible());
0220 }
0221 
0222 } // namespace Digikam
0223 
0224 #include "moc_dcursortracker.cpp"