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