clickable tooltips; plasma-devel@ people: please review API one more time

CCMAIL:plasma-devel@kde.org

svn path=/trunk/KDE/kdelibs/; revision=987462
This commit is contained in:
Aaron J. Seigo 2009-06-26 06:40:15 +00:00
parent 4e69cb0553
commit 9aa367047c
8 changed files with 286 additions and 73 deletions

View File

@ -21,6 +21,7 @@
#include "tooltip_p.h"
#include "windowpreview_p.h"
#include <QAbstractTextDocumentLayout>
#include <QBitmap>
#include <QGridLayout>
#include <QLabel>
@ -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("<p>" + html + "</p>");
document->adjustSize();
m_anchor.clear();
m_document->clear();
data.registerResources(m_document);
m_document->setHtml("<p>" + html + "</p>");
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);

View File

@ -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 *);

View File

@ -22,6 +22,7 @@
#include <QPainter>
#include <QVarLengthArray>
#include <QMouseEvent>
#include <kwindowsystem.h>
#include <kdebug.h>
@ -217,13 +218,30 @@ void WindowPreview::paintEvent(QPaintEvent *e)
m_background->getMargins(left, top, right, bottom);
foreach (QRect r, m_thumbnailRects) {
kWarning()<<r;
//kWarning()<<r;
m_background->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"

View File

@ -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;

View File

@ -47,7 +47,8 @@ class ToolTipContentPrivate
{
public:
ToolTipContentPrivate()
: autohide(true)
: autohide(true),
clickable(false)
{
}
@ -56,7 +57,8 @@ public:
QPixmap image;
QList<WId> windowsToPreview;
QHash<QString, ToolTipResource> 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

View File

@ -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<WId> &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<WId> 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;
};

View File

@ -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<QGraphicsWidget*>(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<QGraphicsWidget *>(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:

View File

@ -22,7 +22,8 @@
#ifndef PLASMA_TOOLTIP_MANAGER_H
#define PLASMA_TOOLTIP_MANAGER_H
//plasma
#include <kurl.h>
#include <plasma/plasma.h>
#include <plasma/plasma_export.h>
#include <plasma/tooltipcontent.h>
@ -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*))
};