Commit cbafc50a authored by Friedemann Kleint's avatar Friedemann Kleint

Debugger: Make tooltips use standard text editor tooltips.

Extend text editor tooltips by a 'widget content', making
it possible to show any widget utilizing the fact that the
QTipLabel actually is a frame (and thus a container).
Introduce concept of 'interactive' tooltips and modify
the tooltip-closing mechanism such that simple interaction
is possible. Emit the base text editor's tooltip signals
with the correct position and add API to calculate the tooltip
position from the cursor position.
Add API for pinning tooltips to the text editor (by removing
them from the QTipLabel layout).
Modify the Debugger's tooltipmanager not to manage tooltips
under TextEditor control and to take over control only once
tooltips are pinned.
Rubber-stamped-by: default avatarLeandro T. C. Melo <leandro.melo@nokia.com>
parent 6507f5ff
......@@ -500,7 +500,7 @@ bool CdbEngine::setToolTipExpression(const QPoint &mousePos,
tw->setDebuggerModel(LocalsWatch);
tw->setExpression(exp);
tw->acquireEngine(this);
DebuggerToolTipManager::instance()->add(mousePos, tw);
DebuggerToolTipManager::instance()->showToolTip(mousePos, editor, tw);
return true;
}
......
......@@ -2008,7 +2008,6 @@ void DebuggerPluginPrivate::cleanupViews()
{
m_reverseDirectionAction->setChecked(false);
m_reverseDirectionAction->setEnabled(false);
m_toolTipManager->closeUnpinnedToolTips();
if (!boolSetting(CloseBuffersOnExit))
return;
......
......@@ -53,9 +53,7 @@ class QStandardItemModel;
class QPlainTextEdit;
class QLabel;
class QToolBar;
class QMenu;
class QDebug;
class QAction;
QT_END_NAMESPACE
namespace Core {
......@@ -72,6 +70,7 @@ class DebuggerEngine;
namespace Internal {
class DraggableLabel;
class DebuggerToolTipEditor;
class PinnableToolTipWidget : public QWidget
{
......@@ -85,32 +84,28 @@ public:
};
explicit PinnableToolTipWidget(QWidget *parent = 0);
PinState pinState() const { return m_pinState; }
void addWidget(QWidget *w);
void addToolBarWidget(QWidget *w);
void addMenuAction(QAction *a);
// Add an action to "close all". Call in constructor after populating the tool button menu.
void addCloseAllMenuAction();
public slots:
void pin();
signals:
void closeAllRequested();
protected:
virtual void leaveEvent(QEvent *ev);
void pinned();
private slots:
void toolButtonClicked();
private:
virtual void doPin();
PinState m_pinState;
QVBoxLayout *m_mainVBoxLayout;
QToolBar *m_toolBar;
QToolButton *m_toolButton;
QMenu *m_menu;
};
class DebuggerToolTipContext
......@@ -165,9 +160,10 @@ public slots:
void acquireEngine(Debugger::DebuggerEngine *engine);
void releaseEngine();
void copy();
bool positionShow(const QPlainTextEdit *pe);
bool positionShow(const DebuggerToolTipEditor &pe);
private slots:
virtual void doPin();
void slotDragged(const QPoint &p);
protected:
......@@ -250,20 +246,19 @@ public:
static DebuggerToolTipManager *instance() { return m_instance; }
void registerEngine(DebuggerEngine *engine);
bool hasToolTips() const { return !m_pinnedTooltips.isEmpty(); }
// Collect all expressions of DebuggerTreeViewToolTipWidget
QStringList treeWidgetExpressions(const QString &fileName,
const QString &engineType = QString(),
const QString &function= QString()) const;
void add(const QPoint &p, AbstractDebuggerToolTipWidget *w);
void showToolTip(const QPoint &p, Core::IEditor *editor, AbstractDebuggerToolTipWidget *);
virtual bool eventFilter(QObject *, QEvent *);
static bool debug();
signals:
public slots:
void debugModeEntered();
void leavingDebugMode();
......@@ -271,31 +266,31 @@ public slots:
void loadSessionData();
void saveSessionData();
void closeAllToolTips();
void closeUnpinnedToolTips();
void hide();
void hide()
;
private slots:
void slotUpdateVisibleToolTips();
void slotDebuggerStateChanged(Debugger::DebuggerState);
void slotStackFrameCompleted();
void slotEditorOpened(Core::IEditor *);
void slotPinnedFirstTime();
void slotTooltipOverrideRequested(TextEditor::ITextEditor *editor, const QPoint &point,
int pos, bool *handled);
private:
typedef QList<QPointer<AbstractDebuggerToolTipWidget> > DebuggerToolTipWidgetList;
inline bool isActive() const { return !m_tooltips.isEmpty(); }
void add(AbstractDebuggerToolTipWidget *toolTipWidget);
void registerToolTip(AbstractDebuggerToolTipWidget *toolTipWidget);
void moveToolTipsBy(const QPoint &distance);
// Purge out closed (null) tooltips and return list for convenience
DebuggerToolTipWidgetList &purgeClosedToolTips();
static DebuggerToolTipManager *m_instance;
DebuggerToolTipWidgetList m_tooltips;
DebuggerToolTipWidgetList m_pinnedTooltips;
bool m_debugModeActive;
int m_lastToolTipPos;
QPoint m_lastToolTipPoint;
Core::IEditor *m_lastToolTipEditor;
};
......
......@@ -108,10 +108,12 @@ namespace Internal {
class GdbToolTipContext : public DebuggerToolTipContext
{
public:
GdbToolTipContext(const DebuggerToolTipContext &c) : DebuggerToolTipContext(c) {}
GdbToolTipContext(const DebuggerToolTipContext &c) :
DebuggerToolTipContext(c), editor(0) {}
QPoint mousePosition;
QString expression;
Core::IEditor *editor;
};
static const char winPythonVersionC[] = "python2.5";
......@@ -3406,7 +3408,8 @@ bool GdbEngine::showToolTip()
tw->setExpression(expression);
tw->setContext(*m_toolTipContext);
tw->acquireEngine(this);
DebuggerToolTipManager::instance()->add(m_toolTipContext->mousePosition, tw);
DebuggerToolTipManager::instance()->showToolTip(m_toolTipContext->mousePosition,
m_toolTipContext->editor, tw);
return true;
}
......@@ -3481,6 +3484,7 @@ bool GdbEngine::setToolTipExpression(const QPoint &mousePos,
m_toolTipContext.reset(new GdbToolTipContext(context));
m_toolTipContext->mousePosition = mousePos;
m_toolTipContext->expression = exp;
m_toolTipContext->editor = editor;
if (DebuggerToolTipManager::debug())
qDebug() << "GdbEngine::setToolTipExpression2 " << exp << (*m_toolTipContext);
......
......@@ -485,6 +485,10 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
menu.addAction(actShowInEditor);
menu.addAction(debuggerCore()->action(SettingsDialog));
QAction *actCloseEditorToolTips = new QAction(tr("Close Editor Tooltips"), &menu);
actCloseEditorToolTips->setEnabled(DebuggerToolTipManager::instance()->hasToolTips());
menu.addAction(actCloseEditorToolTips);
QAction *act = menu.exec(ev->globalPos());
if (act == 0)
return;
......@@ -538,6 +542,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
handler->setUnprintableBase(8);
} else if (act == showUnprintableHexadecimal) {
handler->setUnprintableBase(16);
} else if (act == actCloseEditorToolTips) {
DebuggerToolTipManager::instance()->closeAllToolTips();
} else {
for (int i = 0; i != typeFormatActions.size(); ++i) {
if (act == typeFormatActions.at(i))
......
......@@ -82,16 +82,7 @@ void BaseHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint
editor->setContextHelpId(QString());
process(editor, pos);
const QPoint &actualPoint = point - QPoint(0,
#ifdef Q_WS_WIN
24
#else
16
#endif
);
operateTooltip(editor, actualPoint);
operateTooltip(editor, point);
}
void BaseHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos)
......
......@@ -2507,6 +2507,19 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
q->setTextCursor(cursor);
}
// Calculate global position for a tooltip considering the left extra area.
QPoint BaseTextEditor::toolTipPosition(const QTextCursor &c) const
{
const QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1));
return cursorPos + QPoint(d->m_extraArea->width(),
#ifdef Q_WS_WIN
-24
#else
-16
#endif
);
}
bool BaseTextEditor::viewportEvent(QEvent *event)
{
d->m_contentsChanged = false;
......@@ -2530,15 +2543,13 @@ bool BaseTextEditor::viewportEvent(QEvent *event)
}
// Allow plugins to show tooltips
const QTextCursor &c = cursorForPosition(pos);
QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1));
cursorPos.setX(cursorPos.x() + d->m_extraArea->width());
const QTextCursor c = cursorForPosition(pos);
const QPoint toolTipPoint = toolTipPosition(c);
bool handled = false;
BaseTextEditorEditable *editable = editableInterface();
emit editable->tooltipOverrideRequested(editable, cursorPos, c.position(), &handled);
emit editable->tooltipOverrideRequested(editable, toolTipPoint, c.position(), &handled);
if (!handled)
emit editable->tooltipRequested(editable, cursorPos, c.position());
emit editable->tooltipRequested(editable, toolTipPoint, c.position());
return true;
}
return QPlainTextEdit::viewportEvent(event);
......
......@@ -235,6 +235,8 @@ public:
void setAutoCompleter(AutoCompleter *autoCompleter);
AutoCompleter *autoCompleter() const;
QPoint toolTipPosition(const QTextCursor &c) const;
public slots:
void setDisplayName(const QString &title);
......
......@@ -32,10 +32,14 @@
**************************************************************************/
#include "tipcontents.h"
#include "tooltip.h"
#include "tips.h"
#include <utils/qtcassert.h>
#include <QtCore/QtGlobal>
using namespace TextEditor;
namespace TextEditor {
TipContent::TipContent()
{}
......@@ -64,6 +68,11 @@ bool ColorContent::isValid() const
return m_color.isValid();
}
bool ColorContent::isInteractive() const
{
return false;
}
int ColorContent::showTime() const
{
return 4000;
......@@ -104,6 +113,11 @@ bool TextContent::isValid() const
return !m_text.isEmpty();
}
bool TextContent::isInteractive() const
{
return false;
}
int TextContent::showTime() const
{
return 10000 + 40 * qMax(0, m_text.length() - 100);
......@@ -122,3 +136,64 @@ const QString &TextContent::text() const
{
return m_text;
}
WidgetContent::WidgetContent(QWidget *w, bool interactive) :
m_widget(w), m_interactive(interactive)
{
}
TipContent *WidgetContent::clone() const
{
return new WidgetContent(m_widget, m_interactive);
}
int WidgetContent::typeId() const
{
return WIDGET_CONTENT_ID;
}
bool WidgetContent::isValid() const
{
return m_widget;
}
bool WidgetContent::isInteractive() const
{
return m_interactive;
}
void WidgetContent::setInteractive(bool i)
{
m_interactive = i;
}
int WidgetContent::showTime() const
{
return 30000;
}
bool WidgetContent::equals(const TipContent &tipContent) const
{
if (typeId() == tipContent.typeId()) {
if (m_widget == static_cast<const WidgetContent &>(tipContent).m_widget)
return true;
}
return false;
}
bool WidgetContent::pinToolTip(QWidget *w)
{
QTC_ASSERT(w, return false; )
// Find the parent WidgetTip, tell it to pin/release the
// widget and close.
for (QWidget *p = w->parentWidget(); p ; p = p->parentWidget()) {
if (Internal::WidgetTip *wt = qobject_cast<Internal::WidgetTip *>(p)) {
wt->pinToolTipWidget();
ToolTip::instance()->hide();
return true;
}
}
return false;
}
} // namespace TextEditor
......@@ -52,6 +52,7 @@ public:
virtual TipContent *clone() const = 0;
virtual int typeId() const = 0;
virtual bool isValid() const = 0;
virtual bool isInteractive() const = 0;
virtual int showTime() const = 0;
virtual bool equals(const TipContent &tipContent) const = 0;
};
......@@ -65,6 +66,7 @@ public:
virtual TipContent *clone() const;
virtual int typeId() const;
virtual bool isValid() const;
virtual bool isInteractive() const;
virtual int showTime() const;
virtual bool equals(const TipContent &tipContent) const;
......@@ -85,6 +87,7 @@ public:
virtual TipContent *clone() const;
virtual int typeId() const;
virtual bool isValid() const;
virtual bool isInteractive() const;
virtual int showTime() const;
virtual bool equals(const TipContent &tipContent) const;
......@@ -96,6 +99,34 @@ private:
QString m_text;
};
// A content for displaying any widget (with a layout).
class TEXTEDITOR_EXPORT WidgetContent : public TipContent
{
public:
explicit WidgetContent(QWidget *w, bool interactive = false);
virtual TipContent *clone() const;
virtual int typeId() const;
virtual bool isValid() const;
virtual int showTime() const;
virtual bool isInteractive() const;
void setInteractive(bool i);
virtual bool equals(const TipContent &tipContent) const;
// Helper to 'pin' (show as real window) a tooltip shown
// using WidgetContent
static bool pinToolTip(QWidget *w);
static const int WIDGET_CONTENT_ID = 42;
QWidget *widget() const { return m_widget; }
private:
QWidget *m_widget;
bool m_interactive;
};
} // namespace TextEditor
#endif // TIPCONTENTS_H
......@@ -34,6 +34,9 @@
#include "tipfactory.h"
#include "tipcontents.h"
#include "tips.h"
#include <utils/qtcassert.h>
#include <QtGui/QVBoxLayout>
using namespace TextEditor;
using namespace Internal;
......@@ -46,10 +49,13 @@ TipFactory::~TipFactory()
Internal::QTipLabel *TipFactory::createTip(const TipContent &content, QWidget *w)
{
QTipLabel *tip = 0;
if (content.typeId() == TextContent::TEXT_CONTENT_ID)
tip = new TextTip(w);
else if (content.typeId() == ColorContent::COLOR_CONTENT_ID)
tip = new ColorTip(w);
return tip;
return new TextTip(w);
if (content.typeId() == ColorContent::COLOR_CONTENT_ID)
return new ColorTip(w);
if (content.typeId() == WidgetContent::WIDGET_CONTENT_ID)
return new WidgetTip(w);
QTC_ASSERT(false, return 0; )
return 0;
}
......@@ -35,6 +35,8 @@
#include "tipcontents.h"
#include "reuse.h"
#include <utils/qtcassert.h>
#include <QtCore/QRect>
#include <QtGui/QColor>
#include <QtGui/QPainter>
......@@ -47,6 +49,7 @@
#include <QtGui/QStyleOptionFrame>
#include <QtGui/QResizeEvent>
#include <QtGui/QPaintEvent>
#include <QtGui/QVBoxLayout>
namespace TextEditor {
namespace Internal {
......@@ -77,6 +80,11 @@ QTipLabel::~QTipLabel()
delete m_tipContent;
}
bool QTipLabel::isInteractive() const
{
return m_tipContent && m_tipContent->isInteractive();
}
void QTipLabel::setContent(const TipContent &content)
{
if (m_tipContent)
......@@ -104,7 +112,7 @@ void ColorTip::configure(const QPoint &pos, QWidget *w)
update();
}
bool ColorTip::handleContentReplacement(const TipContent &content) const
bool ColorTip::canHandleContentReplacement(const TipContent &content) const
{
if (content.typeId() == ColorContent::COLOR_CONTENT_ID)
return true;
......@@ -175,7 +183,7 @@ void TextTip::configure(const QPoint &pos, QWidget *w)
resize(tipWidth, heightForWidth(tipWidth) + extraHeight);
}
bool TextTip::handleContentReplacement(const TipContent &content) const
bool TextTip::canHandleContentReplacement(const TipContent &content) const
{
if (content.typeId() == TextContent::TEXT_CONTENT_ID)
return true;
......@@ -204,6 +212,61 @@ void TextTip::resizeEvent(QResizeEvent *event)
QLabel::resizeEvent(event);
}
WidgetTip::WidgetTip(QWidget *parent) :
QTipLabel(parent), m_layout(new QVBoxLayout)
{
m_layout->setContentsMargins(0, 0, 0, 0);
setLayout(m_layout);
}
QWidget *WidgetTip::takeWidget(Qt::WindowFlags wf)
{
// Remove widget from layout
if (!m_layout->count())
return 0;
QLayoutItem *item = m_layout->takeAt(0);
QWidget *widget = item->widget();
delete item;
if (!widget)
return 0;
widget->setParent(0, wf);
return widget;
}
void WidgetTip::configure(const QPoint &pos, QWidget *)
{
const WidgetContent &anyContent = static_cast<const WidgetContent &>(content());
QWidget *widget = anyContent.widget();
QTC_ASSERT(widget && m_layout->count() == 0, return; )
move(pos);
m_layout->addWidget(widget);
m_layout->setSizeConstraint(QLayout::SetFixedSize);
adjustSize();
}
void WidgetTip::pinToolTipWidget()
{
QTC_ASSERT(m_layout->count(), return; )
// Pin the content widget: Rip the widget out of the layout
// and re-show as a tooltip, with delete on close.
const QPoint screenPos = mapToGlobal(QPoint(0, 0));
QWidget *widget = takeWidget(Qt::ToolTip);
QTC_ASSERT(widget, return; )
widget->move(screenPos);
widget->show();
widget->setAttribute(Qt::WA_DeleteOnClose);
}
bool WidgetTip::canHandleContentReplacement(const TipContent & ) const
{
// Always create a new widget.
return false;
}
// need to include it here to force it to be inside the namespaces
#include "moc_tips.cpp"
......
......@@ -38,6 +38,8 @@
#include <QtGui/QLabel>
#include <QtGui/QPixmap>
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
namespace TextEditor {
class TipContent;
}
......@@ -61,7 +63,9 @@ public:
const TextEditor::TipContent &content() const;
virtual void configure(const QPoint &pos, QWidget *w) = 0;
virtual bool handleContentReplacement(const TextEditor::TipContent &content) const = 0;
virtual bool canHandleContentReplacement(const TextEditor::TipContent &content) const = 0;
bool isInteractive() const;
private:
TextEditor::TipContent *m_tipContent;
......@@ -75,7 +79,7 @@ public:
virtual ~ColorTip();
virtual void configure(const QPoint &pos, QWidget *w);
virtual bool handleContentReplacement(const TipContent &content) const;
virtual bool canHandleContentReplacement(const TipContent &content) const;
private:
virtual void paintEvent(QPaintEvent *event);
......@@ -91,13 +95,31 @@ public:
virtual ~TextTip();
virtual void configure(const QPoint &pos, QWidget *w);
virtual bool handleContentReplacement(const TipContent &content) const;
virtual bool canHandleContentReplacement(const TipContent &content) const;
private:
virtual void paintEvent(QPaintEvent *event);
virtual void resizeEvent(QResizeEvent *event);
};
class WidgetTip : public QTipLabel
{
Q_OBJECT
public:
explicit WidgetTip(QWidget *parent = 0);
virtual void configure(const QPoint &pos, QWidget *w);
virtual bool canHandleContentReplacement(const TipContent &content) const;
public slots:
void pinToolTipWidget();
private:
QWidget *takeWidget(Qt::WindowFlags wf = 0);
QVBoxLayout *m_layout;
};
#ifndef Q_MOC_RUN
} // namespace Internal
} // namespace TextEditor
......
......@@ -43,6 +43,9 @@
#include <QtGui/QApplication>
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QMenu>
#include <QtCore/QDebug>
using namespace TextEditor;