kde-workspace/kinfocenter/ToolTips/tooltipmanager.cpp
Ivailo Monev 66f6104da4 generic: replace QLabel with the new KPixmapWidget class
or how to optimize painting of pixmaps and icons

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2023-08-30 05:10:09 +03:00

232 lines
8 KiB
C++

/*******************************************************************************
* Copyright (C) 2008 by Konstantin Heil <konst.heil@stud.uni-heidelberg.de> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
*******************************************************************************/
#include "tooltipmanager.h"
#include "kcmtreeitem.h"
#include "sidepanel.h"
#include "ktooltip.h"
#include <QRect>
#include <QLabel>
#include <QTimer>
#include <QPainter>
#include <QtGui/qevent.h>
#include <QScrollBar>
#include <QGridLayout>
#include <QApplication>
#include <QDesktopWidget>
#include <QAbstractItemView>
#ifdef Q_WS_X11
#include <QtGui/qx11info_x11.h>
#endif
#include <KIcon>
#include <KColorScheme>
#include <KPixmapWidget>
class ToolTipManager::Private
{
public:
Private() :
view(0),
timer(0)
{ }
QAbstractItemView* view;
QTimer* timer;
QModelIndex item;
QRect itemRect;
};
ToolTipManager::ToolTipManager(QAbstractItemView* parent)
: QObject(parent)
, d(new ToolTipManager::Private)
{
d->view = parent;
connect(parent, SIGNAL(viewportEntered()), this, SLOT(hideToolTip()));
connect(parent, SIGNAL(entered(QModelIndex)), this, SLOT(requestToolTip(QModelIndex)));
d->timer = new QTimer(this);
d->timer->setSingleShot(true);
connect(d->timer, SIGNAL(timeout()), this, SLOT(prepareToolTip()));
// When the mousewheel is used, the items don't get a hovered indication
// (Qt-issue #200665). To assure that the tooltip still gets hidden,
// the scrollbars are observed.
connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip()));
connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip()));
d->view->viewport()->installEventFilter(this);
}
ToolTipManager::~ToolTipManager()
{
delete d;
}
bool ToolTipManager::eventFilter(QObject* watched, QEvent* event)
{
if ( watched == d->view->viewport() ) {
switch ( event->type() ) {
case QEvent::Leave:
case QEvent::MouseButtonPress:
hideToolTip();
break;
case QEvent::ToolTip:
return true;
default:
break;
}
}
return QObject::eventFilter(watched, event);
}
void ToolTipManager::requestToolTip(const QModelIndex& index)
{
// only request a tooltip for the name column and when no selection or
// drag & drop operation is done (indicated by the left mouse button)
if ( !(QApplication::mouseButtons() & Qt::LeftButton) ) {
KToolTip::hideTip();
d->itemRect = d->view->visualRect(index);
const QPoint pos = d->view->viewport()->mapToGlobal(d->itemRect.topLeft());
d->itemRect.moveTo(pos);
d->item = index;
d->timer->start(50);
} else {
hideToolTip();
}
}
void ToolTipManager::hideToolTip()
{
d->timer->stop();
KToolTip::hideTip();
}
void ToolTipManager::prepareToolTip()
{
showToolTip( d->item );
}
void ToolTipManager::showToolTip( const QModelIndex& menuItem )
{
if (QApplication::mouseButtons() & Qt::LeftButton) {
return;
}
QWidget * tip = createTipContent( menuItem );
// calculate the x- and y-position of the tooltip
const QSize size = tip->sizeHint();
const QRect desktop = QApplication::desktop()->screenGeometry( d->itemRect.bottomRight() );
// d->itemRect defines the area of the item, where the tooltip should be
// shown. Per default the tooltip is shown in the bottom right corner.
// If the tooltip content exceeds the desktop borders, it must be assured that:
// - the content is fully visible
// - the content is not drawn inside d->itemRect
const bool hasRoomToLeft = (d->itemRect.left() - size.width() >= desktop.left());
const bool hasRoomToRight = (d->itemRect.right() + size.width() <= desktop.right());
const bool hasRoomAbove = (d->itemRect.top() - size.height() >= desktop.top());
const bool hasRoomBelow = (d->itemRect.bottom() + size.height() <= desktop.bottom());
if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) {
delete tip;
tip = 0;
return;
}
int x = 0;
int y = 0;
if (hasRoomBelow || hasRoomAbove) {
x = QCursor::pos().x() + 16; // TODO: use mouse pointer width instead of the magic value of 16
if (x + size.width() >= desktop.right()) {
x = desktop.right() - size.width();
}
y = hasRoomBelow ? d->itemRect.bottom() : d->itemRect.top() - size.height();
} else {
Q_ASSERT(hasRoomToLeft || hasRoomToRight);
x = hasRoomToRight ? d->itemRect.right() : d->itemRect.left() - size.width();
// Put the tooltip at the bottom of the screen. The x-coordinate has already
// been adjusted, so that no overlapping with d->itemRect occurs.
y = desktop.bottom() - size.height();
}
// the ownership of tip is transferred to KToolTip
KToolTip::showTip(QPoint(x, y), tip);
}
QWidget * ToolTipManager::createTipContent( const QModelIndex& item )
{
QWidget * tipContent = new QWidget();
QGridLayout* tipLayout = new QGridLayout();
QLayout * primaryLine = generateToolTipLine( item, tipContent, QSize(32,32), true );
tipLayout->addLayout( primaryLine, 0, 0 );
for ( int done = 0; d->view->model()->rowCount( item ) > done; done = 1 + done ) {
QModelIndex childItem = d->view->model()->index( done, 0, item );
QLayout * subLine = generateToolTipLine( childItem, tipContent, QSize(24,24), false );
tipLayout->addLayout( subLine, done + 2, 0 );
}
tipLayout->setVerticalSpacing( 0 );
tipContent->setLayout( tipLayout );
if( d->view->model()->rowCount( item ) > 0 ) {
QFrame * separatorLine = new QFrame( tipContent );
separatorLine->setFrameStyle( QFrame::HLine );
tipLayout->addWidget( separatorLine, 1, 0 );
}
return tipContent;
}
QLayout * ToolTipManager::generateToolTipLine( const QModelIndex & item, QWidget * toolTip, const QSize& iconSize, bool comment )
{
SidePanel *sidePanel = static_cast<SidePanel*>(d->view);
KcmTreeItem *menuItem = static_cast<KcmTreeItem*>( sidePanel->mapToProxySource(item).internalPointer() );
QString text = menuItem->data();
if ( comment ) {
text = QString( "<b>%1</b>" ).arg( menuItem->data() );
}
QLabel * textLabel = new QLabel( toolTip );
textLabel->setForegroundRole(QPalette::ToolTipText);
textLabel->setText( text );
// Get icon
KIcon icon( menuItem->icon() );
KPixmapWidget * iconWidget = new KPixmapWidget( toolTip );
iconWidget->setPixmap( icon.pixmap(iconSize) );
iconWidget->setMaximumSize( iconSize );
// Generate layout
QHBoxLayout * layout = new QHBoxLayout();
layout->addWidget( iconWidget );
layout->addWidget( textLabel );
return layout;
}