new way to do tooltips

manage it in c++
it still steals hover events from their target and not all features are there.

better than before tough
This commit is contained in:
Marco Martin 2014-01-07 21:43:30 +01:00
parent 0f1bc5e748
commit 9f3af64dac
9 changed files with 361 additions and 209 deletions

View File

@ -102,7 +102,7 @@ PlasmaComponents.Page {
PlasmaCore.ToolTip {
anchors.fill: parent
target: akonadiIcon
iconSource: "klipper"
icon: "klipper"
mainText: "Fish sighted in the wild, in the wild, a fish was seen."
subText: "A mean-looking grouper swam by."
}
@ -114,7 +114,7 @@ PlasmaComponents.Page {
fillMode: Image.PreserveAspectFit
source: "../images/bridge.jpg"
PlasmaCore.ToolTip {
anchors.fill: parent
target: parent
image: parent.source
mainText: "Bridge"
subText: "Waalbrug."
@ -129,16 +129,23 @@ PlasmaComponents.Page {
PlasmaCore.ToolTip {
anchors.fill: parent
image: parent.source
mainComponent: Component {
PlasmaComponents.Label {
text: "Nijmegen North Beach"
anchors.centerIn: parent
}
mainItem: PlasmaComponents.Label {
text: "Nijmegen North Beach"
anchors.centerIn: parent
}
//subText: "A surfboard on the beach. <br />The photo shows the Waal river's north beach, \
//across the water from Nijmegen, Netherlands. It was taken during the summer festivals a few years back."
}
}
PlasmaComponents.Button {
text: "Button"
iconSource: "call-start"
PlasmaCore.ToolTip {
target: parent
mainText: "Tooltip on button"
}
}
QtExtras.MouseEventListener {

View File

@ -57,6 +57,8 @@ if(X11_FOUND)
endif()
install(TARGETS corebindingsplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core)
install(FILES qmldir ToolTip.qml DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core)
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core)
install(FILES private/DefaultToolTip.qml DESTINATION ${QML_INSTALL_DIR}/org/kde/plasma/core/private)
add_subdirectory(tests)

View File

@ -1,179 +0,0 @@
/*
* Copyright 2013 by Sebastian Kügler <sebas@kde.org>
*
* 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 Library 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 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.qtextracomponents 2.0 as QtExtras
/**
* An Item managing a Plasma-themed tooltip. It is rendered in its own window.
* You can either specify iconSource, mainText and subText, or a custom Component
* that will be put inside the tooltip. By specifying the target property, you
* "attach" the ToolTip to an item in your code, by default the tooltip will be
* rendered when hovering over the parent item.
*
* The item inside the ToolTip is loaded on demand and will be destroyed when the
* tooltip is being hidden.
*
* Example usage:
* @code
* import org.kde.plasma.core 2.0 as PlasmaCore
*
* [...]
* PlasmaComponents.IconItem {
* ...
* PlasmaCore.ToolTip {
* mainText: "Tooltip Title"
* subText: "Some explanation."
* iconSource: "plasma"
* // alternatively, you can specify your own component
* // to be loaded when the tooltip shows
* mainComponent: Component {
* YourCustomItem { ... }
* }
* ... }
* }
* @endcode
*
*/
QtExtras.MouseEventListener {
id: tooltip
property string mainText // title text of the tooltip
property string subText // description text
property string iconSource // icon name
property string image // string / url to the image
property Item target: parent // The item that has the tooltip, defaulting to parent item
property Component mainComponent: tooltipComponent // custom component to create inside the tooltip
hoverEnabled: true
onContainsMouseChanged: {
// hide immediately, show after a while
tooltipTimer.interval = tooltip.containsMouse ? 500 : 0;
tooltipTimer.start();
}
Timer {
id: tooltipTimer
onTriggered: {
if (tooltip.containsMouse) {
show();
} else {
hide();
}
}
}
function show() {
var mi = tooltip.mainItem;
if (mi == null) {
mi = tooltip.mainComponent.createObject( tooltip.target, {
"mainText": tooltip.mainText,
"subText": tooltip.subText,
"iconSource": tooltip.iconSource,
"image": tooltip.image
});
}
tooltipWindow.visualParent = tooltip.target;
tooltipWindow.mainItem = mi;
tooltipWindow.visible = true;
}
function hide() {
tooltipWindow.visible = false;
if (tooltipWindow.mainItem != null) {
tooltipWindow.mainItem.destroy();
}
}
Component {
id: tooltipComponent
Item {
id: tooltipContentItem
x: _s
y: _s
width: childrenRect.width + _s
height: childrenRect.height
property string mainText: "" // string
property string subText: "" // string
property string iconSource: "" // icon name
property string image: "" // string / url to the image
property int maxTextSize: Math.max(tooltipMaintext.paintedWidth, tooltipSubtext.paintedWidth)
property int maxSize: theme.iconSizes.desktop * 6
property int preferredTextWidth: Math.min(maxTextSize, maxSize)
property int _s: theme.iconSizes.small / 2
Image {
id: tooltipImage
source: image
}
PlasmaCore.IconItem {
id: tooltipIcon
width: iconSource != "" ? theme.iconSizes.desktop : 0
height: width
source: iconSource
anchors {
leftMargin: width != 0 ? _s : 0
}
}
PlasmaExtras.Heading {
id: tooltipMaintext
level: 3
width: parent.preferredTextWidth
wrapMode: Text.WordWrap
text: mainText
anchors {
left: (tooltipImage.source != "") ? tooltipImage.right : tooltipIcon.right
leftMargin: _s*2
top: tooltipIcon.top
}
}
PlasmaComponents.Label {
id: tooltipSubtext
width: parent.preferredTextWidth
wrapMode: Text.WordWrap
text: subText
opacity: 0.5
anchors {
left: tooltipMaintext.left
topMargin: _s
bottomMargin: _s
top: tooltipMaintext.bottom
}
}
}
}
PlasmaCore.ToolTipProxy {
id: tooltipWindow
visualParent: tooltip.target
}
onTargetChanged: {
print("Target changed");
tooltip.parent = target;
//target.visualParent = tooltip
anchors.fill = target
}
}

View File

@ -85,7 +85,7 @@ void CoreBindingsPlugin::registerTypes(const char *uri)
qmlRegisterType<Plasma::SortFilterModel>(uri, 2, 0, "SortFilterModel");
qmlRegisterType<DialogProxy>(uri, 2, 0, "Dialog");
qmlRegisterType<ToolTip>(uri, 2, 0, "ToolTipProxy");
qmlRegisterType<ToolTip>(uri, 2, 0, "ToolTip");
qmlRegisterInterface<Plasma::Service>("Service");
qRegisterMetaType<Plasma::Service*>("Service");

View File

@ -0,0 +1,117 @@
/*
* Copyright 2013 by Sebastian Kügler <sebas@kde.org>
*
* 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 Library 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 2.010-1301, USA.
*/
import QtQuick 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.qtextracomponents 2.0 as QtExtras
/**
* An Item managing a Plasma-themed tooltip. It is rendered in its own window.
* You can either specify iconSource, mainText and subText, or a custom Component
* that will be put inside the tooltip. By specifying the target property, you
* "attach" the ToolTip to an item in your code, by default the tooltip will be
* rendered when hovering over the parent item.
*
* The item inside the ToolTip is loaded on demand and will be destroyed when the
* tooltip is being hidden.
*
* Example usage:
* @code
* import org.kde.plasma.core 2.0 as PlasmaCore
*
* [...]
* PlasmaComponents.IconItem {
* ...
* PlasmaCore.ToolTip {
* mainText: "Tooltip Title"
* subText: "Some explanation."
* iconSource: "plasma"
* // alternatively, you can specify your own component
* // to be loaded when the tooltip shows
* mainComponent: Component {
* YourCustomItem { ... }
* }
* ... }
* }
* @endcode
*
*/
Item {
id: tooltipContentItem
x: _s
y: _s
width: childrenRect.width + _s
height: childrenRect.height
property Item toolTip
property string mainText: "" // string
property string subText: "" // string
property string iconSource: "" // icon name
property string image: "" // string / url to the image
property int maxTextSize: Math.max(tooltipMaintext.paintedWidth, tooltipSubtext.paintedWidth)
property int maxSize: theme.iconSizes.desktop * 6
property int preferredTextWidth: Math.min(maxTextSize, maxSize)
property int _s: theme.iconSizes.small / 2
Image {
id: tooltipImage
source: toolTip ? toolTip.image : ""
}
PlasmaCore.IconItem {
id: tooltipIcon
width: toolTip.icon != "" ? theme.iconSizes.desktop : 0
height: width
source: toolTip ? toolTip.icon : ""
anchors {
leftMargin: width != 0 ? _s : 0
}
}
PlasmaExtras.Heading {
id: tooltipMaintext
level: 3
width: parent.preferredTextWidth
wrapMode: Text.WordWrap
text: toolTip ? toolTip.mainText : ""
anchors {
left: (tooltipImage.source != "") ? tooltipImage.right : tooltipIcon.right
leftMargin: _s*2
top: tooltipIcon.top
}
}
PlasmaComponents.Label {
id: tooltipSubtext
width: parent.preferredTextWidth
wrapMode: Text.WordWrap
text: toolTip ? toolTip.subText : ""
opacity: 0.5
anchors {
left: tooltipMaintext.left
topMargin: _s
bottomMargin: _s
top: tooltipMaintext.bottom
}
}
}

View File

@ -22,15 +22,22 @@
#include "tooltip.h"
#include "tooltipdialog.h"
#include <QQmlEngine>
#include <QQuickItem>
#include <QDebug>
#include "framesvgitem.h"
#include <kwindoweffects.h>
ToolTip::ToolTip(QObject *parent)
: QObject(parent)
ToolTip::ToolTip(QQuickItem *parent)
: QQuickItem(parent)
{
m_showTimer = new QTimer(this);
m_showTimer->setSingleShot(true);
connect(m_showTimer, &QTimer::timeout, [=]() {
setVisible(true);
});
setAcceptHoverEvents(true);
}
ToolTip::~ToolTip()
@ -45,30 +52,34 @@ QQuickItem *ToolTip::mainItem() const
void ToolTip::setMainItem(QQuickItem *mainItem)
{
if (m_mainItem.data() != mainItem) {
if (m_mainItem) {
m_mainItem.data()->setParent(parent());
}
m_mainItem = mainItem;
emit mainItemChanged();
}
}
QQuickItem *ToolTip::visualParent() const
QQuickItem *ToolTip::target() const
{
if (m_visualParent.data()) {
return m_visualParent.data();
if (m_target.data()) {
return m_target.data();
} else {
QQuickItem *qqi = qobject_cast<QQuickItem*>(parent());
return qqi;
}
}
void ToolTip::setVisualParent(QQuickItem *visualParent)
void ToolTip::setTarget(QQuickItem *target)
{
if (m_visualParent.data() == visualParent) {
if (m_target.data() == target) {
return;
}
emit visualParentChanged();
m_target = target;
setParentItem(target);
property("anchors").value<QObject *>()->setProperty("fill", QVariant::fromValue(parentItem()));
emit targetChanged();
}
bool ToolTip::isVisible() const
@ -81,11 +92,117 @@ void ToolTip::setVisible(const bool visible)
{
ToolTipDialog *dlg = ToolTipDialog::instance();
if (visible) {
if (!mainItem()) {
setMainItem(dlg->loadDefaultItem());
}
if (dlg->mainItem()) {
dlg->mainItem()->setVisible(false);
}
if (mainItem()) {
mainItem()->setProperty("toolTip", QVariant::fromValue(this));
mainItem()->setVisible(true);
}
dlg->setMainItem(mainItem());
dlg->setVisualParent(visualParent());
dlg->setVisualParent(target());
dlg->setVisible(true);
} else {
dlg->setVisible(false);
}
}
QString ToolTip::mainText() const
{
return m_mainText;
}
void ToolTip::setMainText(const QString &mainText)
{
if (mainText == m_mainText) {
return;
}
m_mainText = mainText;
emit mainTextChanged();
}
QString ToolTip::subText() const
{
return m_subText;
}
void ToolTip::setSubText(const QString &subText)
{
if (subText == m_subText) {
return;
}
m_subText = subText;
emit subTextChanged();
}
QVariant ToolTip::icon() const
{
if (m_icon.isValid()) {
return m_icon;
} else {
return QString();
}
}
void ToolTip::setIcon(const QVariant &icon)
{
if (icon == m_icon) {
return;
}
m_icon = icon;
emit iconChanged();
}
QVariant ToolTip::image() const
{
if (m_image.isValid()) {
return m_image;
} else {
return QString();
}
}
void ToolTip::setImage(const QVariant &image)
{
if (image == m_image) {
return;
}
m_image = image;
emit imageChanged();
}
void ToolTip::hoverEnterEvent(QHoverEvent *event)
{
if (ToolTipDialog::instance()->isVisible()) {
//FIXME: setVisible needs to be renamed in sync or something like that
setVisible(true);
} else {
m_showTimer->start(500);
}
//relay the event
if (window()) {
window()->sendEvent(target(), event);
}
}
void ToolTip::hoverLeaveEvent(QHoverEvent *event)
{
m_showTimer->stop();
//relay the event
if (window()) {
window()->sendEvent(target(), event);
}
}

View File

@ -34,7 +34,7 @@ class QGraphicsWidget;
*
* DO NOT USE THIS API, PlasmaCore.ToolTip is what you should use.
*/
class ToolTip : public QObject
class ToolTip : public QQuickItem
{
Q_OBJECT
@ -46,35 +46,80 @@ class ToolTip : public QObject
/**
* The main QML item that will be displayed in the Dialog
*/
Q_PROPERTY(QQuickItem *visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged)
Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged)
/**
* Visibility of the Dialog window. Doesn't have anything to do with the visibility of the mainItem.
*/
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
/**
* The main text of this tooltip
*/
Q_PROPERTY(QString mainText READ mainText WRITE setMainText NOTIFY mainTextChanged)
/**
* The description of this tooltip
*/
Q_PROPERTY(QString subText READ subText WRITE setSubText NOTIFY subTextChanged)
/**
* An icon for this tooltip, accepted values are an icon name, a QIcon, QImage or QPixmap
*/
Q_PROPERTY(QVariant icon READ icon WRITE setIcon NOTIFY iconChanged)
/**
* TODO: single property for images?
* An image for this tooltip, accepted values are an icon name, a QIcon, QImage or QPixmap
*/
Q_PROPERTY(QVariant image READ image WRITE setImage NOTIFY imageChanged)
public:
ToolTip(QObject *parent = 0);
ToolTip(QQuickItem *parent = 0);
~ToolTip();
QQuickItem *mainItem() const;
void setMainItem(QQuickItem *mainItem);
QQuickItem *visualParent() const;
void setVisualParent(QQuickItem *visualParent);
QQuickItem *target() const;
void setTarget(QQuickItem *target);
bool isVisible() const;
void setVisible(const bool visible);
QString mainText() const;
void setMainText(const QString &mainText);
QString subText() const;
void setSubText(const QString &subText);
QVariant icon() const;
void setIcon(const QVariant &icon);
QVariant image() const;
void setImage(const QVariant &image);
protected:
void hoverEnterEvent(QHoverEvent *event);
void hoverLeaveEvent(QHoverEvent *event);
Q_SIGNALS:
void mainItemChanged();
void visualParentChanged();
void targetChanged();
void visibleChanged();
void mainTextChanged();
void subTextChanged();
void iconChanged();
void imageChanged();
private:
QWeakPointer<QQuickItem> m_mainItem;
QWeakPointer<QQuickItem> m_visualParent;
QWeakPointer<QQuickItem> m_target;
QTimer *m_showTimer;
QString m_mainText;
QString m_subText;
QVariant m_image;
QVariant m_icon;
};
#endif

View File

@ -19,24 +19,61 @@
#include "tooltipdialog.h"
#include <QQmlEngine>
#include <QQuickItem>
#include <QDebug>
#include "framesvgitem.h"
#include <kdeclarative/qmlobject.h>
Q_GLOBAL_STATIC(ToolTipDialog, toolTipDialogInstance)
ToolTipDialog::ToolTipDialog(QQuickItem *parent)
: DialogProxy(parent)
: DialogProxy(parent),
m_qmlObject(0)
{
setFlags(Qt::ToolTip);
setLocation(Plasma::Types::Floating);
m_frameSvgItem->setImagePath("widgets/tooltip");
m_showTimer = new QTimer(this);
m_showTimer->setSingleShot(true);
connect(m_showTimer, &QTimer::timeout, [=]() {
setVisible(false);
});
}
ToolTipDialog::~ToolTipDialog()
{
}
QQuickItem *ToolTipDialog::loadDefaultItem()
{
if (!m_qmlObject) {
m_qmlObject = new QmlObject(this);
}
if (!m_qmlObject->rootObject()) {
//HACK: search our own import
foreach (const QString &path, m_qmlObject->engine()->importPathList()) {
if (QFile::exists(path + "/org/kde/plasma/core")) {
m_qmlObject->setSource(QUrl::fromLocalFile(path + "/org/kde/plasma/core/private/DefaultToolTip.qml"));
break;
}
}
}
return qobject_cast<QQuickItem *>(m_qmlObject->rootObject());
}
void ToolTipDialog::showEvent(QShowEvent *event)
{
m_showTimer->start(4000);
DialogProxy::showEvent(event);
}
ToolTipDialog* ToolTipDialog::instance()
{
return toolTipDialogInstance();

View File

@ -29,6 +29,7 @@
class QQuickItem;
class QGraphicsWidget;
class QmlObject;
/**
* QML wrapper for kdelibs Plasma::ToolTipDialog
@ -43,11 +44,16 @@ public:
ToolTipDialog(QQuickItem *parent = 0);
~ToolTipDialog();
QQuickItem *loadDefaultItem();
static ToolTipDialog* instance();
Q_SIGNALS:
protected:
void showEvent(QShowEvent *event);
private:
QmlObject *m_qmlObject;
QTimer *m_showTimer;
};
#endif