plasma-framework/widgets/itembackground.cpp
Marco Martin c4bd7a9db4 don't set the parent item as target, since we don't know when target will die.
instead map the target position to the proper place, in our current parent's coordinates
BUG:211404

svn path=/trunk/KDE/kdelibs/; revision=1061426
2009-12-11 17:46:08 +00:00

324 lines
9.4 KiB
C++

/***************************************************************************
* Copyright 2009 by Alessandro Diaferia <alediaferia@gmail.com> *
* Copyright 2009 by Marco Martin <notmart@gmail.com> *
* *
* 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 "itembackground.h"
#include <QPainter>
#include <QTimer>
#include <QStyleOptionGraphicsItem>
#include <kdebug.h>
#include <plasma/framesvg.h>
#include <plasma/animator.h>
#include <plasma/theme.h>
namespace Plasma
{
class ItemBackgroundPrivate
{
public:
ItemBackgroundPrivate(ItemBackground *parent)
: q(parent),
target(0)
{}
void animationUpdate(qreal progress);
void targetDestroyed(QObject*);
void frameSvgChanged();
void refreshCurrentTarget();
ItemBackground * const q;
QGraphicsItem *target;
Plasma::FrameSvg *frameSvg;
QRectF oldGeometry;
QRectF newGeometry;
int animId;
qreal opacity;
bool fading;
bool fadeIn;
bool immediate;
};
ItemBackground::ItemBackground(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
d(new ItemBackgroundPrivate(this))
{
setCacheMode(DeviceCoordinateCache);
setFlag(ItemIsMovable, false);
setFlag(ItemIsSelectable, false);
setFlag(ItemIsFocusable, false);
d->frameSvg = new Plasma::FrameSvg(this);
d->animId = 0;
d->opacity = 1;
d->fading = false;
d->fadeIn = false;
d->immediate = false;
d->frameSvg->setImagePath("widgets/viewitem");
d->frameSvg->setEnabledBorders(Plasma::FrameSvg::AllBorders);
d->frameSvg->setCacheAllRenderedFrames(true);
d->frameSvg->setElementPrefix("hover");
qreal l, t, r, b;
d->frameSvg->getMargins(l, t, r, b);
setContentsMargins(l, t, r, b);
connect(d->frameSvg, SIGNAL(repaintNeeded()), this, SLOT(frameSvgChanged()));
setAcceptedMouseButtons(0);
setZValue(-800);
}
ItemBackground::~ItemBackground()
{
delete d;
}
QRectF ItemBackground::target() const
{
return d->newGeometry;
}
void ItemBackground::setTarget(const QRectF &newGeometry)
{
d->oldGeometry = geometry();
d->newGeometry = newGeometry;
if (!isVisible() && (!d->target || !d->target->isVisible())) {
setGeometry(d->newGeometry);
targetReached(newGeometry);
if (d->target) {
emit targetItemReached(d->target);
}
return;
}
QGraphicsWidget *pw = parentWidget();
if (pw) {
d->newGeometry = d->newGeometry.intersected(QRectF(QPointF(0,0), pw->size()));
}
if (d->animId != 0) {
Plasma::Animator::self()->stopCustomAnimation(d->animId);
}
if (d->target && d->target->isVisible() && !isVisible()) {
setGeometry(newGeometry);
d->oldGeometry = newGeometry;
show();
} else {
d->fading = false;
d->opacity = 1;
d->animId = Plasma::Animator::self()->customAnimation(
15, 250, Plasma::Animator::EaseInOutCurve, this, "animationUpdate");
}
}
void ItemBackground::setTargetItem(QGraphicsItem *target)
{
if (d->target && d->target != target) {
QObject *obj = 0;
if (d->target->isWidget()) {
obj = static_cast<QGraphicsWidget*>(d->target);
obj->removeEventFilter(this);
} else {
d->target->removeSceneEventFilter(this);
obj = dynamic_cast<QObject *>(d->target);
}
if (obj) {
disconnect(obj, 0, this, 0);
}
}
if (!target) {
hide();
}
bool newTarget = (d->target != target);
d->target = target;
if (target) {
setZValue(target->zValue() - 1);
if (parentItem() != target->parentItem()) {
QTransform t = transform();
setTransform(QTransform());
QRectF geom = mapToScene(geometry()).boundingRect();
setGeometry(mapFromScene(geom).boundingRect());
setTransform(t);
}
QRectF rect = target->boundingRect();
rect.moveTopLeft(mapToParent(mapFromScene(target->mapToScene(QPointF(0, 0)))));
setTarget(rect);
if (newTarget) {
QObject *obj = 0;
if (target->isWidget()) {
obj = static_cast<QGraphicsWidget*>(target);
obj->installEventFilter(this);
} else {
d->target->installSceneEventFilter(this);
obj = dynamic_cast<QObject *>(target);
}
if (obj) {
connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(targetDestroyed(QObject*)));
}
}
}
}
QGraphicsItem *ItemBackground::targetItem() const
{
return d->target;
}
bool ItemBackground::eventFilter(QObject *watched, QEvent *event)
{
QGraphicsWidget *targetWidget = static_cast<QGraphicsWidget *>(d->target);
if (watched == targetWidget) {
if (event->type() == QEvent::GraphicsSceneResize ||
event->type() == QEvent::GraphicsSceneMove) {
// We need to wait for the parent widget to resize...
QTimer::singleShot(0, this, SLOT(refreshCurrentTarget()) );
}
}
return false;
}
bool ItemBackground::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
if (watched == d->target) {
if (event->type() == QEvent::GraphicsSceneMove) {
QTimer::singleShot(0, this, SLOT(refreshCurrentTarget()) );
}
}
return false;
}
void ItemBackground::resizeEvent(QGraphicsSceneResizeEvent *)
{
d->frameSvg->resizeFrame(size());
}
QVariant ItemBackground::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (d->immediate) {
return value;
}
if (change == ItemVisibleChange) {
bool visible = value.toBool();
bool retVisible = visible;
if (visible == isVisible() || d->animId == 0) {
retVisible = true;
}
d->fading = true;
d->fadeIn = visible;
if (d->animId != 0) {
Plasma::Animator::self()->stopCustomAnimation(d->animId);
}
d->animId = Plasma::Animator::self()->customAnimation(
10, 250, Plasma::Animator::EaseInCurve, this, "animationUpdate");
return retVisible;
}
return value;
}
void ItemBackground::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget)
if (qFuzzyCompare(d->opacity, (qreal)1.0)) {
d->frameSvg->paintFrame(painter, option->rect.topLeft());
} else if (qFuzzyCompare(d->opacity+1, (qreal)1.0)) {
return;
} else {
QPixmap framePix = d->frameSvg->framePixmap();
QPainter bufferPainter(&framePix);
bufferPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
bufferPainter.fillRect(framePix.rect(), QColor(0, 0, 0, 255 * d->opacity));
bufferPainter.end();
painter->drawPixmap(framePix.rect(), framePix, framePix.rect());
}
}
void ItemBackgroundPrivate::animationUpdate(qreal progress)
{
if (progress == 1) {
animId = 0;
if ((!fading) || (fadeIn)) {
emit q->targetReached(newGeometry);
if (target) {
emit q->targetItemReached(target);
}
}
}
if (fading) {
opacity = fadeIn?progress:1-progress;
if (!fadeIn && qFuzzyCompare(opacity+1, (qreal)1.0)) {
immediate = true;
q->hide();
immediate = false;
}
} else if (oldGeometry != newGeometry) {
q->setGeometry(oldGeometry.x() + (newGeometry.x() - oldGeometry.x()) * progress,
oldGeometry.y() + (newGeometry.y() - oldGeometry.y()) * progress,
oldGeometry.width() + (newGeometry.width() - oldGeometry.width()) * progress,
oldGeometry.height() + (newGeometry.height() - oldGeometry.height()) * progress);
}
q->update();
emit q->animationStep(progress);
}
void ItemBackgroundPrivate::targetDestroyed(QObject*)
{
target = 0;
q->setTargetItem(0);
}
void ItemBackgroundPrivate::frameSvgChanged()
{
qreal l, t, r, b;
frameSvg->getMargins(l, t, r, b);
q->setContentsMargins(l, t, r, b);
q->update();
emit q->appearanceChanged();
}
void ItemBackgroundPrivate::refreshCurrentTarget()
{
q->setTargetItem(target);
}
} // Plasma namespace
#include "itembackground.moc"