diff --git a/private/tooltip.cpp b/private/tooltip.cpp index 9d2614cf6..eea7c99e6 100644 --- a/private/tooltip.cpp +++ b/private/tooltip.cpp @@ -21,6 +21,7 @@ #include "tooltip_p.h" #include "windowpreview_p.h" +#include #include #include #include @@ -47,19 +48,20 @@ namespace Plasma { class TipTextWidget : public QWidget { public: - TipTextWidget(QWidget *parent) + TipTextWidget(ToolTip *parent) : QWidget(parent), - document(new QTextDocument(this)) + m_toolTip(parent), + m_document(new QTextDocument(this)) { //d->text->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); // QTextOption op; // op.setWrapMode(QTextOption::WordWrap); -// document->setDefaultTextOption(op); +// m_document->setDefaultTextOption(op); } void setStyleSheet(const QString &css) { - document->setDefaultStyleSheet(css); + m_document->setDefaultStyleSheet(css); } void setContent(const ToolTipContent &data) @@ -74,16 +76,17 @@ public: } html.append(data.subText()); - document->clear(); - data.registerResources(document); - document->setHtml("

" + html + "

"); - document->adjustSize(); + m_anchor.clear(); + m_document->clear(); + data.registerResources(m_document); + m_document->setHtml("

" + html + "

"); + m_document->adjustSize(); update(); } QSize minimumSizeHint() const { - return document->size().toSize(); + return m_document->size().toSize(); } QSize maximumSizeHint() const @@ -94,11 +97,34 @@ public: void paintEvent(QPaintEvent *event) { QPainter p(this); - document->drawContents(&p, event->rect()); + m_document->drawContents(&p, event->rect()); + } + + void mousePressEvent(QMouseEvent *event) + { + QAbstractTextDocumentLayout *layout = m_document->documentLayout(); + if (layout) { + m_anchor = layout->anchorAt(event->pos()); + } + } + + void mouseReleaseEvent(QMouseEvent *event) + { + QAbstractTextDocumentLayout *layout = m_document->documentLayout(); + if (layout) { + QString anchor = layout->anchorAt(event->pos()); + if (anchor == m_anchor) { + m_toolTip->linkActivated(m_anchor, event); + } + + m_anchor.clear(); + } } private: - QTextDocument *document; + ToolTip *m_toolTip; + QTextDocument *m_document; + QString m_anchor; }; class ToolTipPrivate @@ -126,28 +152,6 @@ class ToolTipPrivate bool autohide; }; -void ToolTip::showEvent(QShowEvent *e) -{ - checkSize(); - QWidget::showEvent(e); - d->preview->setInfo(); -} - -void ToolTip::hideEvent(QHideEvent *e) -{ - QWidget::hideEvent(e); - if (d->source) { - QMetaObject::invokeMethod(d->source, "toolTipHidden"); - } -} - -void ToolTip::mouseReleaseEvent(QMouseEvent *event) -{ - if (rect().contains(event->pos())) { - hide(); - } -} - ToolTip::ToolTip(QWidget *parent) : QWidget(parent), d(new ToolTipPrivate()) @@ -165,7 +169,8 @@ ToolTip::ToolTip(QWidget *parent) d->background->setEnabledBorders(FrameSvg::AllBorders); updateTheme(); connect(d->background, SIGNAL(repaintNeeded()), this, SLOT(updateTheme())); - + connect(d->preview, SIGNAL(windowPreviewClicked(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)), + this, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint))); l->addWidget(d->preview, 0, 0, 1, 2); l->addWidget(d->imageLabel, 1, 0); l->addWidget(d->text, 1, 1); @@ -177,6 +182,39 @@ ToolTip::~ToolTip() delete d; } +void ToolTip::showEvent(QShowEvent *e) +{ + checkSize(); + QWidget::showEvent(e); + d->preview->setInfo(); +} + +void ToolTip::hideEvent(QHideEvent *e) +{ + QWidget::hideEvent(e); + if (d->source) { + QMetaObject::invokeMethod(d->source, "toolTipHidden"); + } +} + +void ToolTip::mouseReleaseEvent(QMouseEvent *event) +{ + if (rect().contains(event->pos()) && + (!d->preview || !d->preview->geometry().contains(event->pos()))) { + hide(); + } +} + +void ToolTip::enterEvent(QEvent *) +{ + emit hovered(true); +} + +void ToolTip::leaveEvent(QEvent *) +{ + emit hovered(false); +} + void ToolTip::checkSize() { //FIXME: layout bugs even on qlayouts? oh, please, no. @@ -327,6 +365,11 @@ void ToolTip::setDirection(Plasma::Direction direction) d->direction = direction; } +void ToolTip::linkActivated(const QString &anchor, QMouseEvent *event) +{ + emit linkActivated(anchor, event->buttons(), event->modifiers(), event->globalPos()); +} + void ToolTip::updateTheme() { const int topHeight = d->background->marginSize(Plasma::TopMargin); diff --git a/private/tooltip_p.h b/private/tooltip_p.h index 0aea4d6a4..98a6a8c6d 100644 --- a/private/tooltip_p.h +++ b/private/tooltip_p.h @@ -42,6 +42,16 @@ public: void moveTo(const QPoint &to); bool autohide() const; void setDirection(Plasma::Direction); + void linkActivated(const QString &anchor, QMouseEvent *event); + +Q_SIGNALS: + void activateWindowByWId(WId wid, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + const QPoint& screenPos); + void linkActivated(const QString &anchor, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + const QPoint& screenPos); + void hovered(bool hovered); protected: void checkSize(); @@ -49,6 +59,8 @@ protected: void showEvent(QShowEvent *); void hideEvent(QHideEvent *); void mouseReleaseEvent(QMouseEvent *); + void enterEvent(QEvent *); + void leaveEvent(QEvent *); void resizeEvent(QResizeEvent *); void paintEvent(QPaintEvent *); diff --git a/private/windowpreview.cpp b/private/windowpreview.cpp index 7a6870928..6afa6bce2 100644 --- a/private/windowpreview.cpp +++ b/private/windowpreview.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -217,13 +218,30 @@ void WindowPreview::paintEvent(QPaintEvent *e) m_background->getMargins(left, top, right, bottom); foreach (QRect r, m_thumbnailRects) { - kWarning()<resizeFrame(r.size()+QSize(left+right, top+bottom)); m_background->paintFrame(&painter, r.topLeft()-pos()-QPoint(left,top)); } #endif } +void WindowPreview::mousePressEvent(QMouseEvent *event) +{ + QPoint p = event->pos(); + WId wid = 0; + + for (int i = 0; i < m_thumbnailRects.size(); ++i) { + if (m_thumbnailRects[i].contains(p)) { + wid = ids[i]; + break; + } + } + + if (wid) { + emit windowPreviewClicked(wid, event->buttons(), event->modifiers(), event->globalPos()); + } +} + } // namespace Plasma #include "windowpreview_p.moc" diff --git a/private/windowpreview_p.h b/private/windowpreview_p.h index bfc1db17f..e8622927b 100644 --- a/private/windowpreview_p.h +++ b/private/windowpreview_p.h @@ -50,8 +50,12 @@ public: bool isEmpty() const; virtual QSize sizeHint() const; +Q_SIGNALS: + void windowPreviewClicked(WId wid, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys, const QPoint &screenPos); + protected: void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *event); private: void readWindowSizes() const; diff --git a/tooltipcontent.cpp b/tooltipcontent.cpp index dccedbae1..7e8ce6454 100644 --- a/tooltipcontent.cpp +++ b/tooltipcontent.cpp @@ -47,7 +47,8 @@ class ToolTipContentPrivate { public: ToolTipContentPrivate() - : autohide(true) + : autohide(true), + clickable(false) { } @@ -56,7 +57,8 @@ public: QPixmap image; QList windowsToPreview; QHash resources; - bool autohide; + bool autohide : 1; + bool clickable : 1; }; ToolTipContent::ToolTipContent() @@ -211,6 +213,16 @@ void ToolTipContent::registerResources(QTextDocument *document) const } } +void ToolTipContent::setClickable(bool clickable) +{ + d->clickable = clickable; +} + +bool ToolTipContent::isClickable() const +{ + return d->clickable; +} + } // namespace Plasma diff --git a/tooltipcontent.h b/tooltipcontent.h index f2bb7a469..77d9c7be6 100644 --- a/tooltipcontent.h +++ b/tooltipcontent.h @@ -48,79 +48,135 @@ class PLASMA_EXPORT ToolTipContent public: enum ResourceType { ImageResource = 0, HtmlResource, CssResource }; - /** Creates an empty Content */ + /** + * Creates an empty Content + */ ToolTipContent(); ~ToolTipContent(); - /** Copy constructor */ + /** + * Copy constructor + */ ToolTipContent(const ToolTipContent &other); - /** Constructor that sets the common fields */ + /** + * Constructor that sets the common fields + */ ToolTipContent(const QString &mainText, const QString &subText, const QPixmap &image = QPixmap()); - /** Constructor that sets the common fields */ + /** + * Constructor that sets the common fields + */ ToolTipContent(const QString &mainText, const QString &subText, const QIcon &icon); ToolTipContent &operator=(const ToolTipContent &other); - /** @return true if all the fields are empty */ + /** + * @return true if all the fields are empty + */ bool isEmpty() const; - /** Sets the main text which containts important information, e.g. the title */ + /** + * Sets the main text which containts important information, e.g. the title + */ void setMainText(const QString &text); - /** Important information, e.g. the title */ + /** + * Important information, e.g. the title + */ QString mainText() const; - /** Sets text which elaborates on the @p mainText */ + /** + * Sets text which elaborates on the @p mainText + */ void setSubText(const QString &text) ; - /** Elaborates on the @p mainText */ + /** + * Elaborates on the @p mainText + */ QString subText() const; - /** Sets the icon to show **/ + /** + * Sets the icon to show + */ void setImage(const QPixmap &image); - /** Sets the icon to show **/ + /** + * Sets the icon to show + */ void setImage(const QIcon &icon); - /** An icon to display */ + /** + * An icon to display + */ QPixmap image() const; - //BIC FIXME: remove when we can break BC - /** Sets the ID of the window to show a preview for */ + /** + * Sets the ID of the window to show a preview for. + * @deprecated + * @see setWindowsToPreview + */ void setWindowToPreview(WId id); - //BIC FIXME: remove when we can break BC - /** Id of a window if you want to show a preview */ + /** + * Id of a window if you want to show a preview + * @deprecated + * @see windowsToPreview + */ WId windowToPreview() const; - /** Sets the IDS of the windows to show a preview for - @since 4.3*/ + /** + * Sets the IDS of the windows to show a preview for + * @since 4.3 + */ void setWindowsToPreview(const QList &ids); - /** Ids of a windows if you want to show a preview - @since 4.3*/ + /** + * Ids of a windows if you want to show a preview + * @since 4.3 + */ QList windowsToPreview() const; - /** Sets whether or not to autohide the tooltip, defaults to true */ + /** Sets whether or not to autohide the tooltip, defaults to true + */ void setAutohide(bool autohide); - /** Whether or not to autohide the tooltip, defaults to true */ + /** + * Whether or not to autohide the tooltip, defaults to true + */ bool autohide() const; - /** Adds a resource that can then be referenced from the text elements - using rich text **/ + /** + * Adds a resource that can then be referenced from the text elements + * using rich text + */ void addResource(ResourceType type, const QUrl &path, const QVariant &resource); - /** Registers all resources with a given document */ + /** + * Registers all resources with a given document + */ void registerResources(QTextDocument *document) const; + /** + * Sets whether or not the tooltip contains clickable content, such as + * window previews. Defaults to false, or not clickable. + * + * @since 4.3 + */ + void setClickable(bool clickable); + + /** + * @return true if the tooltip is clickabel + * + * @since 4.3 + */ + bool isClickable() const; + private: ToolTipContentPrivate * const d; }; diff --git a/tooltipmanager.cpp b/tooltipmanager.cpp index 8723b6710..82a4151a0 100644 --- a/tooltipmanager.cpp +++ b/tooltipmanager.cpp @@ -62,9 +62,14 @@ public : tipWidget(new ToolTip(0)), state(ToolTipManager::Activated), isShown(false), - delayedHide(false) + delayedHide(false), + clickable(false) { - + QObject::connect(tipWidget, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)), + q, SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint))); + QObject::connect(tipWidget, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)), + q, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint))); + QObject::connect(tipWidget, SIGNAL(hovered(bool)), q, SLOT(toolTipHovered(bool))); } ~ToolTipManagerPrivate() @@ -81,9 +86,10 @@ public : * called when a widget inside the tooltip manager is deleted */ void onWidgetDestroyed(QObject * object); - void removeWidget(QGraphicsWidget *w); + void removeWidget(QGraphicsWidget *w, bool canSafelyAccess = true); void clearTips(); void doDelayedHide(); + void toolTipHovered(bool); ToolTipManager *q; QGraphicsWidget *currentWidget; @@ -94,6 +100,7 @@ public : ToolTipManager::State state; bool isShown : 1; bool delayedHide : 1; + bool clickable : 1; }; //TOOLTIP IMPLEMENTATION @@ -137,6 +144,10 @@ void ToolTipManager::show(QGraphicsWidget *widget) return; } + if (d->currentWidget) { + disconnect(this, 0, d->currentWidget, 0); + } + d->hideTimer->stop(); d->delayedHide = false; d->showTimer->stop(); @@ -160,7 +171,13 @@ void ToolTipManagerPrivate::doDelayedHide() { showTimer->stop(); // stop the timer to show the tooltip delayedHide = true; - hideTimer->start(250); + + if (isShown && clickable) { + // leave enough time for user to choose + hideTimer->start(1000); + } else { + hideTimer->start(250); + } } void ToolTipManager::hide(QGraphicsWidget *widget) @@ -169,6 +186,10 @@ void ToolTipManager::hide(QGraphicsWidget *widget) return; } + if (d->currentWidget) { + disconnect(this, 0, d->currentWidget, 0); + } + d->currentWidget = 0; d->showTimer->stop(); // stop the timer to show the tooltip d->delayedHide = false; @@ -211,6 +232,7 @@ void ToolTipManager::setContent(QGraphicsWidget *widget, const ToolTipContent &d hide(widget); } else { d->delayedHide = data.autohide(); + d->clickable = data.isClickable(); if (d->delayedHide) { //kDebug() << "starting authoide"; d->hideTimer->start(3000); @@ -267,13 +289,16 @@ void ToolTipManagerPrivate::onWidgetDestroyed(QObject *object) // NOTE: DO NOT USE THE w VARIABLE FOR ANYTHING OTHER THAN COMPARING // THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!! QGraphicsWidget *w = static_cast(object); - removeWidget(w); + removeWidget(w, false); } -void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w) +void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w, bool canSafelyAccess) { - // DO NOT ACCESS w HERE!! IT MAY BE IN THE PROCESS OF DELETION! - if (currentWidget == w) { + if (currentWidget == w && currentWidget) { + if (canSafelyAccess) { + QObject::disconnect(q, 0, currentWidget, 0); + } + currentWidget = 0; showTimer->stop(); // stop the timer to show the tooltip tipWidget->setContent(0, ToolTipContent()); @@ -296,6 +321,7 @@ void ToolTipManagerPrivate::resetShownState() //One might have moused out and back in again delayedHide = false; isShown = false; + QObject::disconnect(q, 0, currentWidget, 0); currentWidget = 0; tipWidget->hide(); } @@ -336,6 +362,7 @@ void ToolTipManagerPrivate::showToolTip() tipWidget->setDirection(Plasma::locationToDirection(c->location())); } + clickable = tooltip.value().isClickable(); tipWidget->setContent(currentWidget, tooltip.value()); tipWidget->prepareShowing(); if (q->m_corona) { @@ -353,6 +380,19 @@ void ToolTipManagerPrivate::showToolTip() } } +void ToolTipManagerPrivate::toolTipHovered(bool hovered) +{ + if (!clickable) { + return; + } + + if (hovered) { + hideTimer->stop(); + } else if (delayedHide) { + hideTimer->start(500); + } +} + bool ToolTipManager::eventFilter(QObject *watched, QEvent *event) { QGraphicsWidget * widget = dynamic_cast(watched); @@ -394,11 +434,16 @@ bool ToolTipManager::eventFilter(QObject *watched, QEvent *event) } case QEvent::GraphicsSceneHoverLeave: - d->doDelayedHide(); + if (d->currentWidget == widget) { + d->doDelayedHide(); + } break; case QEvent::GraphicsSceneMousePress: - hide(widget); + if (d->currentWidget == widget) { + hide(widget); + } + break; case QEvent::GraphicsSceneWheel: default: diff --git a/tooltipmanager.h b/tooltipmanager.h index 60a40b226..828b8a8d6 100644 --- a/tooltipmanager.h +++ b/tooltipmanager.h @@ -22,7 +22,8 @@ #ifndef PLASMA_TOOLTIP_MANAGER_H #define PLASMA_TOOLTIP_MANAGER_H -//plasma +#include + #include #include #include @@ -164,6 +165,27 @@ public: */ ToolTipManager::State state() const; +signals: + /** + * This signal is emitted when a window preview in the tooltip is clicked. + * @arg window the id of the window that was clicked + * @arg buttons the mouse buttons involved in the activation + * @arg modifiers the keyboard modifiers involved in the activation, if any + * @since 4.3 + */ + void windowPreviewActivated(WId window, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + const QPoint &screenPos); + + /** + * This signal is emitted when a link in the tooltip is clicked. + * @arg anchor the achor text (e.g. url) that was clicked on + * @arg buttons the mouse buttons involved in the activation + * @arg modifiers the keyboard modifiers involved in the activation, if any + * @since 4.3 + */ + void linkActivated(const QString &anchor, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, + const QPoint &screenPos); + private: /** * Default constructor. @@ -186,6 +208,7 @@ private: Corona* m_corona; Q_PRIVATE_SLOT(d, void showToolTip()) + Q_PRIVATE_SLOT(d, void toolTipHovered(bool)) Q_PRIVATE_SLOT(d, void resetShownState()) Q_PRIVATE_SLOT(d, void onWidgetDestroyed(QObject*)) };