From: Peter Penz Date: Fri, 22 Aug 2008 21:47:22 +0000 (+0000) Subject: tooltip improvements (patch provided by Laurens Vanhove): X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/ad0a321b7dfd57d7d666ac87ef44813f8e16129e?ds=sidebyside tooltip improvements (patch provided by Laurens Vanhove): * do a slightly delayed generation of previews to avoid expensive thumbnail creation when shortly hovering items * prevent a resizing of the tooltip if the preview cannot be generated within a specific timeout CCMAIL: laurens@nature-helps.com svn path=/trunk/KDE/kdebase/apps/; revision=851104 --- diff --git a/src/dolphintooltip.cpp b/src/dolphintooltip.cpp index 4b7031360..84eb2ed70 100644 --- a/src/dolphintooltip.cpp +++ b/src/dolphintooltip.cpp @@ -28,44 +28,6 @@ const int PREVIEW_WIDTH = 256; const int PREVIEW_HEIGHT = 256; -DolphinToolTipItem::DolphinToolTipItem(const KFileItem& fileItem) : - KToolTipItem(KIcon(fileItem.iconName()), fileItem.getToolTipText(), UserType) -{ - - //if (icon().actualSize(QSize(256,256)).width() != PREVIEW_WIDTH) - /*{ - QPixmap paddedImage(QSize(PREVIEW_WIDTH, PREVIEW_HEIGHT)); - paddedImage.fill(Qt::transparent); - QPainter painter(&paddedImage); - KIcon kicon(fileItem.iconName()); - painter.drawPixmap((PREVIEW_WIDTH - 128) / 2, (PREVIEW_HEIGHT - 128) / 2, kicon.pixmap(QSize(128,128))); - setData(Qt::DecorationRole, QIcon(paddedImage)); - }*/ - - // Initiate the preview job. - KFileItemList fileItems; - fileItems << fileItem; - KIO::PreviewJob* job = KIO::filePreview(fileItems, PREVIEW_WIDTH, PREVIEW_HEIGHT ); - connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), - this, SLOT(setPreview(const KFileItem&, const QPixmap&))); -} - -DolphinToolTipItem::~DolphinToolTipItem() -{ -} - -void DolphinToolTipItem::setPreview(const KFileItem& fileItem, const QPixmap& preview) -{ - Q_UNUSED(fileItem); - /* QPixmap paddedImage(QSize(PREVIEW_WIDTH, PREVIEW_HEIGHT)); - paddedImage.fill(Qt::transparent); - QPainter painter(&paddedImage); - KIcon kicon(fileItem.iconName()); - painter.drawPixmap((PREVIEW_WIDTH - preview.width()) / 2, (PREVIEW_HEIGHT - preview.height()) / 2, preview); - setData(Qt::DecorationRole, QIcon(paddedImage));*/ - setData(Qt::DecorationRole, QIcon(preview)); -} - DolphinBalloonTooltipDelegate::DolphinBalloonTooltipDelegate() { } diff --git a/src/dolphintooltip.h b/src/dolphintooltip.h index 5d36112e7..568e3ea78 100644 --- a/src/dolphintooltip.h +++ b/src/dolphintooltip.h @@ -31,16 +31,6 @@ class KFileItem; class QPixmap; -class DolphinToolTipItem : public QObject, public KToolTipItem -{ - Q_OBJECT -public: - DolphinToolTipItem(const KFileItem& fileItem); - virtual ~DolphinToolTipItem(); -private slots: - void setPreview(const KFileItem& fileItem, const QPixmap& preview); -}; - class DolphinBalloonTooltipDelegate : public KFormattedBalloonTipDelegate { public: @@ -50,4 +40,4 @@ public: virtual QSize sizeHint(const KStyleOptionToolTip* option, const KToolTipItem* item) const; virtual void paint(QPainter* painter, const KStyleOptionToolTip* option, const KToolTipItem* item) const; }; -#endif \ No newline at end of file +#endif diff --git a/src/tooltipmanager.cpp b/src/tooltipmanager.cpp index 687df218f..e2d2688df 100644 --- a/src/tooltipmanager.cpp +++ b/src/tooltipmanager.cpp @@ -25,12 +25,19 @@ #include #include +#include #include #include #include #include +const int PREVIEW_WIDTH = 256; +const int PREVIEW_HEIGHT = 256; +const int ICON_WIDTH = 128; +const int ICON_HEIGHT = 128; +const int PREVIEW_DELAY = 250; + K_GLOBAL_STATIC(DolphinBalloonTooltipDelegate, g_delegate) ToolTipManager::ToolTipManager(QAbstractItemView* parent, @@ -40,8 +47,16 @@ ToolTipManager::ToolTipManager(QAbstractItemView* parent, m_dolphinModel(0), m_proxyModel(model), m_timer(0), + m_previewTimer(0), + m_waitOnPreviewTimer(0), m_item(), - m_itemRect() + m_itemRect(), + m_preview(), + m_generatingPreview(), + m_previewIsLate(), + m_previewPass(), + m_emptyRenderedKToolTipItem(), + m_pix() { KToolTip::setToolTipDelegate(g_delegate); @@ -54,9 +69,25 @@ ToolTipManager::ToolTipManager(QAbstractItemView* parent, m_timer = new QTimer(this); m_timer->setSingleShot(true); connect(m_timer, SIGNAL(timeout()), - this, SLOT(showToolTip())); + this, SLOT(prepareToolTip())); + + m_previewTimer = new QTimer(this); + m_previewTimer->setSingleShot(true); + connect(m_previewTimer, SIGNAL(timeout()), + this, SLOT(startPreviewJob())); + + m_waitOnPreviewTimer = new QTimer(this); + m_waitOnPreviewTimer->setSingleShot(true); + connect(m_waitOnPreviewTimer, SIGNAL(timeout()), + this, SLOT(prepareToolTip())); m_view->viewport()->installEventFilter(this); + + m_preview = false; + m_generatingPreview = false; + m_previewIsLate = false; + m_previewPass = 0; + m_pix = QPixmap(); } ToolTipManager::~ToolTipManager() @@ -89,6 +120,14 @@ void ToolTipManager::requestToolTip(const QModelIndex& index) const QModelIndex dirIndex = m_proxyModel->mapToSource(index); m_item = m_dolphinModel->itemForIndex(dirIndex); + // Only start the previewJob when the mouse has been over this item for 200msec, + // this prevents a lot of useless previewJobs (when passing rapidly over a lot of items). + m_previewTimer->start(200); + // reset these variables + m_preview = false; + m_previewIsLate = false; + m_previewPass = 0; + m_timer->start(500); } else { hideToolTip(); @@ -98,16 +137,54 @@ void ToolTipManager::requestToolTip(const QModelIndex& index) void ToolTipManager::hideToolTip() { m_timer->stop(); + m_previewTimer->stop(); KToolTip::hideTip(); } -void ToolTipManager::showToolTip() +void ToolTipManager::prepareToolTip() { - // TODO - create tip during requestTip(...) - this makes it more likely that the previews - // job will have completed by the time the tooltip is shown, resulting in less flicker. - // The memory management will be more intricate, though. - DolphinToolTipItem *tip = new DolphinToolTipItem(m_item); + if (m_generatingPreview) { + if (m_previewPass == 1) { + // We waited 250msec and the preview is still not finished, + // so show the toolTip with a transparent image of maximal width. + // When the preview finishes, m_previewIsLate will cause + // a direct update of the tooltip, via m_emptyRenderedKToolTipItem. + QPixmap paddedImage(QSize(PREVIEW_WIDTH, 32)); + m_previewIsLate = true; + paddedImage.fill(Qt::transparent); + KToolTipItem* toolTip = new KToolTipItem(paddedImage, m_item.getToolTipText()); + m_emptyRenderedKToolTipItem = toolTip; // make toolTip accessible everywhere + showToolTip(toolTip); + } + + ++m_previewPass; + m_waitOnPreviewTimer->start(250); + } else { + // The preview generation has finished, find out under which circumstances. + if (m_preview && m_previewIsLate) { + // We got a preview, but it is late, the tooltip has already been shown. + // So update the tooltip directly. + m_emptyRenderedKToolTipItem->setData(Qt::DecorationRole, KIcon(m_pix)); + return; + } + + KIcon icon; + if (m_preview) { + // We got a preview. + icon = KIcon(m_pix); + } else { + // No preview, so use an icon. + // Force a 128x128 icon, a 256x256 one is far too big. + icon = KIcon(KIcon(m_item.iconName()).pixmap(ICON_WIDTH, ICON_HEIGHT)); + } + KToolTipItem* toolTip = new KToolTipItem(icon, m_item.getToolTipText()); + showToolTip(toolTip); + } +} + +void ToolTipManager::showToolTip(KToolTipItem* tip) +{ KStyleOptionToolTip option; // TODO: get option content from KToolTip or add KToolTip::sizeHint() method option.direction = QApplication::layoutDirection(); @@ -119,7 +196,17 @@ void ToolTipManager::showToolTip() option.state = QStyle::State_None; option.decorationSize = QSize(32, 32); - const QSize size = g_delegate->sizeHint(&option, tip); + QSize size; + if (m_previewIsLate) { + QPixmap paddedImage(QSize(PREVIEW_WIDTH, PREVIEW_HEIGHT)); + KToolTipItem* maxiTip = new KToolTipItem(paddedImage, m_item.getToolTipText()); + size = g_delegate->sizeHint(&option, maxiTip); + delete maxiTip; + maxiTip = 0; + } + else { + size = g_delegate->sizeHint(&option, tip); + } const QRect desktop = QApplication::desktop()->screenGeometry(m_itemRect.bottomRight()); // m_itemRect defines the area of the item, where the tooltip should be @@ -159,4 +246,93 @@ void ToolTipManager::showToolTip() KToolTip::showTip(QPoint(x, y), tip); } + + +void ToolTipManager::startPreviewJob() +{ + m_generatingPreview = true; + KIO::PreviewJob* job = KIO::filePreview(KUrl::List() << m_item.url(), + PREVIEW_WIDTH, + PREVIEW_HEIGHT); + job->setIgnoreMaximumSize(true); + + connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), + this, SLOT(setPreviewPix(const KFileItem&, const QPixmap&))); + connect(job, SIGNAL(failed(const KFileItem&)), + this, SLOT(previewFailed(const KFileItem&))); +} + + +void ToolTipManager::setPreviewPix(const KFileItem& item, + const QPixmap& pixmap) +{ + // If the url has changed, don't do anything. + if ( m_item.url().url() == item.url().url() ) + { + QPixmap icon = pixmap; + // only paint borders if the pixmap is opaque + if (!icon.hasAlphaChannel()) + { + // border painter (adapted from dolphin/src/iconmanager.cpp) + // the frame is painted on top of the pixmap, this is needed to keep + // the text preview visually nice (the previewer adds ugly borders) + QPainter painter; + // make a buffer pixmap, tends to crash when 'icon' is directly painted ... + QPixmap framedIcon(icon.size().width(), pixmap.size().height()); + framedIcon.fill(); + const int width = framedIcon.width() - 1; + const int height = framedIcon.height() - 1; + + // draw the pixmap + painter.begin(&framedIcon); + painter.drawPixmap(0,0, icon); + + // draw the frame + painter.setRenderHint(QPainter::Antialiasing, false); + painter.setPen(QColor(0, 0, 0)); + painter.drawRect(0, 0, width-0, height-1); + painter.drawRect(1, 1, width-2, height-2); + painter.setPen(QColor(255, 255, 255)); + painter.drawRect(2, 2, width-4, height-4); + + painter.end(); + icon = framedIcon; + + // provide an alpha channel for the frame + QPixmap alphaChannel(icon.size()); + alphaChannel.fill(); + + QPainter alphaPainter(&alphaChannel); + alphaPainter.setBrush(Qt::NoBrush); + alphaPainter.setRenderHint(QPainter::Antialiasing, false); + alphaPainter.setPen(QColor(32, 32, 32)); + alphaPainter.drawRect(0, 0, width, height); + alphaPainter.setPen(QColor(64, 64, 64)); + alphaPainter.drawRect(1, 1, width-2, height-2); + + icon.setAlphaChannel(alphaChannel); + } + + if (m_previewIsLate) { + // always use the maximal width + QPixmap paddedImage(QSize(PREVIEW_WIDTH, icon.height())); + paddedImage.fill(Qt::transparent); + QPainter painter(&paddedImage); + painter.drawPixmap((PREVIEW_WIDTH - icon.width()) / 2, 0, icon); + m_pix = paddedImage; + } + else { + m_pix = icon; + } + m_preview = true; + m_generatingPreview = false; + } +} + +void ToolTipManager::previewFailed(const KFileItem& item) +{ + Q_UNUSED(item); + m_generatingPreview = false; +} + #include "tooltipmanager.moc" diff --git a/src/tooltipmanager.h b/src/tooltipmanager.h index 381b1b7a4..213f95871 100644 --- a/src/tooltipmanager.h +++ b/src/tooltipmanager.h @@ -30,6 +30,7 @@ class DolphinSortFilterProxyModel; class QAbstractItemView; class QModelIndex; class QTimer; +class KToolTipItem; /** * @brief Manages the tooltips for an item view. @@ -60,16 +61,30 @@ protected: private slots: void requestToolTip(const QModelIndex& index); void hideToolTip(); - void showToolTip(); + void prepareToolTip(); + void startPreviewJob(); + void setPreviewPix(const KFileItem& item, const QPixmap& pix); + void previewFailed(const KFileItem& item); private: + void showToolTip(KToolTipItem* tip); + QAbstractItemView* m_view; DolphinModel* m_dolphinModel; DolphinSortFilterProxyModel* m_proxyModel; QTimer* m_timer; + QTimer* m_previewTimer; + QTimer* m_waitOnPreviewTimer; KFileItem m_item; QRect m_itemRect; + bool m_preview; + bool m_generatingPreview; + bool m_previewIsLate; + int m_previewPass; + KToolTipItem* m_emptyRenderedKToolTipItem; + QPixmap m_pix; + }; #endif