diff --git a/plasma/widgets/tooltip.cpp b/plasma/widgets/tooltip.cpp index 900f1b3da7..51dc208c26 100644 --- a/plasma/widgets/tooltip.cpp +++ b/plasma/widgets/tooltip.cpp @@ -1,319 +1,347 @@ /* * Copyright 2007 by Dan Meltzer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tooltip_p.h" #include #include #include #include #include #include #include #ifdef Q_WS_X11 #include #endif -#include -#include +#include +#include +#include #include #ifdef Q_WS_X11 #include #include #endif #include "plasma/plasma.h" namespace Plasma { class ToolTip::Private { public: Private() : label(0) , imageLabel(0) , preview(0) , windowToPreview(0) , currentWidget(0) , isShown(false) + , delayedHide(false) , showTimer(0) , hideTimer(0) { } QLabel *label; QLabel *imageLabel; WindowPreview *preview; WId windowToPreview; Plasma::Widget *currentWidget; bool isShown; + bool delayedHide; QTimer *showTimer; QTimer *hideTimer; }; class ToolTipSingleton { public: ToolTip self; }; K_GLOBAL_STATIC( ToolTipSingleton, privateInstance ) ToolTip *ToolTip::self() { return &privateInstance->self; } -void ToolTip::show(const QPoint &location, Plasma::Widget *widget) +void ToolTip::show(Plasma::Widget *widget) { + d->hideTimer->stop(); + d->delayedHide = false; d->currentWidget = widget; - setData(widget->toolTip()); - - // Make the tooltip use Plasma's colorscheme - QPalette plasmaPalette = QPalette(); - plasmaPalette.setColor(QPalette::Window, Plasma::Theme::self()->backgroundColor()); - plasmaPalette.setColor(QPalette::WindowText, Plasma::Theme::self()->textColor()); - setAutoFillBackground(true); - setPalette(plasmaPalette); - - move(location.x(), location.y()); + d->showTimer->stop(); if (d->isShown) { - // Don't delay if the tooltip is already shown(i.e. moving from one task to another) - // Qt doesn't seem to like visible tooltips moving though, so hide it and then - // immediately show it again - setVisible(false); - // small delay to prevent unecessary showing when the mouse is moving quickly across items // which can be too much for less powerful CPUs to keep up with - d->showTimer->start(150); + d->showTimer->start(200); } else { d->showTimer->start(500); } } +void ToolTip::delayedHide() +{ + d->showTimer->stop(); // stop the timer to show the tooltip + d->delayedHide = true; + d->hideTimer->start(250); +} + void ToolTip::hide() { d->currentWidget = 0; d->showTimer->stop(); //Mouse out, stop the timer to show the tooltip + d->delayedHide = false; setVisible(false); d->hideTimer->start(250); //250 ms delay before we are officially "gone" to allow for the time to move between widgets } Plasma::Widget *ToolTip::currentWidget() const { return d->currentWidget; } //PRIVATE FUNCTIONS -void ToolTip::slotShowToolTip() +void ToolTip::showToolTip() { + if (!d->currentWidget || !d->currentWidget->toolTip()) { + return; + } + QGraphicsView *v = d->currentWidget->view(); if (v && v->mouseGrabber()) { return; } + setData(*d->currentWidget->toolTip()); + if( d->windowToPreview != 0 ) { // show/hide the preview area d->preview->show(); } else { d->preview->hide(); } layout()->activate(); d->isShown = true; //ToolTip is visible setVisible(true); + resize(sizeHint()); + move(d->currentWidget->popupPosition(size())); } -void ToolTip::slotResetTimer() +void ToolTip::resetShownState() { - if (!isVisible()) { //One might have moused out and back in again + if (!isVisible() || //One might have moused out and back in again + d->delayedHide) { + d->delayedHide = false; d->isShown = false; + d->currentWidget = 0; + setVisible(false); } } void ToolTip::showEvent(QShowEvent *e) { QWidget::showEvent(e); d->preview->setInfo(); } void ToolTip::mouseReleaseEvent(QMouseEvent* event) { if (rect().contains(event->pos())) { hide(); } } ToolTip::ToolTip() : QWidget(0) , d( new Private ) { setWindowFlags(Qt::ToolTip); QGridLayout *l = new QGridLayout; d->preview = new WindowPreview; d->label = new QLabel; d->label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); d->label->setWordWrap(true); d->imageLabel = new QLabel; d->imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); l->addWidget(d->preview, 0, 0, 1, 2); l->addWidget(d->imageLabel, 1, 0); l->addWidget(d->label, 1, 1); setLayout(l); d->showTimer = new QTimer(this); d->showTimer->setSingleShot(true); d->hideTimer = new QTimer(this); d->hideTimer->setSingleShot(true); - connect(d->showTimer, SIGNAL(timeout()), SLOT(slotShowToolTip())); - connect(d->hideTimer, SIGNAL(timeout()), SLOT(slotResetTimer())); + connect(d->showTimer, SIGNAL(timeout()), SLOT(showToolTip())); + connect(d->hideTimer, SIGNAL(timeout()), SLOT(resetShownState())); + + connect(Plasma::Theme::self(), SIGNAL(changed()), this, SLOT(resetPalette())); + resetPalette(); +} + +ToolTip::~ToolTip() +{ + delete d; } void ToolTip::setData(const Plasma::ToolTipData &data) { //reset our size - d->label->setText("" + data.mainText + "
" + - data.subText + "
"); + d->label->setText("" + data.mainText + "
" + data.subText + "
"); d->imageLabel->setPixmap(data.image); d->windowToPreview = data.windowToPreview; d->preview->setWindowId( d->windowToPreview ); - resize(sizeHint()); + if (isVisible()) { + resize(sizeHint()); + + if (d->currentWidget) { + move(d->currentWidget->popupPosition(size())); + } + } } -ToolTip::~ToolTip() +void ToolTip::resetPalette() { - delete d; + // Make the tooltip use Plasma's colorscheme + QPalette plasmaPalette = QPalette(); + plasmaPalette.setColor(QPalette::Window, Plasma::Theme::self()->backgroundColor()); + plasmaPalette.setColor(QPalette::WindowText, Plasma::Theme::self()->textColor()); + setAutoFillBackground(true); + setPalette(plasmaPalette); } - // A widget which reserves area for window preview and sets hints on the toplevel // tooltip widget that tells KWin to render the preview in this area. This depends // on KWin's TaskbarThumbnail compositing effect (which is here detected). void WindowPreview::setWindowId(WId w) { if (!previewsAvailable()) { id = 0; return; } id = w; readWindowSize(); } bool WindowPreview::previewsAvailable() const { if (!KWindowSystem::compositingActive()) { return false; } #ifdef Q_WS_X11 // hackish way to find out if KWin has the effect enabled, // TODO provide proper support Display* dpy = QX11Info::display(); Atom atom = XInternAtom(dpy, "_KDE_WINDOW_PREVIEW", False); int cnt; Atom* list = XListProperties(dpy, DefaultRootWindow( dpy ), &cnt); if (list != NULL) { bool ret = ( qFind(list, list + cnt, atom) != list + cnt ); XFree(list); return ret; } #endif return false; } QSize WindowPreview::sizeHint() const { if (id == 0) { return QSize(); } if (!windowSize.isValid()) { readWindowSize(); } QSize s = windowSize; s.scale(200, 150, Qt::KeepAspectRatio); return s; } void WindowPreview::readWindowSize() const { #ifdef Q_WS_X11 Window r; int x, y; unsigned int w, h, b, d; if (XGetGeometry(QX11Info::display(), id, &r, &x, &y, &w, &h, &b, &d)) { windowSize = QSize( w, h ); } else { windowSize = QSize(); } #else windowSize = QSize(); #endif } void WindowPreview::setInfo() { #ifdef Q_WS_X11 Display *dpy = QX11Info::display(); Atom atom = XInternAtom(dpy, "_KDE_WINDOW_PREVIEW", False); if (id == 0) { XDeleteProperty(dpy, winId(), atom); return; } if (!windowSize.isValid()) { readWindowSize(); } if (!windowSize.isValid()) { XDeleteProperty(dpy, winId(), atom); return; } Q_ASSERT( parentWidget()->isWindow()); // parent must be toplevel long data[] = { 1, 5, id, x(), y(), width(), height() }; XChangeProperty(dpy, parentWidget()->winId(), atom, atom, 32, PropModeReplace, reinterpret_cast< unsigned char* >( data ), sizeof( data ) / sizeof( data[ 0 ] )); #endif } void ToolTip::resizeEvent(QResizeEvent *) { QBitmap mask(width(), height()); QPainter painter(&mask); mask.fill(Qt::white); painter.setBrush(Qt::black); painter.setPen(Qt::black); painter.drawPath(roundedRectangle(mask.rect(), 10)); setMask(mask); } void ToolTip::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); //Stroke border painter.setPen(Plasma::Theme::self()->textColor()); painter.drawPath(roundedRectangle(rect(), 10)); } } #include "tooltip_p.moc" diff --git a/plasma/widgets/tooltip_p.h b/plasma/widgets/tooltip_p.h index 626c43dd13..98b371d040 100644 --- a/plasma/widgets/tooltip_p.h +++ b/plasma/widgets/tooltip_p.h @@ -1,87 +1,89 @@ /* * Copyright 2007 by Dan Meltzer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PLASMATOOLTIP_H #define PLASMATOOLTIP_H #include //ToolTipData struct #include // base class #include // stack allocated #include // stack allocated #include // stack allocated namespace Plasma { class ToolTipData; /** @author Dan Meltzer * A Singleton tooltip. Before calling show it is necessary to * call setLocation and setData */ class ToolTip : public QWidget { Q_OBJECT public: ToolTip(); ~ToolTip(); static ToolTip *self(); - void show(const QPoint &location, Plasma::Widget *widget); + void show(Plasma::Widget *widget); + void delayedHide(); void hide(); Plasma::Widget *currentWidget() const; protected: void showEvent(QShowEvent *); void mouseReleaseEvent(QMouseEvent *); void resizeEvent(QResizeEvent *); void paintEvent(QPaintEvent *); private Q_SLOTS: - void slotResetTimer(); - void slotShowToolTip(); + void resetShownState(); + void showToolTip(); private: void setData(const Plasma::ToolTipData &data); + void resetPalette(); class Private; Private *const d; }; class WindowPreview : public QWidget { Q_OBJECT public: void setWindowId(WId w); void setInfo(); virtual QSize sizeHint() const; bool previewsAvailable() const; private: void readWindowSize() const; WId id; mutable QSize windowSize; }; } #endif diff --git a/plasma/widgets/widget.cpp b/plasma/widgets/widget.cpp index 33d5f3cd55..1027e0eca3 100644 --- a/plasma/widgets/widget.cpp +++ b/plasma/widgets/widget.cpp @@ -1,655 +1,671 @@ /* * Copyright 2007 by Alexander Wiedenbruch * and Matias Valdenegro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "layouts/freelayout.h" #include "plasma/plasma.h" #include "plasma/view.h" #include "plasma/containment.h" #include "tooltip_p.h" namespace Plasma { class Widget::Private { public: Private() : minimumSize(0,0), maximumSize(std::numeric_limits::infinity(), std::numeric_limits::infinity()), opacity(1.0), cachePaintMode(Widget::NoCacheMode), - wasMovable(false) + wasMovable(false), + toolTip(0) { } - ~Private() { } + + ~Private() + { + delete toolTip; + } QSizeF size; QSizeF minimumSize; QSizeF maximumSize; qreal opacity; // Replace with CacheMode in 4.4 #if QT_VERSION >= 0x040400 #warning Replace Plasma::Widget::CachePaintMode with QGraphicsItem::CacheMode #endif Widget::CachePaintMode cachePaintMode; QSize cacheSize; QString cacheKey; QRectF cacheInvalidated; bool wasMovable; bool shouldPaint(QPainter *painter, const QTransform &transform); - ToolTipData toolTip; + ToolTipData *toolTip; }; QGraphicsItem* Widget::graphicsItem() { return this; } QGraphicsView *Widget::view() const { // It's assumed that we won't be visible on more than one view here. // Anything that actually needs view() should only really care about // one of them anyway though. if (!scene()) { return 0; } foreach (QGraphicsView *view, scene()->views()) { if (view->sceneRect().intersects(sceneBoundingRect()) || view->sceneRect().contains(scenePos())) { return view; } } return 0; } QRectF Widget::mapFromView(const QGraphicsView *view, const QRect &rect) const { // TODO: Confirm that adjusted() is needed and is not covering for some // issue elsewhere return mapFromScene(view->mapToScene(rect)).boundingRect().adjusted(0, 0, 1, 1); } QRect Widget::mapToView(const QGraphicsView *view, const QRectF &rect) const { // TODO: Confirm that adjusted() is needed and is not covering for some // issue elsewhere return view->mapFromScene(mapToScene(rect)).boundingRect().adjusted(0, 0, -1, -1); } bool Widget::Private::shouldPaint(QPainter *painter, const QTransform &transform) { Q_UNUSED(painter) Q_UNUSED(transform) //qreal zoomLevel = painter->transform().m11() / transform.m11(); //return (fabs(zoomLevel - scalingFactor(Plasma::DesktopZoom))) < std::numeric_limits::epsilon(); return true; } Widget::Widget(QGraphicsItem *parent, QObject* parentObject) : QObject(parentObject), QGraphicsItem(parent), d(new Private) { setFlag(QGraphicsItem::ItemClipsToShape, true); setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); setCachePaintMode(DeviceCoordinateCacheMode); Widget *w = dynamic_cast(parent); if (w) { w->addChild(this); } } Widget::~Widget() { if (ToolTip::self()->currentWidget() == this) { ToolTip::self()->hide(); } delete d; } void Widget::setOpacity(qreal opacity) { d->opacity = opacity; } qreal Widget::opacity() const { return d->opacity; } void Widget::setCachePaintMode(CachePaintMode mode, const QSize &size) { d->cachePaintMode = mode; if (mode == NoCacheMode) { QPixmapCache::remove(d->cacheKey); d->cacheKey.clear(); } else { d->cacheKey = QString("%1").arg(long(this)); if (mode == ItemCoordinateCacheMode) { d->cacheSize = size.isNull() ? boundingRect().size().toSize() : size; } } } Widget::CachePaintMode Widget::cachePaintMode() const { return d->cachePaintMode; } void Widget::update(const QRectF &rect) { if (d->cachePaintMode != NoCacheMode) { d->cacheInvalidated |= rect.isNull() ? boundingRect() : rect; } QGraphicsItem::update(rect); } Qt::Orientations Widget::expandingDirections() const { return Qt::Horizontal | Qt::Vertical; } void Widget::setMinimumSize(const QSizeF& size) { d->minimumSize = size; if (d->size != d->size.expandedTo(size)) { d->size = d->size.expandedTo(size); updateGeometry(); } } QSizeF Widget::minimumSize() const { return d->minimumSize; } void Widget::setMaximumSize(const QSizeF& size) { d->maximumSize = size; if (d->size != d->size.boundedTo(size)) { d->size = d->size.boundedTo(size); updateGeometry(); } } QSizeF Widget::maximumSize() const { return d->maximumSize; } bool Widget::hasHeightForWidth() const { return false; } qreal Widget::heightForWidth(qreal w) const { Q_UNUSED(w); return -1.0; } bool Widget::hasWidthForHeight() const { return false; } qreal Widget::widthForHeight(qreal h) const { Q_UNUSED(h); return -1.0; } QRectF Widget::geometry() const { return QRectF(pos(), d->size); } #if 0 QRectF Widget::localGeometry() const { return QRectF(QPointF(0.0f, 0.0f), boundingRect().size); } #endif void Widget::setGeometry(const QRectF& geometry) { if (geometry.size().width() > 0 && geometry.size().height() > 0 && d->size != geometry.size()) { prepareGeometryChange(); qreal width = qBound(d->minimumSize.width(), geometry.size().width(), d->maximumSize.width()); qreal height = qBound(d->minimumSize.height(), geometry.size().height(), d->maximumSize.height()); d->size = QSizeF(width, height); if (layout()) { layout()->setGeometry(QRectF(QPointF(0, 0), d->size)); } if (managingLayout()) { managingLayout()->invalidate(); } } setPos(geometry.topLeft()); update(); } void Widget::setSize(qreal width, qreal height) { setSize(QSizeF(width, height)); } void Widget::setSize(const QSizeF& size) { d->size = size; } void Widget::updateGeometry() { if (managingLayout()) { managingLayout()->invalidate(); } else { setGeometry(QRectF(pos(), sizeHint())); } } QSizeF Widget::sizeHint() const { if (layout()) { return layout()->sizeHint(); } else { return d->size; } } QSizeF Widget::size() const { return d->size; } QFont Widget::font() const { return QApplication::font(); } /* void Widget::setFont(const QFront& font) { } */ QRectF Widget::boundingRect() const { return QRectF(QPointF(0,0), d->size); } void Widget::resize(const QSizeF& size) { //FIXME: we need to respect minimum size here i think setGeometry(QRectF(pos(), size)); } void Widget::resize(qreal w, qreal h) { resize(QSizeF(w, h)); } Widget *Widget::parent() const { return parent(this); } Widget *Widget::parent(const QGraphicsItem *item) { Q_ASSERT(item); QGraphicsItem *parent = item->parentItem(); while (parent) { Widget *parentWidget = dynamic_cast(parent); if (parentWidget) { return parentWidget; } parent = parent->parentItem(); } return 0; } void Widget::addChild(Widget *w) { if (!w || QGraphicsItem::children().contains(w)) { return; } w->setParentItem(this); //kDebug() << "Added Child Widget" << (QObject*)w << "our geom is" << geometry(); if (layout()) { layout()->addItem(w); } updateGeometry(); //kDebug() << "after the item is added our geom is now" << geometry(); } void Widget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (d->opacity < 1.0) { painter->setOpacity(painter->opacity() * d->opacity); } /* NOTE: put this back if we end up needing to control when things paint due to, e.g. zooming. if (!d->shouldPaint(painter, transform())) { return; } */ if (d->cachePaintMode == NoCacheMode) { paintWidget(painter, option, widget); return; } // Cached painting QRectF brect = boundingRect(); // Fetch the off-screen transparent buffer and exposed area info. QPixmap pix; QPixmapCache::find(d->cacheKey, pix); QRectF exposed = d->cacheInvalidated; // Render using item coodinate cache mode. if (d->cachePaintMode == ItemCoordinateCacheMode) { // Recreate the pixmap if it's gone. if (pix.isNull()) { pix = QPixmap(d->cacheSize); pix.fill(Qt::transparent); exposed = brect; } // Check for newly invalidated areas. if (!exposed.isNull()) { d->cacheInvalidated = QRectF(); QStyleOptionGraphicsItem cacheOption = *option; cacheOption.exposedRect = exposed.toRect(); // <- truncation QPainter pixmapPainter(&pix); // Fit the item's bounding rect into the pixmap's coordinates. pixmapPainter.scale(pix.width() / brect.width(), pix.height() / brect.height()); pixmapPainter.translate(-brect.topLeft()); // erase the exposed area so we don't paint over and over pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); pixmapPainter.fillRect(exposed, Qt::transparent); pixmapPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); // Re-render the invalidated areas of the pixmap. Important: don't // fool the item into using the widget - pass 0 instead of \a // widget. paintWidget(&pixmapPainter, &cacheOption, 0); pixmapPainter.end(); // Reinsert this pixmap into the cache QPixmapCache::insert(d->cacheKey, pix); } // Redraw the exposed area using the transformed painter. Depending on // the hardware, this may be a server-side operation, or an expensive // qpixmap-image-transform-pixmap roundtrip. painter->drawPixmap(brect, pix, QRectF(QPointF(), pix.size())); return; } // Render using device coordinate cache mode. if (d->cachePaintMode == DeviceCoordinateCacheMode) { QTransform transform = painter->worldTransform(); QRect deviceRect = transform.mapRect(brect).toRect(); if (deviceRect.width() < 1 || deviceRect.height() < 1) { // we have nothing to paint! return; } // Auto-adjust the pixmap size. if (deviceRect.size() != pix.size()) { pix = QPixmap(deviceRect.size()); pix.fill(Qt::transparent); exposed = brect; } // Check for newly invalidated areas. if (!exposed.isNull()) { d->cacheInvalidated = QRectF(); // Construct the new styleoption, reset the exposed rect. QStyleOptionGraphicsItem cacheOption = *option; cacheOption.exposedRect = exposed.toRect(); // <- truncation QPointF viewOrigo = transform.map(QPointF(0, 0)); QPointF offset = viewOrigo - deviceRect.topLeft(); // Transform the painter, and render the item in device coordinates. QPainter pixmapPainter(&pix); pixmapPainter.translate(offset); pixmapPainter.setWorldTransform(transform, true); pixmapPainter.translate(transform.inverted().map(QPointF(0, 0))); // erase the exposed area so we don't paint over and over pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); pixmapPainter.fillRect(exposed, Qt::transparent); pixmapPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); paintWidget(&pixmapPainter, &cacheOption, 0); pixmapPainter.end(); // Reinsert this pixmap into the cache QPixmapCache::insert(d->cacheKey, pix); } // Redraw the exposed area using an untransformed painter. This // effectively becomes a bitblit that does not transform the cache. painter->setWorldTransform(QTransform()); painter->drawPixmap(deviceRect.topLeft(), pix); return; } } -ToolTipData Widget::toolTip() const +const ToolTipData* Widget::toolTip() const { return d->toolTip; } void Widget::setToolTip(const ToolTipData &tip) { - d->toolTip = tip; + if (tip.image.isNull() && + tip.subText.isEmpty() && + tip.mainText.isEmpty()) { + delete d->toolTip; + d->toolTip = 0; + } + + if (!d->toolTip) { + d->toolTip = new ToolTipData; + } + + *d->toolTip = tip; + if (ToolTip::self()->currentWidget() == this) { - ToolTip::self()->show(popupPosition(ToolTip::self()->sizeHint()), this); + ToolTip::self()->show(this); } } void Widget::paintWidget(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); // Replaced by widget's own function } QVariant Widget::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == QGraphicsItem::ItemChildRemovedChange) { if (layout()) { layout()->removeItem(dynamic_cast(value.value())); updateGeometry(); } } return QGraphicsItem::itemChange(change, value); } void Widget::managingLayoutChanged() { if (managingLayout()) { d->wasMovable = flags() & ItemIsMovable; if (!dynamic_cast(managingLayout())) { setFlag(ItemIsMovable, false); } } else { setFlag(ItemIsMovable, d->wasMovable); } } QPoint Widget::popupPosition(const QSize s) const { QPoint pos = view()->mapFromScene(scenePos()); pos = view()->mapToGlobal(pos); Plasma::View *pv = dynamic_cast(view()); Plasma::Location loc = Floating; if (pv) { loc = pv->containment()->location(); } switch (loc) { case BottomEdge: pos = QPoint(pos.x(), pos.y() - s.height()); break; case TopEdge: pos = QPoint(pos.x(), pos.y() + (int)size().height()); break; case LeftEdge: pos = QPoint(pos.x() + (int)size().width(), pos.y()); break; case RightEdge: pos = QPoint(pos.x() - s.width(), pos.y()); break; default: if (pos.y() - s.height() > 0) { pos = QPoint(pos.x(), pos.y() - s.height()); } else { pos = QPoint(pos.x(), pos.y() + (int)size().height()); } } //are we out of screen? QRect screenRect = QApplication::desktop()->screenGeometry(pv->containment()->screen()); if (pos.rx() + s.width() > screenRect.width()) { pos.rx() -= ((pos.rx() + s.width()) - screenRect.width()); } if (pos.ry() + s.height() > screenRect.height()) { pos.ry() -= ((pos.ry() + s.height()) - screenRect.height()); } pos.rx() = qMax(0, pos.rx()); return pos; } void Widget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { // HACK: QGraphicsItem's documentation says that the event will be passed // to the parent if it's not handled, but it isn't passed. This can be // removed when Qt4.4 becomes a requirement. See Qt bug #176902. Widget *parentWidget = parent(); if (parentWidget) { parentWidget->contextMenuEvent(event); } } bool Widget::sceneEvent(QEvent *event) { switch (event->type()) { case QEvent::GraphicsSceneHoverMove: // If the tooltip isn't visible, run through showing the tooltip again // so that it only becomes visible after a stationary hover if (ToolTip::self()->isVisible()) { break; } case QEvent::GraphicsSceneHoverEnter: { // Check that there is a tooltip to show - if (d->toolTip.image.isNull() && - d->toolTip.subText.isEmpty() && - d->toolTip.mainText.isEmpty()) { + if (!d->toolTip) { break; } // If the mouse is in the widget's area at the time that it is being // created the widget can receive a hover event before it is fully // initialized, in which case view() will return 0. QGraphicsView *parentView = view(); if (parentView) { - ToolTip *tip = ToolTip::self(); - tip->adjustSize(); - tip->show(popupPosition(tip->size()), this); + ToolTip::self()->show(this); } break; } case QEvent::GraphicsSceneHoverLeave: + ToolTip::self()->delayedHide(); + break; + case QEvent::GraphicsSceneMousePress: case QEvent::GraphicsSceneWheel: ToolTip::self()->hide(); default: break; } return QGraphicsItem::sceneEvent(event); } } // Plasma namespace diff --git a/plasma/widgets/widget.h b/plasma/widgets/widget.h index e137c9ca97..efbd694042 100644 --- a/plasma/widgets/widget.h +++ b/plasma/widgets/widget.h @@ -1,325 +1,325 @@ /* * Copyright 2007 by Alexander Wiedenbruch * and Matias Valdenegro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WIDGET_H_ #define WIDGET_H_ #include #include #include #include #include #include #include class QGraphicsView; class QGraphicsSceneHoverEvent; namespace Plasma { struct PLASMA_EXPORT ToolTipData { ToolTipData() : windowToPreview( 0 ) {} QString mainText; //Important information QString subText; //Elaborates on the Main Text QPixmap image; // Icon to show; WId windowToPreview; // Id of window to show preview }; class Layout; /** * Base class for all Widgets in Plasma. * * @author Alexander Wiedenbruch * @author Matias Valdenegro * * Widgets are the basis for User Interfaces inside Plasma. * Widgets are rectangular, but can be in any visible shape by just using transparency to mask * out non-rectangular areas. * * To implement a Widget, just subclass Plasma::Widget and implement at minimum, * sizeHint() and paintWidget() */ class PLASMA_EXPORT Widget : public QObject, public QGraphicsItem, public LayoutItem { Q_OBJECT Q_PROPERTY( Qt::Orientations expandingDirections READ expandingDirections ) Q_PROPERTY( QSizeF minimumSize READ minimumSize WRITE setMinimumSize ) Q_PROPERTY( QSizeF maximumSize READ maximumSize WRITE setMaximumSize ) Q_PROPERTY( QRectF geometry READ geometry WRITE setGeometry ) Q_PROPERTY( QSizeF sizeHint READ sizeHint ) Q_PROPERTY( QSizeF size READ size WRITE resize ) Q_PROPERTY( qreal opacity READ opacity WRITE setOpacity ) public: enum CachePaintMode { NoCacheMode, ItemCoordinateCacheMode, DeviceCoordinateCacheMode }; /** * Creates a new Plasma::Widget. * @param parent the QGraphicsItem this icon is parented to. */ explicit Widget(QGraphicsItem *parent = 0 , QObject *parentObject = 0); /** * Destroys a Plasma::Widget. */ virtual ~Widget(); /** * This method is used by Plasma::Layout to determine which directions the * widget naturally expands. * @return bitmask with the directions that this Widget can be expanded. */ virtual Qt::Orientations expandingDirections() const; /** * Sets the minimum size of the Widget. * @param size the size to set as the minimum size. */ void setMinimumSize(const QSizeF& size); /** * @return minimum size of the Widget. */ QSizeF minimumSize() const; /** * Sets the maximum size of the Widget. * @param size the size to set as the maximum size. */ void setMaximumSize(const QSizeF& size); /** * @return maximum size of the Widget. */ QSizeF maximumSize() const; /** * This method is used by Plasma::Layout to determine whether this widget * can provide a height value given a width value. * @return whether or not this Widget has heightForWidth. */ virtual bool hasHeightForWidth() const; /** * This method is used by Plasma::Layout to determine a height value * given a width value. * @param width the width to use to determine height. * @return height calculated using width given. */ virtual qreal heightForWidth(qreal width) const; /** * This method is used by Plasma::Layout to determine whether this widget * can provide a width value given a height value. * @return whether or not this Widget has widthForHeight. */ virtual bool hasWidthForHeight() const; /** * This method is used by Plasma::Layout to determine a width value * given a height value. * @param height the width to use to determine width. * @return width calculated using height given. */ virtual qreal widthForHeight(qreal h) const; /** * @return geometry of this widget. */ QRectF geometry() const; /** * Sets the geometry of this Widget. */ /** * Sets the geometry of this Plasma::Widget * @param geometry the geometry to apply to this Plasma::Widget. */ virtual void setGeometry(const QRectF &geometry); /** * This method is used to notify any containing Plasma::Layout that it should * reset its geometry. */ // NOTE: this is a completely broken concept -MB Q_INVOKABLE void updateGeometry(); /** * Returns the recommended size for this widget. Note that this size is not * necessarily only the size for the widget, but might also include margins etc. * @return recommended size for this Plasma::Widget. */ virtual QSizeF sizeHint() const; /** * @return the size of this Plasma::Widget */ QSizeF size() const; /** * @return the font currently set for this widget **/ QFont font() const; /** * Reimplemented from QGraphicsItem * @return the bounding rectangle for this Plasma::Widget */ virtual QRectF boundingRect() const; /** * Resizes this Plasma::Widget. * @param size the new size of this Plasma::Widget. */ Q_INVOKABLE void resize(const QSizeF &size); /** * Convenience method for resizing this Plasma::Widget * @param width the new width. * @param height the new height. */ Q_INVOKABLE void resize(qreal width, qreal height); /** * @return this Plasma::Widget's parent, returns a null pointer if * none exist. */ Q_INVOKABLE Widget *parent() const; /** * @return the Plasma::Widget parent for a given QGraphicsItem */ static Widget *parent(const QGraphicsItem *item); /** * Add another Plasma::Widget as a child of this one. * @param widget the widget to reparent to this Plasma::Widget. */ Q_INVOKABLE void addChild(Widget *widget); void setOpacity(qreal opacity); qreal opacity() const; /** * Sets the widget's cache paint mode and cache size. * @param mode the new cache paint mode * @param mode the new cache size, only applies to ItemCoordinateCacheMode */ void setCachePaintMode(CachePaintMode mode, const QSize &size = QSize()); /** * The current cache paint mode. */ CachePaintMode cachePaintMode() const; /** * Invalidates the widget's cache paint mode for a given item rectangle. * @param rect the optional invalidated rectangle; if null, defaults to boundingRect(). */ void update(const QRectF &rect = QRectF()); inline void update(qreal _x, qreal _y, qreal w, qreal h) { update(QRectF(_x, _y, w, h)); } virtual QGraphicsItem* graphicsItem(); /** * Returns the view this widget is visible on */ QGraphicsView *view() const; /** * Maps a QRect from a view's coordinates to local coordinates. * @param view the view from which rect should be mapped * @param rect the rect to be mapped */ QRectF mapFromView(const QGraphicsView *view, const QRect &rect) const; /** * Maps a QRectF from local coordinates to a view's coordinates. * @param view the view to which rect should be mapped * @param rect the rect to be mapped */ QRect mapToView(const QGraphicsView *view, const QRectF &rect) const; /** * The Data from the tooltip * @returns A ToolTip::Data object with current information */ - ToolTipData toolTip() const; + const ToolTipData* toolTip() const; /** * Setter for data shown in tooltip * @param data a ToolTip::Data object containing icon and text */ void setToolTip( const ToolTipData &dt ); /** * Recomended position for a popup window like a menu or a tooltip * given its size * @param s size of the popup * @returns recomended position */ QPoint popupPosition(const QSize s) const; /** * Reimplemented from QGraphicsItem */ virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); protected: /** * Paints the widget * @param painter the QPainter to use to paint. * @param option the style option used to give specific info on the item being dawn. * @param widget the parent QWidget (most likely the Corona) */ virtual void paintWidget(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); QVariant itemChange(GraphicsItemChange change, const QVariant &value); void setSize(qreal width, qreal height); void setSize(const QSizeF& size); void managingLayoutChanged(); virtual bool sceneEvent(QEvent *event); private: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); class Private; Private *const d; }; } // Plasma namespace #endif