From 1172ef79e9a18df736a1cb62501432f48c5c4221 Mon Sep 17 00:00:00 2001 From: Rob Scheepmaker Date: Sun, 11 May 2008 13:56:35 +0000 Subject: [PATCH] Start of improved applet drag & drop. What works now is: * dragging an applet creates a top level window so the applet won't appear behind the panel when dragging, as is the case now. * dragging leaves a semi transparent version of the applet at the original location, so it's clear to the user where the applet came from and where it will go when the drag is cancelled. There are still some open issues, I'm working on those... svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=806484 --- applet.cpp | 59 ++++++++++++++++++++++++++++++++----- applet.h | 13 ++++++++ applet_p.h | 1 + applethandle.cpp | 77 +++++++++++++++++++++++++++++++++--------------- applethandle_p.h | 4 +++ 5 files changed, 123 insertions(+), 31 deletions(-) diff --git a/applet.cpp b/applet.cpp index 8a3ad28b0..ed2ae6b50 100644 --- a/applet.cpp +++ b/applet.cpp @@ -700,23 +700,43 @@ QList Applet::contextualActions() void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + QPainter *p; + QPixmap pixmap(size().toSize()); + + QGraphicsView* qgv = qobject_cast(widget->parent()); + bool ghost = (qgv && (qgv == d->ghostView)); + + if (ghost) { + // The applet has to be displayed semi transparent. Create a pixmap and a painter on + // that pixmap where the applet can draw on so we can draw the result transparently + // at the end. + kDebug() << "Painting ghosted..."; + + pixmap.fill(Qt::transparent); + + p = new QPainter(); + p->begin(&pixmap); + } else { + p = painter; + } + if (d->shadow && d->shadow->shadowedSize() != boundingRect().size()) { //kDebug() << "sizes are " << d->shadow->shadowedSize() << boundingRect().size(); d->shadow->generate(); } - painter->save(); + p->save(); if (transform().isRotating()) { - painter->setRenderHint(QPainter::SmoothPixmapTransform); - painter->setRenderHint(QPainter::Antialiasing); + p->setRenderHint(QPainter::SmoothPixmapTransform); + p->setRenderHint(QPainter::Antialiasing); } if (d->background && formFactor() != Plasma::Vertical && formFactor() != Plasma::Horizontal) { //kDebug() << "option rect is" << option->rect; - d->background->paintPanel(painter, option->rect, QPointF(0,0)); + d->background->paintPanel(p, option->rect, QPointF(0,0)); } if (!d->failed) { @@ -731,17 +751,29 @@ void Applet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW Containment::StyleOption coption(*option); coption.view = v; - paintInterface(painter, &coption, contentsRect); + paintInterface(p, &coption, contentsRect); } - painter->restore(); + p->restore(); return; } //kDebug() << "paint interface of" << (QObject*) this; - paintInterface(painter, option, contentsRect); + paintInterface(p, option, contentsRect); + } + p->restore(); + + if (ghost) { + // Lets display the pixmap that we've just drawn... transparently. + p->setCompositionMode(QPainter::CompositionMode_DestinationIn); + p->fillRect(pixmap.rect(), QColor(0, 0, 0, (0.3 * 255))); + p->end(); + + delete p; + + kDebug() << "draw the pixmap!"; + painter->drawPixmap(0, 0, pixmap); } - painter->restore(); } void Applet::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, @@ -800,6 +832,17 @@ void Applet::setAspectRatioMode(Plasma::AspectRatioMode mode) d->aspectRatioMode = mode; } +QGraphicsView * Applet::ghostView() +{ + return d->ghostView; +} + +void Applet::setGhostView( QGraphicsView * view ) +{ + d->ghostView = view; + update(); +} + void Applet::registerAsDragHandle( QGraphicsItem * item ) { if (!item) { diff --git a/applet.h b/applet.h index fc365e279..0736d607e 100644 --- a/applet.h +++ b/applet.h @@ -230,6 +230,19 @@ class PLASMA_EXPORT Applet : public QGraphicsWidget */ void setAspectRatioMode(Plasma::AspectRatioMode); + /** + * @return The view where the applet appears ghosted. + */ + QGraphicsView * ghostView(); + + /** + * Sets a view which displays the applet semi transparent. + * + * @param view The view where the applet should appear 'ghosted'. Set to + * 0 to don't ghost the applet anywhere. + */ + void setGhostView(QGraphicsView * view); + /** * Returns a list of all known applets. * diff --git a/applet_p.h b/applet_p.h index d5f475abc..2ed12c7a6 100644 --- a/applet_p.h +++ b/applet_p.h @@ -85,6 +85,7 @@ public: KConfigGroup *mainConfig; Plasma::Constraints pendingConstraints; Plasma::AspectRatioMode aspectRatioMode; + QGraphicsView* ghostView; ImmutabilityType immutability; int constraintsTimerId; bool hasConfigurationInterface : 1; diff --git a/applethandle.cpp b/applethandle.cpp index abd20157c..4d33bddc8 100644 --- a/applethandle.cpp +++ b/applethandle.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -57,7 +58,8 @@ AppletHandle::AppletHandle(Containment *parent, Applet *applet) m_scaleWidth(1.0), m_scaleHeight(1.0), m_buttonsOnRight(false), - m_pendingFade(false) + m_pendingFade(false), + m_topview(0) { KColorScheme colorScheme(QPalette::Active, KColorScheme::View, Theme::defaultTheme()->colorScheme()); m_gradientColor = colorScheme.background(KColorScheme::NormalBackground).color(); @@ -270,6 +272,10 @@ void AppletHandle::mousePressEvent(QGraphicsSceneMouseEvent *event) setZValue(m_applet->zValue()); } event->accept(); + //set mousePos to the position in the applet, in screencoords, so it becomes easy + //to reposition the toplevel view to the correct position. + m_mousePos = event->screenPos() - m_applet->view()->mapToGlobal( + m_applet->view()->mapFromScene(m_applet->pos())); update(); return; } @@ -333,6 +339,12 @@ void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) } break; case MoveButton: { + if (m_topview) { + m_topview->hide(); + delete m_topview; + m_topview = 0; + m_applet->setGhostView(0); + } //find out if we were dropped on a panel or something QWidget *w = QApplication::topLevelAt(event->screenPos()); kDebug() << "move to widget" << w; @@ -340,14 +352,21 @@ void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Plasma::View *v = qobject_cast(w); if (v) { Containment *c = v->containment(); - //XXX the dashboard view won't give us a containment. if it did, this could + QPoint pos = v->mapFromGlobal(event->screenPos() - m_mousePos); + + //XXX the dashboard view won't give us a + //containment. if it did, this could //break shit. if (c && c != m_containment) { - //we actually have been dropped on another containment, so move there + //we actually have been dropped on another + //containment, so move there //we have a screenpos, we need a scenepos //FIXME how reliable is this transform? - QPoint pos = v->mapFromGlobal(event->screenPos()); switchContainment(c, v->mapToScene(pos)); + } else { + //just update the position + kDebug() << "just update the position"; + m_applet->setPos(v->mapToScene(pos)); } } } @@ -392,25 +411,37 @@ void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) QPointF delta = curPos-lastPos; if (m_pressedButton == MoveButton) { - setPos(pos()+delta); - // test for containment change - if (!m_containment->sceneBoundingRect().contains(event->scenePos())) { - // see which containment it belongs to - Corona * corona = qobject_cast(scene()); - if (corona) { - QList containments = corona->containments(); - for (int i = 0; i < containments.size(); ++i) { - if (containments[i]->sceneBoundingRect().contains(event->scenePos())) { - // add the applet to the new containment - // and take it from the old one - //kDebug() << "moving to other containment with position" << pos() << event->scenePos(); - //kDebug() << "position before reparenting" << pos() << scenePos(); - switchContainment(containments[i], scenePos()); - break; - } - } - } + if (!m_topview) { //create a new toplevel view + m_topview = new View(m_applet->containment(), -1, 0); + m_topview->setTrackContainmentChanges(false); + + m_topview->setWindowFlags(Qt::Widget | Qt::FramelessWindowHint + | Qt::WindowStaysOnTopHint); + KWindowSystem::setState(m_topview->winId(), NET::SkipTaskbar | NET::SkipPager); + + m_topview->setWallpaperEnabled(false); + + //TODO: when zoomed out, this doesn't work correctly + m_topview->setSceneRect(m_applet->sceneBoundingRect()); + + // Calculate the size of the applet in screen coordinates. + QPointF bottomRight = m_applet->pos(); + bottomRight.setX(bottomRight.x() + m_applet->size().width()); + bottomRight.setY(bottomRight.y() + m_applet->size().height()); + + QPoint tL = m_applet->view()->mapToGlobal(m_applet->view()->mapFromScene( + m_applet->pos())); + QPoint bR = m_applet->view()->mapToGlobal(m_applet->view()->mapFromScene( + bottomRight)); + + m_topview->resize(bR.x() - tL.x(), bR.y() - tL.y()); + m_topview->show(); + + m_applet->setGhostView(m_applet->containment()->view()); } + + m_topview->move((event->screenPos() - m_mousePos)); + } else if (m_pressedButton == RotateButton || m_pressedButton == ResizeButton) { if (_k_distanceForPoint(delta) <= 1.0) { @@ -510,6 +541,7 @@ void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } } + //pos relative to scene void AppletHandle::switchContainment(Containment *containment, const QPointF &pos) { @@ -665,7 +697,6 @@ void AppletHandle::calculateSize() } m_rect.adjust(-HANDLE_WIDTH, -HANDLE_WIDTH, HANDLE_WIDTH, HANDLE_WIDTH); - if (m_applet->pos().x() <= ((HANDLE_WIDTH * 2) + ICON_SIZE)) { m_rect.adjust(0.0, 0.0, ICON_SIZE, 0.0); m_buttonsOnRight = true; diff --git a/applethandle_p.h b/applethandle_p.h index 840cb36f1..1fa421fa7 100644 --- a/applethandle_p.h +++ b/applethandle_p.h @@ -31,6 +31,7 @@ namespace Plasma { class Applet; class Containment; +class View; class AppletHandle : public QObject, public QGraphicsItem { @@ -96,6 +97,9 @@ class AppletHandle : public QObject, public QGraphicsItem QTimer *m_hoverTimer; bool m_buttonsOnRight; bool m_pendingFade; + View *m_topview; + QPoint m_mousePos; //the position of the mousecursor relative to the + //applets position. }; }