File indexing completed on 2024-05-12 05:46:36

0001 /***************************************************************************
0002  *   Copyright 2011 Marco Martin <mart@kde.org>                            *
0003  *   Copyright 2011 Artur Duque de Souza <asouza@kde.org>                  *
0004  *   Copyright 2013 Sebastian Kügler <sebas@kde.org>                       *
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  *   This program is distributed in the hope that it will be useful,       *
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0014  *   GNU General Public License for more details.                          *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU General Public License     *
0017  *   along with this program; if not, write to the                         *
0018  *   Free Software Foundation, Inc.,                                       *
0019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
0020  ***************************************************************************/
0021 
0022 #include "tooltip.h"
0023 #include "tooltipdialog.h"
0024 
0025 #include <QQmlEngine>
0026 #include <QQuickItem>
0027 #include <QDebug>
0028 
0029 #include "framesvgitem.h"
0030 #include <kwindoweffects.h>
0031 #include <KDirWatch>
0032 
0033 ToolTipDialog *ToolTip::s_dialog = nullptr;
0034 int ToolTip::s_dialogUsers  = 0;
0035 
0036 ToolTip::ToolTip(QQuickItem *parent)
0037     : QQuickItem(parent),
0038       m_tooltipsEnabledGlobally(false),
0039       m_containsMouse(false),
0040       m_location(Plasma::Types::Floating),
0041       m_textFormat(Qt::AutoText),
0042       m_active(true),
0043       m_interactive(false),
0044       m_usingDialog(false)
0045 {
0046     setAcceptHoverEvents(true);
0047     setFiltersChildMouseEvents(true);
0048 
0049     m_showTimer = new QTimer(this);
0050     m_showTimer->setSingleShot(true);
0051     connect(m_showTimer, &QTimer::timeout, this, &ToolTip::showToolTip);
0052 
0053     loadSettings();
0054 
0055     const QString configFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/plasmarc");
0056     KDirWatch::self()->addFile(configFile);
0057     QObject::connect(KDirWatch::self(), &KDirWatch::created, this, &ToolTip::settingsChanged);
0058     QObject::connect(KDirWatch::self(), &KDirWatch::dirty, this, &ToolTip::settingsChanged);
0059 }
0060 
0061 ToolTip::~ToolTip()
0062 {
0063     if (s_dialog && s_dialog->owner() == this) {
0064         s_dialog->setVisible(false);
0065     }
0066 
0067     if (m_usingDialog) {
0068         --s_dialogUsers;
0069     }
0070 
0071     if (s_dialogUsers == 0) {
0072         delete s_dialog;
0073         s_dialog = nullptr;
0074     }
0075 }
0076 
0077 void ToolTip::settingsChanged(const QString &file)
0078 {
0079     if (!file.endsWith(QLatin1String("plasmarc"))) {
0080         return;
0081     }
0082 
0083     KSharedConfig::openConfig(QStringLiteral("plasmarc"))->reparseConfiguration();
0084     loadSettings();
0085 }
0086 
0087 void ToolTip::loadSettings()
0088 {
0089     KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "PlasmaToolTips");
0090     m_interval = cfg.readEntry("Delay", 700);
0091     m_tooltipsEnabledGlobally = (m_interval > 0);
0092 }
0093 
0094 QQuickItem *ToolTip::mainItem() const
0095 {
0096     return m_mainItem.data();
0097 }
0098 
0099 ToolTipDialog *ToolTip::tooltipDialogInstance()
0100 {
0101     if (!s_dialog) {
0102         s_dialog = new ToolTipDialog;
0103         s_dialogUsers = 1;
0104     }
0105 
0106     if (!m_usingDialog) {
0107         s_dialogUsers++;
0108         m_usingDialog = true;
0109     }
0110 
0111     return s_dialog;
0112 }
0113 
0114 void ToolTip::setMainItem(QQuickItem *mainItem)
0115 {
0116     if (m_mainItem.data() != mainItem) {
0117         m_mainItem = mainItem;
0118 
0119         emit mainItemChanged();
0120 
0121         if (!isValid() && s_dialog && s_dialog->owner() == this) {
0122             s_dialog->setVisible(false);
0123         }
0124     }
0125 }
0126 
0127 void ToolTip::showToolTip()
0128 {
0129     if (!m_active) {
0130         return;
0131     }
0132 
0133     emit aboutToShow();
0134 
0135     ToolTipDialog *dlg = tooltipDialogInstance();
0136 
0137     if (!mainItem()) {
0138         setMainItem(dlg->loadDefaultItem());
0139     }
0140 
0141     // Unset the dialog's old contents before reparenting the dialog.
0142     dlg->setMainItem(nullptr);
0143 
0144     Plasma::Types::Location location = m_location;
0145     if (m_location == Plasma::Types::Floating) {
0146         QQuickItem *p = parentItem();
0147         while (p) {
0148             if (p->property("location").isValid()) {
0149                 location = (Plasma::Types::Location)p->property("location").toInt();
0150                 break;
0151             }
0152             p = p->parentItem();
0153         }
0154     }
0155 
0156     if (mainItem()) {
0157         mainItem()->setProperty("toolTip", QVariant::fromValue(this));
0158         mainItem()->setVisible(true);
0159     }
0160 
0161     dlg->setOwner(this);
0162     dlg->setLocation(location);
0163     dlg->setVisualParent(this);
0164     dlg->setMainItem(mainItem());
0165     dlg->setInteractive(m_interactive);
0166     dlg->setVisible(true);
0167 }
0168 
0169 QString ToolTip::mainText() const
0170 {
0171     return m_mainText;
0172 }
0173 
0174 void ToolTip::setMainText(const QString &mainText)
0175 {
0176     if (mainText == m_mainText) {
0177         return;
0178     }
0179 
0180     m_mainText = mainText;
0181     emit mainTextChanged();
0182 
0183     if (!isValid() && s_dialog && s_dialog->owner() == this) {
0184         s_dialog->setVisible(false);
0185     }
0186 }
0187 
0188 QString ToolTip::subText() const
0189 {
0190     return m_subText;
0191 }
0192 
0193 void ToolTip::setSubText(const QString &subText)
0194 {
0195     if (subText == m_subText) {
0196         return;
0197     }
0198 
0199     m_subText = subText;
0200     emit subTextChanged();
0201 
0202     if (!isValid() && s_dialog && s_dialog->owner() == this) {
0203         s_dialog->setVisible(false);
0204     }
0205 }
0206 
0207 int ToolTip::textFormat() const
0208 {
0209     return m_textFormat;
0210 }
0211 
0212 void ToolTip::setTextFormat(int format)
0213 {
0214     if (m_textFormat == format) {
0215         return;
0216     }
0217 
0218     m_textFormat = format;
0219     emit textFormatChanged();
0220 }
0221 
0222 Plasma::Types::Location ToolTip::location() const
0223 {
0224     return m_location;
0225 }
0226 
0227 void ToolTip::setLocation(Plasma::Types::Location location)
0228 {
0229     if (m_location == location) {
0230         return;
0231     }
0232     m_location = location;
0233     emit locationChanged();
0234 }
0235 
0236 void ToolTip::setActive(bool active)
0237 {
0238     if (m_active == active) {
0239         return;
0240     }
0241 
0242     m_active = active;
0243     if (!active) {
0244         tooltipDialogInstance()->dismiss();
0245     }
0246     emit activeChanged();
0247 }
0248 
0249 void ToolTip::setInteractive(bool interactive)
0250 {
0251     if (m_interactive == interactive) {
0252         return;
0253     }
0254 
0255     m_interactive = interactive;
0256 
0257     emit interactiveChanged();
0258 }
0259 
0260 void ToolTip::hideToolTip()
0261 {
0262     m_showTimer->stop();
0263     tooltipDialogInstance()->dismiss();
0264 }
0265 
0266 QVariant ToolTip::icon() const
0267 {
0268     if (m_icon.isValid()) {
0269         return m_icon;
0270     } else {
0271         return QString();
0272     }
0273 }
0274 
0275 void ToolTip::setIcon(const QVariant &icon)
0276 {
0277     if (icon == m_icon) {
0278         return;
0279     }
0280 
0281     m_icon = icon;
0282     emit iconChanged();
0283 }
0284 
0285 QVariant ToolTip::image() const
0286 {
0287     if (m_image.isValid()) {
0288         return m_image;
0289     } else {
0290         return QString();
0291     }
0292 }
0293 
0294 void ToolTip::setImage(const QVariant &image)
0295 {
0296     if (image == m_image) {
0297         return;
0298     }
0299 
0300     m_image = image;
0301     emit imageChanged();
0302 }
0303 
0304 bool ToolTip::containsMouse() const
0305 {
0306     return m_containsMouse;
0307 }
0308 
0309 void ToolTip::setContainsMouse(bool contains)
0310 {
0311     if (m_containsMouse != contains) {
0312         m_containsMouse = contains;
0313         emit containsMouseChanged();
0314     }
0315     if (!contains) {
0316         tooltipDialogInstance()->dismiss();
0317     }
0318 }
0319 
0320 void ToolTip::hoverEnterEvent(QHoverEvent *event)
0321 {
0322     Q_UNUSED(event)
0323     setContainsMouse(true);
0324 
0325     if (!m_tooltipsEnabledGlobally) {
0326         return;
0327     }
0328 
0329     if (!isValid()) {
0330         return;
0331     }
0332 
0333     if (tooltipDialogInstance()->isVisible()) {
0334         // We signal the tooltipmanager that we're "potentially interested,
0335         // and ask to keep it open for a bit, so other items get the chance
0336         // to update the content before the tooltip hides -- this avoids
0337         // flickering
0338         // It need to be considered only when other items can deal with tooltip area
0339         if (m_active) {
0340             tooltipDialogInstance()->keepalive();
0341             //FIXME: showToolTip needs to be renamed in sync or something like that
0342             showToolTip();
0343         }
0344     } else {
0345         m_showTimer->start(m_interval);
0346     }
0347 }
0348 
0349 void ToolTip::hoverLeaveEvent(QHoverEvent *event)
0350 {
0351     Q_UNUSED(event)
0352     setContainsMouse(false);
0353     m_showTimer->stop();
0354 }
0355 
0356 bool ToolTip::childMouseEventFilter(QQuickItem *item, QEvent *event)
0357 {
0358     if (event->type() == QEvent::MouseButtonPress) {
0359         hideToolTip();
0360     }
0361     return QQuickItem::childMouseEventFilter(item, event);
0362 }
0363 
0364 bool ToolTip::isValid() const
0365 {
0366     return m_mainItem || !mainText().isEmpty() || !subText().isEmpty();
0367 }