From a26b73b6bb86bfa12a6c1b86439c40a27b8f1cb6 Mon Sep 17 00:00:00 2001 From: Rob Scheepmaker Date: Mon, 20 Oct 2008 10:58:37 +0000 Subject: [PATCH] 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 --- dialog.cpp | 51 +++- extender.cpp | 106 +++++-- extender.h | 44 +++ extenderitem.cpp | 576 ++++++++++++++++++------------------- extenderitem.h | 1 + popupapplet.cpp | 19 +- private/extender_p.h | 6 +- private/extenderapplet.cpp | 43 +-- private/extenderapplet_p.h | 6 +- private/extenderitem_p.h | 103 ++++++- 10 files changed, 595 insertions(+), 360 deletions(-) diff --git a/dialog.cpp b/dialog.cpp index 7cb21b813..928f04822 100644 --- a/dialog.cpp +++ b/dialog.cpp @@ -38,8 +38,11 @@ #include #include -#include -#include +#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 @@ -89,7 +92,39 @@ void DialogPrivate::themeUpdated() const int leftWidth = background->marginSize(Plasma::LeftMargin); const int rightWidth = background->marginSize(Plasma::RightMargin); const int bottomHeight = background->marginSize(Plasma::BottomMargin); - q->setContentsMargins(leftWidth, topHeight, rightWidth, bottomHeight); + + //TODO: correct handling of the situation when having vertical panels. + Extender *extender = qobject_cast(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); diff --git a/extender.cpp b/extender.cpp index 12a3d1705..3695345e6 100644 --- a/extender.cpp +++ b/extender.cpp @@ -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(d->layout->itemAt(0)); + ExtenderItem *bottomItem = dynamic_cast(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(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 diff --git a/extender.h b/extender.h index 310d036d0..ef0e207cc 100644 --- a/extender.h +++ b/extender.h @@ -22,6 +22,7 @@ #include +#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; diff --git a/extenderitem.cpp b/extenderitem.cpp index 766b78f11..63596255f 100644 --- a/extenderitem.cpp +++ b/extenderitem.cpp @@ -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(widget); - QGraphicsView *currentV = 0; - - if (hostApplet()) { - currentV = qobject_cast(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(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 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(w); - if (dialog) { - if (dialog->isVisible() && dialog->geometry().contains(pos)) { - v = qobject_cast(dialog->layout()->itemAt(0)->widget()); - if (v) { - return v->mapToScene(v->mapFromGlobal(pos)); - } - } - } else { - v = qobject_cast(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 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(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(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->elementSize("hint-preferred-icon-size").height() + - d->dragTop + d->dragBottom); - d->dragger->resizePanel(newDraggerSize); + d->dragger->resizePanel(QSizeF(width - d->bgLeft - d->bgRight, + d->dragger->elementSize("hint-preferred-icon-size").height() + + 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(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(widget); + QGraphicsView *currentV = 0; + + if (hostApplet()) { + currentV = qobject_cast(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(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 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(w); + if (dialog) { + if (dialog->isVisible() && dialog->geometry().contains(pos)) { + v = qobject_cast(dialog->layout()->itemAt(0)->widget()); + if (v) { + return v->mapToScene(v->mapFromGlobal(pos)); + } + } + } else { + v = qobject_cast(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" diff --git a/extenderitem.h b/extenderitem.h index b8f17f4ab..e7c19da7a 100644 --- a/extenderitem.h +++ b/extenderitem.h @@ -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 diff --git a/popupapplet.cpp b/popupapplet.cpp index 0b89a800e..db2f58674 100644 --- a/popupapplet.cpp +++ b/popupapplet.cpp @@ -185,6 +185,11 @@ void PopupAppletPrivate::popupConstraintsEvent(Plasma::Constraints constraints) q->setLayout(lay); } + Extender *extender = qobject_cast(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(gWidget->scene()); + Extender *extender = qobject_cast(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); diff --git a/private/extender_p.h b/private/extender_p.h index d645cb3bb..98ac773bd 100644 --- a/private/extender_p.h +++ b/private/extender_p.h @@ -23,6 +23,7 @@ #include #include #include +#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; }; diff --git a/private/extenderapplet.cpp b/private/extenderapplet.cpp index bf4d22b55..5ef98081f 100644 --- a/private/extenderapplet.cpp +++ b/private/extenderapplet.cpp @@ -19,18 +19,15 @@ #include "extenderapplet_p.h" -#include "extender.h" -#include "extenderitem.h" - -#include -#include +#include "../extender.h" +#include "../extenderitem.h" #include 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..."; } } diff --git a/private/extenderapplet_p.h b/private/extenderapplet_p.h index e73381e6f..6b49ab40e 100644 --- a/private/extenderapplet_p.h +++ b/private/extenderapplet_p.h @@ -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 diff --git a/private/extenderitem_p.h b/private/extenderitem_p.h index cae6d160c..b563bcf98 100644 --- a/private/extenderitem_p.h +++ b/private/extenderitem_p.h @@ -1,4 +1,101 @@ -#ifndef WIDGETS_EXTENDERITEM_P_H -#define WIDGETS_EXTENDERITEM_P_H +/* + * Copyright 2008 by Rob Scheepmaker + * + * 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 +#include +#include +#include + +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 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