forked from qt-creator/qt-creator
TextEditor: Allow asynchronous hover handlers
Change-Id: I956b126e2c779aa81f86e4432d127b45ac1912ff Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
#include "basehoverhandler.h"
|
#include "basehoverhandler.h"
|
||||||
#include "texteditor.h"
|
#include "texteditor.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/tooltip/tooltip.h>
|
#include <utils/tooltip/tooltip.h>
|
||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
@@ -33,6 +34,11 @@ namespace TextEditor {
|
|||||||
BaseHoverHandler::~BaseHoverHandler()
|
BaseHoverHandler::~BaseHoverHandler()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
bool BaseHoverHandler::isAsyncHandler() const
|
||||||
|
{
|
||||||
|
return m_isAsyncHandler;
|
||||||
|
}
|
||||||
|
|
||||||
void BaseHoverHandler::showToolTip(TextEditorWidget *widget, const QPoint &point, bool decorate)
|
void BaseHoverHandler::showToolTip(TextEditorWidget *widget, const QPoint &point, bool decorate)
|
||||||
{
|
{
|
||||||
if (decorate)
|
if (decorate)
|
||||||
@@ -40,13 +46,18 @@ void BaseHoverHandler::showToolTip(TextEditorWidget *widget, const QPoint &point
|
|||||||
operateTooltip(widget, point);
|
operateTooltip(widget, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseHoverHandler::checkToolTip(TextEditorWidget *widget, int pos)
|
void BaseHoverHandler::checkPriority(TextEditorWidget *widget,
|
||||||
|
int pos,
|
||||||
|
ReportPriority report)
|
||||||
{
|
{
|
||||||
widget->setContextHelpId(QString());
|
widget->setContextHelpId(QString());
|
||||||
|
|
||||||
process(widget, pos);
|
process(widget, pos, report);
|
||||||
|
}
|
||||||
|
|
||||||
return priority();
|
void BaseHoverHandler::cancelAsyncCheck()
|
||||||
|
{
|
||||||
|
QTC_CHECK(false && "BaseHoverHandler: Implement cancelCheck() in derived class!");
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseHoverHandler::priority() const
|
int BaseHoverHandler::priority() const
|
||||||
@@ -73,7 +84,7 @@ QString BaseHoverHandler::contextHelpId(TextEditorWidget *widget, int pos)
|
|||||||
// If the tooltip is visible and there is a help match, this match is used to update
|
// If the tooltip is visible and there is a help match, this match is used to update
|
||||||
// the help id. Otherwise, let the identification process happen.
|
// the help id. Otherwise, let the identification process happen.
|
||||||
if (!Utils::ToolTip::isVisible() || !lastHelpItemIdentified().isValid())
|
if (!Utils::ToolTip::isVisible() || !lastHelpItemIdentified().isValid())
|
||||||
process(widget, pos);
|
process(widget, pos, ReportPriority()); // TODO
|
||||||
|
|
||||||
if (lastHelpItemIdentified().isValid())
|
if (lastHelpItemIdentified().isValid())
|
||||||
return lastHelpItemIdentified().helpId();
|
return lastHelpItemIdentified().helpId();
|
||||||
@@ -100,13 +111,23 @@ const HelpItem &BaseHoverHandler::lastHelpItemIdentified() const
|
|||||||
return m_lastHelpItemIdentified;
|
return m_lastHelpItemIdentified;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseHoverHandler::process(TextEditorWidget *widget, int pos)
|
void BaseHoverHandler::process(TextEditorWidget *widget, int pos, ReportPriority report)
|
||||||
{
|
{
|
||||||
m_toolTip.clear();
|
m_toolTip.clear();
|
||||||
m_priority = -1;
|
m_priority = -1;
|
||||||
m_lastHelpItemIdentified = HelpItem();
|
m_lastHelpItemIdentified = HelpItem();
|
||||||
|
|
||||||
identifyMatch(widget, pos);
|
if (m_isAsyncHandler) {
|
||||||
|
identifyMatchAsync(widget, pos, report);
|
||||||
|
} else {
|
||||||
|
identifyMatch(widget, pos);
|
||||||
|
report(priority());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseHoverHandler::setIsAsyncHandler(bool isAsyncHandler)
|
||||||
|
{
|
||||||
|
m_isAsyncHandler = isAsyncHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
|
void BaseHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
|
||||||
@@ -116,6 +137,11 @@ void BaseHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
|
|||||||
setToolTip(tooltip);
|
setToolTip(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseHoverHandler::identifyMatchAsync(TextEditorWidget *, int, BaseHoverHandler::ReportPriority)
|
||||||
|
{
|
||||||
|
QTC_CHECK(false && "BaseHoverHandler: Implement identifyMatchAsync() in derived class!");
|
||||||
|
}
|
||||||
|
|
||||||
void BaseHoverHandler::decorateToolTip()
|
void BaseHoverHandler::decorateToolTip()
|
||||||
{
|
{
|
||||||
if (Qt::mightBeRichText(toolTip()))
|
if (Qt::mightBeRichText(toolTip()))
|
||||||
|
@@ -28,6 +28,8 @@
|
|||||||
#include "texteditor_global.h"
|
#include "texteditor_global.h"
|
||||||
#include "helpitem.h"
|
#include "helpitem.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QPoint;
|
class QPoint;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
@@ -41,9 +43,15 @@ class TEXTEDITOR_EXPORT BaseHoverHandler
|
|||||||
public:
|
public:
|
||||||
virtual ~BaseHoverHandler();
|
virtual ~BaseHoverHandler();
|
||||||
|
|
||||||
|
bool isAsyncHandler() const;
|
||||||
|
void setIsAsyncHandler(bool isAsyncHandler);
|
||||||
|
|
||||||
QString contextHelpId(TextEditorWidget *widget, int pos);
|
QString contextHelpId(TextEditorWidget *widget, int pos);
|
||||||
|
|
||||||
int checkToolTip(TextEditorWidget *widget, int pos);
|
using ReportPriority = std::function<void(int priority)>;
|
||||||
|
void checkPriority(TextEditorWidget *widget, int pos, ReportPriority report);
|
||||||
|
virtual void cancelAsyncCheck();
|
||||||
|
|
||||||
void showToolTip(TextEditorWidget *widget, const QPoint &point, bool decorate = true);
|
void showToolTip(TextEditorWidget *widget, const QPoint &point, bool decorate = true);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -63,11 +71,14 @@ protected:
|
|||||||
const HelpItem &lastHelpItemIdentified() const;
|
const HelpItem &lastHelpItemIdentified() const;
|
||||||
|
|
||||||
virtual void identifyMatch(TextEditorWidget *editorWidget, int pos);
|
virtual void identifyMatch(TextEditorWidget *editorWidget, int pos);
|
||||||
|
virtual void identifyMatchAsync(TextEditorWidget *editorWidget, int pos, ReportPriority report);
|
||||||
virtual void decorateToolTip();
|
virtual void decorateToolTip();
|
||||||
virtual void operateTooltip(TextEditorWidget *editorWidget, const QPoint &point);
|
virtual void operateTooltip(TextEditorWidget *editorWidget, const QPoint &point);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process(TextEditorWidget *widget, int pos);
|
void process(TextEditorWidget *widget, int pos, ReportPriority report);
|
||||||
|
|
||||||
|
bool m_isAsyncHandler = false;
|
||||||
|
|
||||||
QString m_toolTip;
|
QString m_toolTip;
|
||||||
HelpItem m_lastHelpItemIdentified;
|
HelpItem m_lastHelpItemIdentified;
|
||||||
|
@@ -248,6 +248,119 @@ public:
|
|||||||
TextEditorFactoryPrivate *m_origin;
|
TextEditorFactoryPrivate *m_origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HoverHandlerRunner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HoverHandlerRunner(TextEditorWidget *widget, QList<BaseHoverHandler *> &handlers)
|
||||||
|
: m_widget(widget)
|
||||||
|
, m_handlers(handlers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void startChecking(const QTextCursor &textCursor, const QPoint &point)
|
||||||
|
{
|
||||||
|
if (m_handlers.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Does the last handler still applies?
|
||||||
|
const int documentRevision = textCursor.document()->revision();
|
||||||
|
const int position = Convenience::wordStartCursor(textCursor).position();
|
||||||
|
if (m_lastHandlerInfo.applies(documentRevision, position)) {
|
||||||
|
m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel currently running checks
|
||||||
|
for (BaseHoverHandler *handler : m_handlers) {
|
||||||
|
if (handler->isAsyncHandler())
|
||||||
|
handler->cancelAsyncCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update invocation data
|
||||||
|
m_documentRevision = documentRevision;
|
||||||
|
m_position = position;
|
||||||
|
m_point = point;
|
||||||
|
|
||||||
|
// Re-initialize process data
|
||||||
|
m_currentHandlerIndex = 0;
|
||||||
|
m_bestHandler = nullptr;
|
||||||
|
m_highestHandlerPriority = -1;
|
||||||
|
|
||||||
|
// Start checking
|
||||||
|
checkNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkNext()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
|
||||||
|
BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
|
||||||
|
|
||||||
|
currentHandler->checkPriority(m_widget, m_position, [this](int priority) {
|
||||||
|
onHandlerFinished(m_documentRevision, m_position, priority);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onHandlerFinished(int documentRevision, int position, int priority)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
|
||||||
|
QTC_ASSERT(documentRevision == m_documentRevision, return);
|
||||||
|
QTC_ASSERT(position == m_position, return);
|
||||||
|
|
||||||
|
BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
|
||||||
|
if (priority > m_highestHandlerPriority) {
|
||||||
|
m_highestHandlerPriority = priority;
|
||||||
|
m_bestHandler = currentHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are more, check next
|
||||||
|
++m_currentHandlerIndex;
|
||||||
|
if (m_currentHandlerIndex < m_handlers.size()) {
|
||||||
|
checkNext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All were queried, run the best
|
||||||
|
if (m_bestHandler) {
|
||||||
|
m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position);
|
||||||
|
m_bestHandler->showToolTip(m_widget, m_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextEditorWidget *m_widget = nullptr;
|
||||||
|
const QList<BaseHoverHandler *> &m_handlers;
|
||||||
|
|
||||||
|
struct LastHandlerInfo {
|
||||||
|
LastHandlerInfo() = default;
|
||||||
|
LastHandlerInfo(BaseHoverHandler *handler, int documentRevision, int cursorPosition)
|
||||||
|
: handler(handler)
|
||||||
|
, documentRevision(documentRevision)
|
||||||
|
, cursorPosition(cursorPosition)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool applies(int documentRevision, int cursorPosition) const
|
||||||
|
{
|
||||||
|
return handler
|
||||||
|
&& documentRevision == this->documentRevision
|
||||||
|
&& cursorPosition == this->cursorPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseHoverHandler *handler = nullptr;
|
||||||
|
int documentRevision = -1;
|
||||||
|
int cursorPosition = -1;
|
||||||
|
} m_lastHandlerInfo;
|
||||||
|
|
||||||
|
// invocation data
|
||||||
|
QPoint m_point;
|
||||||
|
int m_position = -1;
|
||||||
|
int m_documentRevision = -1;
|
||||||
|
|
||||||
|
// processing data
|
||||||
|
int m_currentHandlerIndex = -1;
|
||||||
|
int m_highestHandlerPriority = -1;
|
||||||
|
BaseHoverHandler *m_bestHandler = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
class TextEditorWidgetPrivate : public QObject
|
class TextEditorWidgetPrivate : public QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -469,26 +582,8 @@ public:
|
|||||||
CodeAssistant m_codeAssistant;
|
CodeAssistant m_codeAssistant;
|
||||||
bool m_assistRelevantContentAdded = false;
|
bool m_assistRelevantContentAdded = false;
|
||||||
|
|
||||||
struct LastHoverHandlerInfo {
|
|
||||||
LastHoverHandlerInfo() = default;
|
|
||||||
LastHoverHandlerInfo(BaseHoverHandler *handler, int documentRevision, int cursorPosition)
|
|
||||||
: handler(handler)
|
|
||||||
, documentRevision(documentRevision)
|
|
||||||
, cursorPosition(cursorPosition)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool applies(int documentRevision, int cursorPosition) const
|
|
||||||
{
|
|
||||||
return handler
|
|
||||||
&& documentRevision == this->documentRevision
|
|
||||||
&& cursorPosition == this->cursorPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseHoverHandler *handler = nullptr;
|
|
||||||
int documentRevision = -1;
|
|
||||||
int cursorPosition = -1;
|
|
||||||
} m_lastHoverHandlerInfo;
|
|
||||||
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
|
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
|
||||||
|
HoverHandlerRunner m_hoverHandlerRunner;
|
||||||
|
|
||||||
QPointer<QSequentialAnimationGroup> m_navigationAnimation;
|
QPointer<QSequentialAnimationGroup> m_navigationAnimation;
|
||||||
|
|
||||||
@@ -535,6 +630,7 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent)
|
|||||||
m_requestMarkEnabled(true),
|
m_requestMarkEnabled(true),
|
||||||
m_lineSeparatorsAllowed(false),
|
m_lineSeparatorsAllowed(false),
|
||||||
m_maybeFakeTooltipEvent(false),
|
m_maybeFakeTooltipEvent(false),
|
||||||
|
m_hoverHandlerRunner(parent, m_hoverHandlers),
|
||||||
m_clipboardAssistProvider(new ClipboardAssistProvider),
|
m_clipboardAssistProvider(new ClipboardAssistProvider),
|
||||||
m_autoCompleter(new AutoCompleter)
|
m_autoCompleter(new AutoCompleter)
|
||||||
{
|
{
|
||||||
@@ -3181,30 +3277,7 @@ void TextEditorWidgetPrivate::processTooltipRequest(const QTextCursor &c)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does the last handler still applies?
|
m_hoverHandlerRunner.startChecking(c, toolTipPoint);
|
||||||
const int documentRevision = m_document->document()->revision();
|
|
||||||
const int cursorPosition = Convenience::wordStartCursor(c).position();
|
|
||||||
if (m_lastHoverHandlerInfo.applies(documentRevision, cursorPosition)) {
|
|
||||||
m_lastHoverHandlerInfo.handler->showToolTip(q, toolTipPoint, /*decorate=*/ false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine best handler
|
|
||||||
int highestPriority = -1;
|
|
||||||
BaseHoverHandler *highest = 0;
|
|
||||||
foreach (BaseHoverHandler *handler, m_hoverHandlers) {
|
|
||||||
int priority = handler->checkToolTip(q, c.position());
|
|
||||||
if (priority > highestPriority) {
|
|
||||||
highestPriority = priority;
|
|
||||||
highest = handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the best handler show the tooltip
|
|
||||||
if (highest) {
|
|
||||||
m_lastHoverHandlerInfo = LastHoverHandlerInfo{highest, documentRevision, cursorPosition};
|
|
||||||
highest->showToolTip(q, toolTipPoint);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextEditorWidgetPrivate::processAnnotaionTooltipRequest(const QTextBlock &block,
|
bool TextEditorWidgetPrivate::processAnnotaionTooltipRequest(const QTextBlock &block,
|
||||||
|
Reference in New Issue
Block a user