Give extenders the 'stacked' look from Pinheiro's mockup. It still needs some minor tweaks, but it allready looks quite sexy.

svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=873867
This commit is contained in:
Rob Scheepmaker 2008-10-20 10:58:37 +00:00
parent 6ef3b851ac
commit a26b73b6bb
10 changed files with 595 additions and 360 deletions

View File

@ -38,8 +38,11 @@
#include <KDebug>
#include <NETRootInfo>
#include <plasma/panelsvg.h>
#include <plasma/theme.h>
#include "plasma/applet.h"
#include "plasma/extender.h"
#include "plasma/private/extender_p.h"
#include "plasma/panelsvg.h"
#include "plasma/theme.h"
#ifdef Q_WS_X11
#include <X11/Xlib.h>
@ -89,8 +92,40 @@ void DialogPrivate::themeUpdated()
const int leftWidth = background->marginSize(Plasma::LeftMargin);
const int rightWidth = background->marginSize(Plasma::RightMargin);
const int bottomHeight = background->marginSize(Plasma::BottomMargin);
//TODO: correct handling of the situation when having vertical panels.
Extender *extender = qobject_cast<Extender*>(widget);
if (extender) {
switch (extender->d->applet->location()) {
case BottomEdge:
background->setEnabledBorders(PanelSvg::LeftBorder | PanelSvg::TopBorder
| PanelSvg::RightBorder);
q->setContentsMargins(0, topHeight, 0, 0);
break;
case TopEdge:
background->setEnabledBorders(PanelSvg::LeftBorder | PanelSvg::BottomBorder
| PanelSvg::RightBorder);
q->setContentsMargins(0, 0, 0, bottomHeight);
break;
case LeftEdge:
background->setEnabledBorders(PanelSvg::TopBorder | PanelSvg::BottomBorder
| PanelSvg::RightBorder);
q->setContentsMargins(0, topHeight, 0, bottomHeight);
break;
case RightEdge:
background->setEnabledBorders(PanelSvg::TopBorder | PanelSvg::BottomBorder
| PanelSvg::LeftBorder);
q->setContentsMargins(0, topHeight, 0, bottomHeight);
break;
default:
background->setEnabledBorders(PanelSvg::AllBorders);
q->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
}
} else {
q->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight);
}
q->update();
}
void DialogPrivate::adjustView()
{
@ -99,13 +134,15 @@ void DialogPrivate::adjustView()
kDebug() << "Widget size:" << widget->size()
<< "| Widget size hint:" << widget->effectiveSizeHint(Qt::PreferredSize)
<< "| Widget minsize hint:" << widget->minimumSize()
<< "| Widget maxsize hint:" << widget->maximumSize()
<< "| Widget bounding rect:" << widget->boundingRect();
QRectF boundingRect = widget->boundingRect();
boundingRect.setSize(widget->effectiveSizeHint(Qt::PreferredSize));
//reposition and resize the view.
view->setSceneRect(widget->mapToScene(boundingRect).boundingRect());
view->setSceneRect(widget->sceneBoundingRect());
view->resize(view->mapFromScene(view->sceneRect()).boundingRect().size());
view->centerOn(widget);
@ -115,10 +152,10 @@ void DialogPrivate::adjustView()
q->setMinimumSize(qMin(int(widget->minimumSize().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(widget->minimumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
q->resize(qMin(int(widget->minimumSize().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(widget->minimumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
q->setMaximumSize(qMin(int(widget->maximumSize().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(widget->maximumSize().height()) + top + bottom, QWIDGETSIZE_MAX));
q->resize(qMin(int(view->size().width()) + left + right, QWIDGETSIZE_MAX),
qMin(int(view->size().height()) + top + bottom, QWIDGETSIZE_MAX));
q->updateGeometry();
if (q->size() != prevSize) {
@ -316,6 +353,8 @@ void Dialog::setGraphicsWidget(QGraphicsWidget *widget)
lay->setSpacing(0);
}
d->themeUpdated();
if (!d->view) {
d->view = new QGraphicsView(this);
d->view->setFrameShape(QFrame::NoFrame);

View File

@ -27,12 +27,14 @@
#include "containment.h"
#include "corona.h"
#include "extenderitem.h"
#include "panelsvg.h"
#include "popupapplet.h"
#include "svg.h"
#include "widgets/label.h"
#include "private/applet_p.h"
#include "private/extender_p.h"
#include "private/extenderitem_p.h"
namespace Plasma
{
@ -42,12 +44,15 @@ Extender::Extender(Applet *applet)
d(new ExtenderPrivate(applet, this))
{
applet->d->extender = this;
setContentsMargins(0, 0, 0, 0);
d->layout = new QGraphicsLinearLayout(this);
d->layout->setOrientation(Qt::Vertical);
d->layout->setContentsMargins(0, 0, 0, 0);
d->layout->setSpacing(0);
setLayout(d->layout);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
d->emptyExtenderLabel = new Label(this);
d->emptyExtenderLabel->setText(d->emptyExtenderMessage);
d->emptyExtenderLabel->setMinimumSize(QSizeF(150, 64));
@ -55,8 +60,6 @@ Extender::Extender(Applet *applet)
d->emptyExtenderLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
d->layout->addItem(d->emptyExtenderLabel);
d->adjustSizeHints();
d->loadExtenderItems();
}
@ -130,6 +133,21 @@ ExtenderItem *Extender::item(const QString &name) const
return 0;
}
void Extender::setExtenderAppearance(Appearance appearance)
{
if (d->appearance == appearance) {
return;
}
d->appearance = appearance;
d->updateBorders();
}
Extender::Appearance Extender::extenderAppearance() const
{
return d->appearance;
}
void Extender::saveState()
{
foreach (ExtenderItem *item, attachedItems()) {
@ -140,7 +158,7 @@ void Extender::saveState()
QVariant Extender::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemPositionHasChanged) {
emit geometryChanged();
d->adjustSize();
}
return QGraphicsWidget::itemChange(change, value);
@ -169,7 +187,7 @@ void Extender::itemAddedEvent(ExtenderItem *item, const QPointF &pos)
d->emptyExtenderLabel->hide();
}
d->adjustSizeHints();
d->adjustSize();
}
void Extender::itemRemovedEvent(ExtenderItem *item)
@ -185,7 +203,7 @@ void Extender::itemRemovedEvent(ExtenderItem *item)
d->layout->addItem(d->emptyExtenderLabel);
}
d->adjustSizeHints();
d->adjustSize();
}
void Extender::itemHoverEnterEvent(ExtenderItem *item)
@ -209,7 +227,7 @@ void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
//Create a widget that functions as spacer, and add that to the layout.
QGraphicsWidget *widget = new QGraphicsWidget(this);
widget->setPreferredSize(QSizeF(item->size().width(), item->size().height()));
widget->setPreferredSize(QSizeF(item->minimumSize().width(), item->size().height()));
widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
d->spacerWidget = widget;
d->layout->insertItem(insertIndex, widget);
@ -221,7 +239,7 @@ void Extender::itemHoverMoveEvent(ExtenderItem *item, const QPointF &pos)
d->emptyExtenderLabel->hide();
}
d->adjustSizeHints();
d->adjustSize();
}
void Extender::itemHoverLeaveEvent(ExtenderItem *item)
@ -244,7 +262,22 @@ void Extender::itemHoverLeaveEvent(ExtenderItem *item)
d->layout->addItem(d->emptyExtenderLabel);
}
d->adjustSizeHints();
d->adjustSize();
}
}
PanelSvg::EnabledBorders Extender::enabledBordersForItem(ExtenderItem *item) const
{
ExtenderItem *topItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(0));
ExtenderItem *bottomItem = dynamic_cast<ExtenderItem*>(d->layout->itemAt(d->layout->count() - 1));
if (d->appearance == TopDownStacked && bottomItem != item) {
return PanelSvg::LeftBorder | PanelSvg::BottomBorder | PanelSvg::RightBorder;
} else if (d->appearance == BottomUpStacked && topItem != item) {
return PanelSvg::LeftBorder | PanelSvg::TopBorder | PanelSvg::RightBorder;
} else if (d->appearance != NoBorders) {
return PanelSvg::LeftBorder | PanelSvg::RightBorder;
} else {
return 0;
}
}
@ -254,7 +287,8 @@ ExtenderPrivate::ExtenderPrivate(Applet *applet, Extender *extender) :
spacerWidget(0),
emptyExtenderMessage(i18n("no items")),
emptyExtenderLabel(0),
popup(false)
popup(false),
appearance(Extender::NoBorders)
{
}
@ -265,32 +299,19 @@ ExtenderPrivate::~ExtenderPrivate()
void ExtenderPrivate::addExtenderItem(ExtenderItem *item, const QPointF &pos)
{
attachedExtenderItems.append(item);
q->itemAddedEvent(item, pos);
q->itemHoverLeaveEvent(item);
q->itemAddedEvent(item, pos);
updateBorders();
emit q->itemAttached(item);
}
void ExtenderPrivate::removeExtenderItem(ExtenderItem *item)
{
attachedExtenderItems.removeOne(item);
updateBorders();
q->itemRemovedEvent(item);
}
void ExtenderPrivate::adjustSizeHints()
{
if (!q->layout()) {
return;
}
q->layout()->updateGeometry();
q->setMinimumSize(q->layout()->minimumSize());
q->setPreferredSize(q->layout()->preferredSize());
q->setMaximumSize(q->layout()->maximumSize());
q->updateGeometry();
}
int ExtenderPrivate::insertIndexFromPos(const QPointF &pos) const
{
int insertIndex = -1;
@ -376,7 +397,40 @@ void ExtenderPrivate::loadExtenderItems()
}
}
adjustSizeHints();
adjustSize();
}
void ExtenderPrivate::updateBorders()
{
foreach (ExtenderItem *item, q->attachedItems()) {
if (item && (item->d->background->enabledBorders() != q->enabledBordersForItem(item))) {
//call themeChanged to change the backgrounds enabled borders, and move all contained
//widgets according to it's changed margins.
item->d->themeChanged();
}
}
}
void ExtenderPrivate::adjustSize()
{
if (q->layout()) {
q->layout()->updateGeometry();
//FIXME: for some reason the preferred size hint get's set correctly,
//but the minimumSizeHint doesn't. Investigate why.
q->setMinimumSize(q->layout()->preferredSize());
}
//check if our parentItem is an applet, and set the applets correct minimumsize.
Applet *applet = qgraphicsitem_cast<Applet*>(q->parentItem());
if (applet) {
QSizeF marginSize = applet->size() - applet->contentsRect().size();
//FIXME: for some reason the preferred size hint get's set correctly,
//but the minimumSizeHint doesn't. Investigate why.
applet->setMinimumSize(q->minimumSize() + marginSize);
applet->adjustSize();
}
emit q->geometryChanged();
}
} // Plasma namespace

View File

@ -22,6 +22,7 @@
#include <QtGui/QGraphicsWidget>
#include "plasma/panelsvg.h"
#include "plasma/plasma_export.h"
namespace Plasma
@ -54,6 +55,27 @@ class PLASMA_EXPORT Extender : public QGraphicsWidget
Q_PROPERTY(QString emptyExtenderMessage READ emptyExtenderMessage WRITE setEmptyExtenderMessage)
public:
/**
* Description on how to render the extender's items.
*/
enum Appearance {
NoBorders = 0, /** Draws no borders on the extender's items. When placed in an applet
on the desktop, use this setting and use the standard margins of
the applet containing this extender. */
BottomUpStacked = 1, /** Draws no borders on the topmost extenderitem, but draws the
left, top and right border on subsequent items. When margins
of the containing dialog are set to 0, except for the top
margin, this leads to the 'stacked' look, recommended for
extenders of applet's contained in a panel at the bottom of
the screen. */
TopDownStacked = 2 /** Draws no borders on the bottom extenderitem, but draws the
left, bottom and right border on subsequent items. When margins
of the containing dialog are set to 0, except for the bottom
margin, this leads to the 'stacked' look, recommended for
extenders of applet's contained in a panel at the top of
the screen. */
};
/**
* Creates an extender. Note that extender expects applet to have a config(), and needs a
* scene because of that. So you should only instantiate an extender in init() or later, not
@ -102,6 +124,18 @@ class PLASMA_EXPORT Extender : public QGraphicsWidget
*/
ExtenderItem *item(const QString &name) const;
/**
* Use this function to instruct the extender on how to render it's items. Usually you will
* want to call this function in your applet's constraintsEvent, allthough this is already
* done for you when using PopupApplet at base class for your applet. Defaults to NoBorders.
*/
void setExtenderAppearance(Appearance appearance);
/**
* @return the current way of rendering extender items that is used.
*/
Appearance extenderAppearance() const;
protected:
/**
* Get's called after an item has been added to this extender. The bookkeeping has already
@ -151,6 +185,14 @@ class PLASMA_EXPORT Extender : public QGraphicsWidget
*/
virtual void saveState();
/**
* This function get's called on every item to determine which background border's to
* render.
* @param item the item for which it's position or extender has changed.
* @return the borders that have to be enabled on it's background.
*/
virtual PanelSvg::EnabledBorders enabledBordersForItem(ExtenderItem *item) const;
/**
* @reimplemented from QGraphicsWidget
*/
@ -183,6 +225,8 @@ class PLASMA_EXPORT Extender : public QGraphicsWidget
friend class ExtenderPrivate;
friend class ExtenderItem;
friend class ExtenderItemPrivate;
//dialog needs access to the extender's applet location.
friend class DialogPrivate;
//applet should be able to call saveState();
friend class Applet;

View File

@ -46,247 +46,11 @@
#include "private/applet_p.h"
#include "private/extender_p.h"
#include "private/extenderitem_p.h"
namespace Plasma
{
class ExtenderItemPrivate
{
public:
ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
: q(extenderItem),
widget(0),
toplevel(0),
previousTargetExtender(0),
extender(hostExtender),
title(QString()),
sourceAppletId(hostExtender->d->applet->id()),
mousePressed(false),
expirationTimer(0)
{
}
~ExtenderItemPrivate()
{
delete toplevel;
}
/**
* @return a Rect containing the area of the detachable where the draghandle can be drawn.
*/
QRectF dragHandleRect()
{
QRectF rect(bgLeft, bgTop, q->size().width() - bgLeft - bgRight,
dragger->elementSize("hint-preferred-icon-size").height() +
dragTop + dragBottom);
return rect;
}
QRectF titleRect()
{
return dragHandleRect().adjusted(dragLeft + collapseIcon->size().width() + 1, dragTop,
-toolbox->size().width() - dragRight, -dragBottom);
}
//XXX kinda duplicated from applethandle.
//returns true if the applet overlaps with a different view then the current one.
bool leaveCurrentView(const QRect &rect)
{
if ((q->sceneBoundingRect().left() < 0)) {
//always leaveCurrentView when the item is in a Plasma::Dialog which can easy be
//seen by checking if it is in topleft quadrant and it's not just there because it's
//being viewed by the toplevel view.
return true;
}
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
if (widget->geometry().intersects(rect) && widget->isVisible() && widget != toplevel) {
//is this widget a plasma view, a different view then our current one,
//AND not a dashboardview?
QGraphicsView *v = qobject_cast<QGraphicsView*>(widget);
QGraphicsView *currentV = 0;
if (hostApplet()) {
currentV = qobject_cast<QGraphicsView*>(hostApplet()->containment()->view());
}
if (v && v != currentV) {
return true;
}
}
}
return false;
}
QRect screenRect() {
return hostApplet()->containment()->view()
->mapFromScene(q->sceneBoundingRect()).boundingRect();
}
void toggleCollapse() {
q->setCollapsed(!q->isCollapsed());
}
void updateToolBox() {
if (toolbox && dragger && toolboxLayout) {
//clean the layout.
uint iconHeight = dragger->elementSize("hint-preferred-icon-size").height();
while (toolboxLayout->count()) {
QGraphicsLayoutItem *icon = toolboxLayout->itemAt(0);
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(icon);
widget->deleteLater();
toolboxLayout->removeAt(0);
}
//add the actions that are actually set to visible.
foreach (QAction *action, actions) {
if (action->isVisible()) {
Icon *icon = new Icon(q);
icon->setAction(action);
QSizeF iconSize = icon->sizeFromIconSize(iconHeight);
icon->setMinimumSize(iconSize);
icon->setMaximumSize(iconSize);
toolboxLayout->addItem(icon);
}
}
if (q->isDetached() && sourceApplet) {
Icon *returnToSource = new Icon(q);
returnToSource->setSvg("widgets/configuration-icons", "return-to-source");
QSizeF iconSize = returnToSource->sizeFromIconSize(iconHeight);
returnToSource->setMinimumSize(iconSize);
returnToSource->setMaximumSize(iconSize);
toolboxLayout->addItem(returnToSource);
QObject::connect(returnToSource, SIGNAL(clicked()),
q, SLOT(moveBackToSource()));
}
toolboxLayout->updateGeometry();
//position the toolbox correctly.
QSizeF minimum = toolboxLayout->minimumSize();
toolbox->resize(minimum);
toolbox->setPos(q->size().width() - minimum.width() - bgRight,
((dragger->size().height() + dragTop + dragBottom) / 2) -
(minimum.height() / 2) + bgTop);
toolbox->update();
}
}
//TODO: something like this as static function in corona might be a good idea.
QPointF scenePosFromScreenPos(const QPoint &pos) const
{
//get the stacking order of the toplevel windows and remove the
//toplevel view that's only here while dragging, since we're not
//interested in finding that.
QList<WId> order = KWindowSystem::stackingOrder();
if (toplevel) {
order.removeOne(toplevel->winId());
}
QGraphicsView *found = 0;
foreach (QWidget *w, QApplication::topLevelWidgets()) {
QGraphicsView *v = 0;
//first check if we're over a Dialog.
Dialog *dialog = qobject_cast<Dialog*>(w);
if (dialog) {
if (dialog->isVisible() && dialog->geometry().contains(pos)) {
v = qobject_cast<QGraphicsView*>(dialog->layout()->itemAt(0)->widget());
if (v) {
return v->mapToScene(v->mapFromGlobal(pos));
}
}
} else {
v = qobject_cast<QGraphicsView *>(w);
}
//else check if it is a QGV:
if (v && w->isVisible() && w->geometry().contains(pos)) {
if (found && order.contains(found->winId())) {
if (order.indexOf(found->winId()) < order.indexOf(v->winId())) {
found = v;
}
} else {
found = v;
}
}
}
if (!found) {
return QPointF();
}
return found->mapToScene(found->mapFromGlobal(pos));
}
Applet *hostApplet() const
{
if (extender) {
return extender->d->applet;
} else {
return 0;
}
}
void themeChanged()
{
dragger->setImagePath("widgets/extender-dragger");
background->setImagePath("widgets/extender-background");
dragger->getMargins(dragLeft, dragTop, dragRight, dragBottom);
background->getMargins(bgLeft, bgTop, bgRight, bgBottom);
//setCollapsed recalculates size hints.
q->setCollapsed(q->isCollapsed());
q->update();
}
ExtenderItem *q;
QGraphicsItem *widget;
QGraphicsWidget *toolbox;
QGraphicsLinearLayout *toolboxLayout;
QGraphicsView *toplevel;
Extender *previousTargetExtender;
Extender *extender;
Applet *sourceApplet;
KConfigGroup config;
PanelSvg *dragger;
PanelSvg *background;
Icon *collapseIcon;
QMap<QString, QAction*> actions;
QString title;
QString name;
uint sourceAppletId;
uint extenderItemId;
qreal dragLeft, dragTop, dragRight, dragBottom;
qreal bgLeft, bgTop, bgRight, bgBottom;
QPointF deltaScene;
QPoint mousePos;
bool mousePressed;
bool mouseOver;
QTimer *expirationTimer;
static uint s_maxExtenderItemId;
};
uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
: QGraphicsWidget(hostExtender),
d(new ExtenderItemPrivate(this, hostExtender))
@ -302,12 +66,15 @@ ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
d->extenderItemId = ++ExtenderItemPrivate::s_maxExtenderItemId;
}
//TODO: make sourceApplet a function so stuff doesn't break when the source applet has been
//removed.
d->sourceApplet = hostExtender->d->applet;
//create items's configgroup
KConfigGroup cg = hostExtender->d->applet->config("ExtenderItems");
KConfigGroup dg = KConfigGroup(&cg, QString::number(d->extenderItemId));
//TODO: automatically load title and icon.
if (!dg.readEntry("sourceAppletId", 0)) {
//The item is new
dg.writeEntry("sourceAppletPluginName", hostExtender->d->applet->pluginName());
@ -330,33 +97,22 @@ ExtenderItem::ExtenderItem(Extender *hostExtender, uint extenderItemId)
}
}
//create the dragger and standard applet background.
d->dragger = new PanelSvg(this);
d->background = new PanelSvg(this);
d->themeChanged();
//create the toolbox.
d->toolbox = new QGraphicsWidget(this);
d->toolboxLayout = new QGraphicsLinearLayout(d->toolbox);
d->toolbox->setLayout(d->toolboxLayout);
//allow the theme to set the size of the icon.
QSizeF iconSize = d->dragger->elementSize("hint-preferred-icon-size");
//create the collapse/applet icon.
d->collapseIcon = new Icon(KIcon(hostExtender->d->applet->icon()), "", this);
d->collapseIcon->resize(d->collapseIcon->sizeFromIconSize(iconSize.height()));
d->collapseIcon->setPos(d->bgLeft + d->dragLeft,
(d->dragger->size().height() + d->dragTop + d->dragBottom) / 2 - d->collapseIcon->size().height() / 2 + d->bgTop);
connect(d->collapseIcon, SIGNAL(clicked()), this, SLOT(toggleCollapse()));
//set the extender we want to move to.
setExtender(hostExtender);
setAcceptHoverEvents(true);
//set the image paths, image sizes and collapseIcon position.
d->themeChanged();
d->updateToolBox();
updateGeometry();
setAcceptsHoverEvents(true);
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
}
@ -397,7 +153,11 @@ QString ExtenderItem::name() const
void ExtenderItem::setWidget(QGraphicsItem *widget)
{
widget->setParentItem(this);
widget->setPos(QPointF(d->bgLeft + d->dragLeft, d->dragHandleRect().height() +
QSizeF iconSize = d->dragger->elementSize("hint-preferred-icon-size");
QSizeF panelSize(QSizeF(size().width() - d->bgLeft - d->bgRight,
iconSize.height() + d->dragTop + d->dragBottom));
widget->setPos(QPointF(d->bgLeft + d->dragLeft, panelSize.height() +
d->bgTop + d->dragTop));
d->widget = widget;
setCollapsed(isCollapsed()); //updates the size hints.
@ -466,6 +226,9 @@ void ExtenderItem::setExtender(Extender *extender, const QPointF &pos)
d->expirationTimer = 0;
}
//set the background svg to that of the extender we're moving to.
d->themeChanged();
//we might have to enable or disable the returnToSource button.
d->updateToolBox();
}
@ -578,27 +341,27 @@ void ExtenderItem::setCollapsed(bool collapsed)
d->widget->setVisible(!collapsed);
QSizeF minimumSize;
QSizeF preferredSize;
QSizeF maximumSize;
QSizeF min;
QSizeF pref;
QSizeF max;
if (d->widget->isWidget()) {
QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
minimumSize = graphicsWidget->minimumSize();
preferredSize = graphicsWidget->preferredSize();
maximumSize = graphicsWidget->maximumSize();
min = graphicsWidget->minimumSize();
pref = graphicsWidget->preferredSize();
max = graphicsWidget->maximumSize();
} else {
minimumSize = d->widget->boundingRect().size();
preferredSize = d->widget->boundingRect().size();
maximumSize = d->widget->boundingRect().size();
min = d->widget->boundingRect().size();
pref = d->widget->boundingRect().size();
max = d->widget->boundingRect().size();
}
if (collapsed) {
setPreferredSize(QSizeF(preferredSize.width() + marginWidth,
setPreferredSize(QSizeF(pref.width() + marginWidth,
d->dragHandleRect().height() + marginHeight));
setMinimumSize(QSizeF(minimumSize.width() + marginWidth,
setMinimumSize(QSizeF(min.width() + marginWidth,
d->dragHandleRect().height() + marginHeight));
setMaximumSize(QSizeF(maximumSize.width() + marginWidth,
setMaximumSize(QSizeF(max.width() + marginWidth,
d->dragHandleRect().height() + marginHeight));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@ -606,16 +369,15 @@ void ExtenderItem::setCollapsed(bool collapsed)
d->collapseIcon->setToolTip(i18n("Expand this widget"));
}
} else {
setPreferredSize(QSizeF(preferredSize.width() + marginWidth,
preferredSize.height() + d->dragHandleRect().height() + marginHeight));
setMinimumSize(QSizeF(minimumSize.width() + marginWidth,
minimumSize.height() + d->dragHandleRect().height() + marginHeight));
setMaximumSize(QSizeF(maximumSize.width() + marginWidth,
maximumSize.height() + d->dragHandleRect().height() + marginHeight));
setPreferredSize(QSizeF(pref.width() + marginWidth,
pref.height() + d->dragHandleRect().height() + marginHeight));
setMinimumSize(QSizeF(min.width() + marginWidth,
min.height() + d->dragHandleRect().height() + marginHeight));
setMaximumSize(QSizeF(max.width() + marginWidth,
max.height() + d->dragHandleRect().height() + marginHeight));
if (d->widget->isWidget()) {
QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
setSizePolicy(graphicsWidget->sizePolicy());
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
} else {
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
}
@ -626,8 +388,7 @@ void ExtenderItem::setCollapsed(bool collapsed)
}
updateGeometry();
d->extender->d->adjustSizeHints();
d->extender->d->adjustSize();
}
void ExtenderItem::moveBackToSource()
@ -647,17 +408,21 @@ void ExtenderItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
painter->setRenderHint(QPainter::TextAntialiasing, true);
painter->setRenderHint(QPainter::Antialiasing, true);
d->background->paintPanel(painter);
if (d->background->enabledBorders() != (PanelSvg::LeftBorder | PanelSvg::RightBorder)) {
//Don't paint if only the left and right borders are enabled, we only use the left and right
//border in this situation to set the correct margins on this item.
d->background->paintPanel(painter, QPointF(0, 0));
}
d->dragger->paintPanel(painter, QPointF(d->bgLeft, d->bgTop));
//draw the title.
//set the font for the title.
Plasma::Theme *theme = Plasma::Theme::defaultTheme();
QFont font = theme->font(Plasma::Theme::DefaultFont);
font.setPointSize(KGlobalSettings::smallestReadableFont().pointSize());
font.setWeight(QFont::Bold);
//XXX: duplicated from windowtaskitem.
//create a pixmap with the title that is faded out at the right side of the titleRect.
//TODO: hmm, create something generic for this... there's probably more stuff that wants to have
//this functionality
QRectF rect = QRectF(d->titleRect().width() - 30, 0, 30, d->titleRect().height());
@ -693,10 +458,9 @@ void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
qreal height = event->newSize().height();
//resize the dragger
QSizeF newDraggerSize(width - d->bgLeft - d->bgRight,
d->dragger->resizePanel(QSizeF(width - d->bgLeft - d->bgRight,
d->dragger->elementSize("hint-preferred-icon-size").height() +
d->dragTop + d->dragBottom);
d->dragger->resizePanel(newDraggerSize);
d->dragTop + d->dragBottom));
//resize the applet background
d->background->resizePanel(event->newSize());
@ -704,13 +468,17 @@ void ExtenderItem::resizeEvent(QGraphicsSceneResizeEvent *event)
//resize the widget
if (d->widget && d->widget->isWidget()) {
QSizeF newWidgetSize(width - d->bgLeft - d->bgRight - d->dragLeft - d->dragRight,
height - d->dragger->size().height() - d->bgTop - d->bgBottom - 2 * d->dragTop - 2 * d->dragBottom);
height - d->dragHandleRect().height() - d->bgTop - d->bgBottom -
2 * d->dragTop - 2 * d->dragBottom);
QGraphicsWidget *graphicsWidget = static_cast<QGraphicsWidget*>(d->widget);
graphicsWidget->resize(newWidgetSize);
}
d->updateToolBox();
//reposition the toolbox.
d->repositionToolbox();
update();
}
void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
@ -722,13 +490,9 @@ void ExtenderItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
d->mousePressed = true;
d->deltaScene = pos();
Applet *parentApplet = d->hostApplet();
d->mousePos = event->pos().toPoint();
parentApplet->raise();
setZValue(parentApplet->zValue());
d->hostApplet()->raise();
setZValue(d->hostApplet()->zValue());
QPointF mousePos = d->scenePosFromScreenPos(event->screenPos());
@ -746,6 +510,10 @@ void ExtenderItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return;
}
if (d->background->enabledBorders() != PanelSvg::AllBorders) {
d->themeChanged();
}
//keep track of the movement in scene coordinates. we use this to set the position of the
//applet when remaining in the current view.
d->deltaScene += event->scenePos() - event->lastScenePos();
@ -940,6 +708,236 @@ void ExtenderItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
}
}
ExtenderItemPrivate::ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender)
: q(extenderItem),
widget(0),
toplevel(0),
previousTargetExtender(0),
extender(hostExtender),
dragger(new PanelSvg(extenderItem)),
background(new PanelSvg(extenderItem)),
title(QString()),
sourceAppletId(hostExtender->d->applet->id()),
mousePressed(false),
expirationTimer(0)
{
}
ExtenderItemPrivate::~ExtenderItemPrivate()
{
delete toplevel;
}
//returns a Rect containing the area of the detachable where the draghandle will be drawn.
QRectF ExtenderItemPrivate::dragHandleRect()
{
QSizeF iconSize = dragger->elementSize("hint-preferred-icon-size");
QSizeF panelSize(QSizeF(q->size().width() - bgLeft - bgRight,
iconSize.height() + dragTop + dragBottom));
return QRectF(QPointF(bgLeft, bgTop), panelSize);
}
QRectF ExtenderItemPrivate::titleRect()
{
return dragHandleRect().adjusted(dragLeft + collapseIcon->size().width() + 1, dragTop,
-toolbox->size().width() - dragRight, -dragBottom);
}
bool ExtenderItemPrivate::leaveCurrentView(const QRect &rect)
{
if ((q->sceneBoundingRect().left() < 0)) {
//always leaveCurrentView when the item is in a Plasma::Dialog which can easy be
//seen by checking if it is in topleft quadrant and it's not just there because it's
//being viewed by the toplevel view.
return true;
}
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
if (widget->geometry().intersects(rect) && widget->isVisible() && widget != toplevel) {
//is this widget a plasma view, a different view then our current one,
//AND not a dashboardview?
QGraphicsView *v = qobject_cast<QGraphicsView*>(widget);
QGraphicsView *currentV = 0;
if (hostApplet()) {
currentV = qobject_cast<QGraphicsView*>(hostApplet()->containment()->view());
}
if (v && v != currentV) {
return true;
}
}
}
return false;
}
QRect ExtenderItemPrivate::screenRect()
{
return hostApplet()->containment()->view()
->mapFromScene(q->sceneBoundingRect()).boundingRect();
}
void ExtenderItemPrivate::toggleCollapse()
{
q->setCollapsed(!q->isCollapsed());
}
void ExtenderItemPrivate::updateToolBox()
{
Q_ASSERT(toolbox);
Q_ASSERT(dragger);
Q_ASSERT(toolboxLayout);
uint iconHeight = dragger->elementSize("hint-preferred-icon-size").height();
//TODO: only delete items that actually have to be deleted, current performance is horrible.
//clean the layout.
while (toolboxLayout->count()) {
QGraphicsLayoutItem *icon = toolboxLayout->itemAt(0);
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget*>(icon);
widget->deleteLater();
toolboxLayout->removeAt(0);
}
//add the actions that are actually set to visible.
foreach (QAction *action, actions) {
if (action->isVisible()) {
Icon *icon = new Icon(q);
icon->setAction(action);
QSizeF iconSize = icon->sizeFromIconSize(iconHeight);
icon->setMinimumSize(iconSize);
icon->setMaximumSize(iconSize);
toolboxLayout->addItem(icon);
}
}
//add the returntosource icon if we are detached, and have a source applet.
if (q->isDetached() && sourceApplet) {
Icon *returnToSource = new Icon(q);
returnToSource->setSvg("widgets/configuration-icons", "return-to-source");
QSizeF iconSize = returnToSource->sizeFromIconSize(iconHeight);
returnToSource->setMinimumSize(iconSize);
returnToSource->setMaximumSize(iconSize);
toolboxLayout->addItem(returnToSource);
QObject::connect(returnToSource, SIGNAL(clicked()), q, SLOT(moveBackToSource()));
}
toolboxLayout->updateGeometry();
//position the toolbox correctly.
QSizeF minimum = toolboxLayout->minimumSize();
toolbox->resize(minimum);
repositionToolbox();
}
void ExtenderItemPrivate::repositionToolbox()
{
QSizeF minimum = toolboxLayout->minimumSize();
toolbox->setPos(q->size().width() - minimum.width() - bgRight,
((dragHandleRect().height() + dragTop + dragBottom)/2) -
(minimum.height()/2) + bgTop);
}
//TODO: something like this as static function in corona might be a good idea.
QPointF ExtenderItemPrivate::scenePosFromScreenPos(const QPoint &pos) const
{
//get the stacking order of the toplevel windows and remove the toplevel view that's
//only here while dragging, since we're not interested in finding that.
QList<WId> order = KWindowSystem::stackingOrder();
if (toplevel) {
order.removeOne(toplevel->winId());
}
QGraphicsView *found = 0;
foreach (QWidget *w, QApplication::topLevelWidgets()) {
QGraphicsView *v = 0;
//first check if we're over a Dialog.
Dialog *dialog = qobject_cast<Dialog*>(w);
if (dialog) {
if (dialog->isVisible() && dialog->geometry().contains(pos)) {
v = qobject_cast<QGraphicsView*>(dialog->layout()->itemAt(0)->widget());
if (v) {
return v->mapToScene(v->mapFromGlobal(pos));
}
}
} else {
v = qobject_cast<QGraphicsView *>(w);
}
//else check if it is a QGV:
if (v && w->isVisible() && w->geometry().contains(pos)) {
if (found && order.contains(found->winId())) {
if (order.indexOf(found->winId()) < order.indexOf(v->winId())) {
found = v;
}
} else {
found = v;
}
}
}
if (!found) {
return QPointF();
}
return found->mapToScene(found->mapFromGlobal(pos));
}
Applet *ExtenderItemPrivate::hostApplet() const
{
if (extender) {
return extender->d->applet;
} else {
return 0;
}
}
void ExtenderItemPrivate::themeChanged()
{
QSizeF iconSize = dragger->elementSize("hint-preferred-icon-size");
background->setImagePath("widgets/extender-background");
if (mousePressed) {
background->setEnabledBorders(PanelSvg::AllBorders);
} else {
background->setEnabledBorders(extender->enabledBordersForItem(q));
}
background->getMargins(bgLeft, bgTop, bgRight, bgBottom);
dragger->setImagePath("widgets/extender-dragger");
dragger->getMargins(dragLeft, dragTop, dragRight, dragBottom);
QSizeF panelSize(QSizeF(q->size().width() - bgLeft - bgRight,
iconSize.height() + dragTop + dragBottom));
//resize the collapse icon.
collapseIcon->resize(collapseIcon->sizeFromIconSize(iconSize.height()));
//reposition the collapse icon based on the new margins and size.
collapseIcon->setPos(bgLeft + dragLeft,
(panelSize.height() + dragTop + dragBottom)/2 -
collapseIcon->size().height()/2 + bgTop);
//reposition the widget based on the new margins.
if (widget) {
widget->setPos(QPointF(bgLeft + dragLeft, panelSize.height() +
bgTop + dragTop));
}
//reposition the toolbox.
repositionToolbox();
//setCollapsed recalculates size hints.
q->setCollapsed(q->isCollapsed());
q->update();
}
uint ExtenderItemPrivate::s_maxExtenderItemId = 0;
} // namespace Plasma
#include "extenderitem.moc"

View File

@ -213,6 +213,7 @@ class PLASMA_EXPORT ExtenderItem : public QGraphicsWidget
ExtenderItemPrivate * const d;
friend class Extender;
friend class ExtenderPrivate;
};
} // namespace Plasma
#endif // PLASMA_EXTENDERITEM_H

View File

@ -185,6 +185,11 @@ void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints)
q->setLayout(lay);
}
Extender *extender = qobject_cast<Extender*>(gWidget);
if (extender) {
extender->setExtenderAppearance(Extender::NoBorders);
}
q->setMinimumSize(gWidget->minimumSize() + marginSize);
lay->addItem(gWidget);
//gWidget->installEventFilter(q);
@ -235,6 +240,15 @@ void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints)
if (gWidget) {
Corona *corona = qobject_cast<Corona *>(gWidget->scene());
Extender *extender = qobject_cast<Extender*>(gWidget);
if (extender) {
if (q->location() == TopEdge) {
extender->setExtenderAppearance(Extender::TopDownStacked);
} else {
extender->setExtenderAppearance(Extender::BottomUpStacked);
}
}
//could that cast ever fail??
if (corona) {
corona->addOffscreenWidget(gWidget);
@ -289,9 +303,8 @@ bool PopupApplet::eventFilter(QObject *watched, QEvent *event)
QTimer::singleShot(100, this, SLOT(clearPopupLostFocus()));
}
/*
if (watched == graphicsWidget() && (event->type() == QEvent::GraphicsSceneResize)) {
kDebug() << "do the resize!";
/**
if (layout() && watched == graphicsWidget() && (event->type() == QEvent::GraphicsSceneResize)) {
//sizes are recalculated in the constraintsevent so let's just call that.
d->popupConstraintsEvent(Plasma::FormFactorConstraint);

View File

@ -23,6 +23,7 @@
#include <QString>
#include <QList>
#include <QPointF>
#include "../extender.h"
class QGraphicsGridLayout;
class QGraphicsLinearLayout;
@ -45,9 +46,10 @@ class ExtenderPrivate
void addExtenderItem(ExtenderItem *item, const QPointF &pos = QPointF(-1, -1));
void removeExtenderItem(ExtenderItem *item);
void adjustSizeHints();
int insertIndexFromPos(const QPointF &pos) const;
void loadExtenderItems();
void updateBorders();
void adjustSize();
Extender *q;
@ -67,6 +69,8 @@ class ExtenderPrivate
bool popup;
Extender::Appearance appearance;
static QGraphicsGridLayout *s_popupLayout;
};

View File

@ -19,18 +19,15 @@
#include "extenderapplet_p.h"
#include "extender.h"
#include "extenderitem.h"
#include <KDebug>
#include <KIcon>
#include "../extender.h"
#include "../extenderitem.h"
#include <QGraphicsLinearLayout>
ExtenderApplet::ExtenderApplet(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
ExtenderApplet::~ExtenderApplet()
@ -39,44 +36,28 @@ ExtenderApplet::~ExtenderApplet()
void ExtenderApplet::init()
{
new Plasma::Extender(this);
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout();
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
layout->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
setLayout(layout);
extender()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
connect(extender(), SIGNAL(geometryChanged()), this, SLOT(adjustSize()));
connect(extender(), SIGNAL(itemDetached(Plasma::ExtenderItem*)),
this, SLOT(itemDetached(Plasma::ExtenderItem*)));
layout->addItem(extender());
adjustSize();
}
void ExtenderApplet::adjustSize()
{
layout()->updateGeometry();
qreal left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
extender()->setExtenderAppearance(Plasma::Extender::NoBorders);
extender()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
setPreferredSize(QSizeF(left + right + layout()->preferredWidth(),
top + bottom + layout()->preferredHeight()));
setMinimumSize(QSizeF(left + right + layout()->minimumWidth(),
top + bottom + layout()->minimumHeight()));
setMaximumSize(QSizeF(left + right + layout()->maximumWidth(),
top + bottom + layout()->maximumHeight()));
connect(extender(), SIGNAL(itemDetached(Plasma::ExtenderItem*)),
this, SLOT(itemDetached(Plasma::ExtenderItem*)));
layout->addItem(extender());
updateGeometry();
//This wasn't necesarry before... but it is now. weirdness.
resize(size().width(), preferredHeight());
layout()->invalidate();
}
void ExtenderApplet::itemDetached(Plasma::ExtenderItem *)
{
kDebug() << "item detached: " << extender()->attachedItems().count();
if (!extender()->attachedItems().count()) {
destroy();
//FIXME: only fire the signal when the item is really detached, not just being dragged away.
kDebug() << "delete the extender applet...";
}
}

View File

@ -22,6 +22,11 @@
#include "applet.h"
/**
* This class is used as a 'host' for detached extender items. When an extender item is dropped
* somewhere, this applet is added at the location where the item is dropped, and the item is added
* to it's extender.
*/
class ExtenderApplet : public Plasma::Applet
{
Q_OBJECT
@ -33,7 +38,6 @@ class ExtenderApplet : public Plasma::Applet
public Q_SLOTS:
void itemDetached(Plasma::ExtenderItem *);
void adjustSize();
};
#endif

View File

@ -1,4 +1,101 @@
#ifndef WIDGETS_EXTENDERITEM_P_H
#define WIDGETS_EXTENDERITEM_P_H
/*
* Copyright 2008 by Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#endif // WIDGETS_EXTENDERITEM_P_H
#ifndef LIBS_PLASMA_EXTENDERITEM_P_H
#define LIBS_PLASMA_EXTENDERITEM_P_H
#include <QPointF>
#include <QPoint>
#include <QRect>
#include <QString>
class QGraphicsItem;
class QGraphicsWidget;
class QGraphicsLinearLayout;
class QGraphicsView;
class QTimer;
namespace Plasma
{
class Applet;
class ExtenderItem;
class Extender;
class Icon;
class PanelSvg;
class ExtenderItemPrivate
{
public:
ExtenderItemPrivate(ExtenderItem *extenderItem, Extender *hostExtender);
~ExtenderItemPrivate();
QRectF dragHandleRect();
QRectF titleRect();
bool leaveCurrentView(const QRect &rect);
QRect screenRect();
void toggleCollapse();
void updateToolBox();
void repositionToolbox();
QPointF scenePosFromScreenPos(const QPoint &pos) const;
Applet *hostApplet() const;
void themeChanged();
ExtenderItem *q;
QGraphicsItem *widget;
QGraphicsWidget *toolbox;
QGraphicsLinearLayout *toolboxLayout;
QGraphicsView *toplevel;
Extender *previousTargetExtender;
Extender *extender;
Applet *sourceApplet;
KConfigGroup config;
PanelSvg *dragger;
PanelSvg *background;
Icon *collapseIcon;
QMap<QString, QAction*> actions;
QString title;
QString name;
uint sourceAppletId;
uint extenderItemId;
qreal dragLeft, dragTop, dragRight, dragBottom;
qreal bgLeft, bgTop, bgRight, bgBottom;
QPointF deltaScene;
QPoint mousePos;
bool mousePressed;
bool mouseOver;
QTimer *expirationTimer;
static uint s_maxExtenderItemId;
};
}
#endif // LIB_PLASMA_EXTENDERITEM_P_H