Warning, file /plasma/systemsettings/app/ToolTips/tooltipmanager.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *   SPDX-FileCopyrightText: 2008 Konstantin Heil <konst.heil@stud.uni-heidelberg.de>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "tooltipmanager.h"
0008 
0009 #include "MenuItem.h"
0010 
0011 #include <QAbstractItemView>
0012 #include <QApplication>
0013 #include <QGridLayout>
0014 #include <QLabel>
0015 #include <QRect>
0016 #include <QScrollBar>
0017 #include <QStyle>
0018 #include <QTimer>
0019 
0020 #include <KIconLoader>
0021 #include <KLocalizedString>
0022 #include <KToolTipWidget>
0023 
0024 class IconLoaderSingleton
0025 {
0026 public:
0027     IconLoaderSingleton() = default;
0028 
0029     KIconLoader self;
0030 };
0031 
0032 Q_GLOBAL_STATIC(IconLoaderSingleton, privateIconLoaderSelf)
0033 
0034 class ToolTipManager::Private
0035 {
0036 public:
0037     Private()
0038         : tooltip(nullptr)
0039         , view(nullptr)
0040         , timer(nullptr)
0041         , delay(QApplication::style()->styleHint(QStyle::SH_ToolTip_WakeUpDelay))
0042     {
0043     }
0044 
0045     KToolTipWidget *tooltip = nullptr;
0046     QAbstractItemView *view = nullptr;
0047     QTimer *timer = nullptr;
0048     QModelIndex item;
0049     QRect itemRect;
0050     int delay;
0051 };
0052 
0053 ToolTipManager::ToolTipManager(QAbstractItemView *parent)
0054     : QObject(parent)
0055     , d(new ToolTipManager::Private)
0056 {
0057     d->view = parent;
0058     d->tooltip = new KToolTipWidget(d->view);
0059     d->tooltip->setHideDelay(0);
0060 
0061     connect(parent, &QAbstractItemView::viewportEntered, this, &ToolTipManager::hideToolTip);
0062     connect(parent, &QAbstractItemView::entered, this, &ToolTipManager::requestToolTip);
0063 
0064     d->timer = new QTimer(this);
0065     d->timer->setSingleShot(true);
0066     connect(d->timer, &QTimer::timeout, this, &ToolTipManager::prepareToolTip);
0067 
0068     // When the mousewheel is used, the items don't get a hovered indication
0069     // (Qt-issue #200665). To assure that the tooltip still gets hidden,
0070     // the scrollbars are observed.
0071     connect(parent->horizontalScrollBar(), &QAbstractSlider::valueChanged, this, &ToolTipManager::hideToolTip);
0072     connect(parent->verticalScrollBar(), &QAbstractSlider::valueChanged, this, &ToolTipManager::hideToolTip);
0073 
0074     d->view->viewport()->installEventFilter(this);
0075 }
0076 
0077 ToolTipManager::~ToolTipManager()
0078 {
0079     delete d;
0080 }
0081 
0082 bool ToolTipManager::eventFilter(QObject *watched, QEvent *event)
0083 {
0084     if (watched == d->view->viewport()) {
0085         switch (event->type()) {
0086         case QEvent::Leave:
0087         case QEvent::MouseButtonPress:
0088             hideToolTip();
0089             break;
0090         default:
0091             break;
0092         }
0093     }
0094 
0095     return QObject::eventFilter(watched, event);
0096 }
0097 
0098 void ToolTipManager::requestToolTip(const QModelIndex &index)
0099 {
0100     // only request a tooltip for the name column and when no selection or
0101     // drag & drop operation is done (indicated by the left mouse button)
0102     if (!(QApplication::mouseButtons() & Qt::LeftButton)) {
0103         d->tooltip->hide();
0104 
0105         d->itemRect = d->view->visualRect(index);
0106         const QPoint pos = d->view->viewport()->mapToGlobal(d->itemRect.topLeft());
0107         d->itemRect.moveTo(pos);
0108         d->item = index;
0109         d->timer->start(d->delay);
0110     } else {
0111         hideToolTip();
0112     }
0113 }
0114 
0115 void ToolTipManager::hideToolTip()
0116 {
0117     d->timer->stop();
0118     d->tooltip->hideLater();
0119 }
0120 
0121 void ToolTipManager::prepareToolTip()
0122 {
0123     showToolTip(d->item);
0124 }
0125 
0126 void ToolTipManager::showToolTip(const QModelIndex &menuItem)
0127 {
0128     if (QApplication::mouseButtons() & Qt::LeftButton) {
0129         return;
0130     }
0131 
0132     QWidget *tip = createTipContent(menuItem);
0133 
0134     d->tooltip->showBelow(d->itemRect, tip, d->view->nativeParentWidget()->windowHandle());
0135 
0136     connect(d->tooltip, &KToolTipWidget::hidden, tip, &QObject::deleteLater);
0137 }
0138 
0139 QWidget *ToolTipManager::createTipContent(QModelIndex item)
0140 {
0141     auto loader = KIconLoader::global();
0142     const QSize dialogIconSize = QSize(loader->currentSize(KIconLoader::Dialog), loader->currentSize(KIconLoader::Dialog));
0143     const QSize toolbarIconSize = QSize(loader->currentSize(KIconLoader::MainToolbar), loader->currentSize(KIconLoader::MainToolbar));
0144 
0145     auto tipContent = new QWidget();
0146     auto tipLayout = new QGridLayout();
0147     tipLayout->setAlignment(Qt::AlignLeft);
0148 
0149     QLayout *primaryLine = generateToolTipLine(&item, tipContent, dialogIconSize, true);
0150     primaryLine->setAlignment(Qt::AlignLeft);
0151     tipLayout->addLayout(primaryLine, 0, 0, Qt::AlignLeft);
0152 
0153     for (int done = 0; d->view->model()->rowCount(item) > done; done = 1 + done) {
0154         QModelIndex childItem = d->view->model()->index(done, 0, item);
0155         QLayout *subLine = generateToolTipLine(&childItem, tipContent, toolbarIconSize, false);
0156         subLine->setAlignment(Qt::AlignLeft);
0157         tipLayout->addLayout(subLine, done + 2, 0, Qt::AlignLeft);
0158     }
0159 
0160     tipLayout->setVerticalSpacing(tipContent->fontMetrics().height() / 3);
0161     tipContent->setLayout(tipLayout);
0162 
0163     if (d->view->model()->rowCount(item) > 0) {
0164         auto separatorLine = new QFrame(tipContent);
0165         separatorLine->setFrameStyle(QFrame::HLine);
0166         tipLayout->addWidget(separatorLine, 1, 0);
0167     }
0168 
0169     return tipContent;
0170 }
0171 
0172 QLayout *ToolTipManager::generateToolTipLine(QModelIndex *item, QWidget *toolTip, QSize iconSize, bool comment)
0173 {
0174     // Get MenuItem
0175     auto menuItem = d->view->model()->data(*item, Qt::UserRole).value<MenuItem *>();
0176 
0177     QString text = menuItem->name();
0178     if (comment) {
0179         text = QStringLiteral("<b>%1</b>").arg(menuItem->name());
0180 
0181         // Generate text
0182         text += QStringLiteral("<br />");
0183         if (!menuItem->comment().isEmpty()) {
0184             text += menuItem->comment();
0185         } else {
0186             int childCount = d->view->model()->rowCount(*item);
0187             text += i18np("Contains 1 item", "Contains %1 items", childCount);
0188         }
0189     }
0190     auto textLabel = new QLabel(toolTip);
0191     textLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0192     textLabel->setForegroundRole(QPalette::ToolTipText);
0193     textLabel->setText(text);
0194 
0195     // Get icon
0196     QPalette pal = textLabel->palette();
0197     for (auto state : {QPalette::Active, QPalette::Inactive, QPalette::Disabled}) {
0198         pal.setBrush(state, QPalette::WindowText, pal.toolTipText());
0199         pal.setBrush(state, QPalette::Window, pal.toolTipBase());
0200     }
0201 
0202     privateIconLoaderSelf->self.setCustomPalette(pal);
0203 
0204     QIcon icon = KDE::icon(menuItem->iconName(), &privateIconLoaderSelf->self);
0205     auto iconLabel = new QLabel(toolTip);
0206     iconLabel->setPixmap(icon.pixmap(iconSize));
0207     iconLabel->setMaximumSize(iconSize);
0208 
0209     // Generate layout
0210     auto layout = new QHBoxLayout();
0211     layout->setSpacing(textLabel->fontMetrics().height() / 3);
0212     layout->setAlignment(Qt::AlignLeft);
0213     layout->addWidget(iconLabel, Qt::AlignLeft);
0214     layout->addWidget(textLabel, Qt::AlignLeft);
0215 
0216     return layout;
0217 }
0218 
0219 #include "moc_tooltipmanager.cpp"