enable dragging from desktop to panel

note: there's no visual feedback before you release the mouse button, and there's no way to drag back to the desktop.

svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=770333
This commit is contained in:
Chani Armitage 2008-02-03 13:09:21 +00:00
parent 29215641ef
commit be64c5939d
4 changed files with 167 additions and 94 deletions

View File

@ -19,6 +19,7 @@
#include "applethandle_p.h"
#include <QApplication>
#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QLinearGradient>
#include <QtGui/QPainter>
@ -31,6 +32,7 @@
#include <cmath>
#include <math.h>
#include "plasma/view.h"
#include "applet.h"
#include "containment.h"
#include "corona.h"
@ -328,6 +330,27 @@ void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
Phase::self()->animateItem(m_applet, Phase::Disappear);
}
break;
case MoveButton: {
//find out if we were dropped on a panel or something
QWidget *w = QApplication::topLevelAt(event->screenPos());
kDebug() << "move to widget" << w;
if (w) {
Plasma::View *v = qobject_cast<Plasma::View *>(w);
if (v) {
Containment *c = v->containment();
//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 have a screenpos, we need a scenepos
//FIXME how reliable is this transform?
QPoint pos = v->mapFromGlobal(event->screenPos());
switchContainment(c, v->mapToScene(pos));
}
}
}
break;
}
default:
break;
}
@ -378,17 +401,9 @@ void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (containments[i]->sceneBoundingRect().contains(event->scenePos())) {
// add the applet to the new containment
// and take it from the old one
QPointF scenePosition = scenePos();
//kDebug() << "moving to other containment with position" << pos() << event->scenePos();
//kDebug() << "position before reparenting" << pos() << scenePos();
m_containment = containments[i];
//m_containment->addChild(m_applet);
//setParentItem(containments[i]);
m_containment->addApplet(m_applet);
setParentItem(m_containment);
m_applet->setParentItem(this);
setPos(m_containment->mapFromScene(scenePosition));
update();
switchContainment(containments[i], scenePos());
break;
}
}
@ -493,6 +508,31 @@ void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
}
}
//pos relative to scene
void AppletHandle::switchContainment(Containment *containment, const QPointF &pos)
{
Applet *applet=m_applet;
switch (containment->containmentType()) {
case Containment::PanelContainment:
kDebug() << "panel";
//we need to fully disassociate the applet and handle, then kill the handle
forceDisappear(); //takes care of event filter and killing handle
m_applet=0; //make sure we don't try to act on the applet again
applet->disconnect(this); //make sure the applet doesn't tell us to do anything
containment->addApplet(applet, containment->mapFromScene(pos));
break;
default: //FIXME assuming everything else behaves like desktop
kDebug() << "desktop";
m_containment = containment;
containment->addApplet(m_applet);
setParentItem(containment);
m_applet->setParentItem(this);
setPos(containment->mapFromScene(pos));
//setPos(pos);
}
update();
}
QVariant AppletHandle::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionHasChanged && m_applet) {

View File

@ -74,6 +74,12 @@ class AppletHandle : public QObject, public QGraphicsItem
void calculateSize();
ButtonType mapToButton(const QPointF &point) const;
void forceDisappear();
/**
* move our applet to another containment
* @param containment the containment to move to
* @param pos the (scene-relative) position to place it at
*/
void switchContainment(Containment *containment, const QPointF &pos);
QRectF m_rect;
ButtonType m_pressedButton;

View File

@ -404,63 +404,9 @@ Applet* Containment::addApplet(const QString& name, const QVariantList& args, ui
applet = new Applet;
}
switch (containmentType()) {
case PanelContainment:
{
//panels don't want backgrounds, which is important when setting geometry
applet->setDrawStandardBackground(false);
// Calculate where the user wants the applet to go before adding it
int index = -1;
QPointF position = appletGeometry.topLeft();
BoxLayout *l = dynamic_cast<BoxLayout *>(layout());
if (!delayInit && l && position != QPointF(-1, -1)) {
foreach (Applet *existingApplet, d->applets) {
if (formFactor() == Horizontal) {
qreal middle = (existingApplet->geometry().left() +
existingApplet->geometry().right()) / 2.0;
// Applets are checked in order so there is no need to check
// if the position is equal to or greater than the applet's
// leftmost point. This also allows for dropping in the gap
// between applets.
if (position.x() < middle) {
index = l->indexOf(existingApplet);
break;
} else if (position.x() <= existingApplet->geometry().right()) {
index = l->indexOf(existingApplet) + 1;
break;
}
} else {
qreal middle = (existingApplet->geometry().top() +
existingApplet->geometry().bottom()) / 2.0;
if (position.y() < middle) {
index = l->indexOf(existingApplet);
break;
} else if (position.y() <= existingApplet->geometry().bottom()) {
index = l->indexOf(existingApplet) + 1;
break;
}
}
}
}
addApplet(applet);
prepareApplet(applet, delayInit);
// Reposition the applet after adding has been done
if (index != -1) {
l->insertItem(index, l->takeAt(l->indexOf(applet)));
d->applets.removeAll(applet);
d->applets.insert(index, applet);
}
break;
}
default:
addApplet(applet);
prepareApplet(applet, delayInit);
addApplet(applet, appletGeometry.topLeft(), delayInit);
if (containmentType() != PanelContainment) {
//kDebug() << "adding applet" << applet->name() << "with a default geometry of" << appletGeometry << appletGeometry.isValid();
if (appletGeometry.isValid()) {
applet->setGeometry(appletGeometry);
@ -484,6 +430,106 @@ Applet* Containment::addApplet(const QString& name, const QVariantList& args, ui
return applet;
}
//pos must be relative to the containment already. use mapfromscene.
//what we're trying to do here for panels is make the applet go to the requested position,
//or somewhere close to it, and get integrated properly into the containment as if it were created
//there.
void Containment::addApplet(Applet *applet, const QPointF &pos, bool dontInit)
{
if (!applet) {
kDebug() << "adding null applet!?!";
return;
}
Containment *currentContainment = applet->containment();
int index = -1;
if (containmentType() == PanelContainment) {
//panels don't want backgrounds, which is important when setting geometry
applet->setDrawStandardBackground(false);
// Calculate where the user wants the applet to go before adding it
//so long as this isn't a new applet with a delayed init
if (! dontInit || (currentContainment && currentContainment != this)) {
index = indexAt(pos);
}
}
if (currentContainment && currentContainment != this) {
applet->removeSceneEventFilter(currentContainment);
KConfigGroup oldConfig = applet->config();
applet->resetConfigurationObject();
currentContainment->d->applets.removeAll(applet);
addChild(applet);
// now move the old config to the new location
KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
oldConfig.reparent(&c);
} else {
addChild(applet);
}
d->applets << applet;
connect(applet, SIGNAL(destroyed(QObject*)),
this, SLOT(appletDestroyed(QObject*)));
if (containmentType() == PanelContainment) {
// Reposition the applet after adding has been done
if (index != -1) {
BoxLayout *l = dynamic_cast<BoxLayout *>(layout());
l->insertItem(index, l->takeAt(l->indexOf(applet)));
d->applets.removeAll(applet);
d->applets.insert(index, applet);
}
} else {
//FIXME if it came from a panel its bg was disabled
//maybe we should expect the applet to handle that on a constraint update?
//should we really do this? hell, do we have any business playing with the geometry of
//non-panel applets at all?
if (pos != QPointF(-1, -1)) {
applet->setPos(pos);
}
}
prepareApplet(applet, dontInit); //must at least flush constraints
}
//containment-relative pos... right?
int Containment::indexAt(const QPointF &pos) const
{
if (pos == QPointF(-1, -1)) {
return -1;
}
BoxLayout *l = dynamic_cast<BoxLayout *>(layout());
if (l) {
foreach (Applet *existingApplet, d->applets) {
if (formFactor() == Horizontal) {
qreal middle = (existingApplet->geometry().left() +
existingApplet->geometry().right()) / 2.0;
// Applets are checked in order so there is no need to check
// if the position is equal to or greater than the applet's
// leftmost point. This also allows for dropping in the gap
// between applets.
if (pos.x() < middle) {
return l->indexOf(existingApplet);
} else if (pos.x() <= existingApplet->geometry().right()) {
return l->indexOf(existingApplet) + 1;
}
} else {
qreal middle = (existingApplet->geometry().top() +
existingApplet->geometry().bottom()) / 2.0;
if (pos.y() < middle) {
return l->indexOf(existingApplet);
} else if (pos.y() <= existingApplet->geometry().bottom()) {
return l->indexOf(existingApplet) + 1;
}
}
}
}
return -1;
}
void Containment::prepareApplet(Applet *applet, bool delayInit)
{
if (delayInit) {
@ -549,33 +595,6 @@ bool Containment::regionIsEmpty(const QRectF &region, Applet *ignoredApplet) con
return true;
}
void Containment::addApplet(Applet *applet)
{
Containment *currentContainment = applet->containment();
if (currentContainment && currentContainment != this) {
applet->removeSceneEventFilter(currentContainment);
KConfigGroup oldConfig = applet->config();
applet->resetConfigurationObject();
currentContainment->d->applets.removeAll(applet);
addChild(applet);
// now move the old config to the new location
KConfigGroup c = config().group("Applets").group(QString::number(applet->id()));
oldConfig.reparent(&c);
} else {
addChild(applet);
}
d->applets << applet;
if (currentContainment) {
applet->installSceneEventFilter(this);
}
connect(applet, SIGNAL(destroyed(QObject*)),
this, SLOT(appletDestroyed(QObject*)));
}
void Containment::appletDestroyed(QObject* object)
{
// we do a static_cast here since it really isn't an Applet by this

View File

@ -183,9 +183,17 @@ class PLASMA_EXPORT Containment : public Applet
void clearApplets();
/**
* add existing applet to this containment
* add existing applet to this containment at pos
* @param pos the containment-relative position
* @param dontInit if true, init() will not be called on the applet
*/
void addApplet(Applet * applet);
void addApplet(Applet *applet, const QPointF &pos = QPointF(-1, -1), bool dontInit = true);
/**
* @return the index to insert an applet at if you want it near the point pos.
* @param pos the containment-relative position
*/
virtual int indexAt(const QPointF &pos) const;
/**
* Sets the physical screen this Containment is associated with.