Files
qt-creator/src/plugins/texteditor/codeassist/functionhintproposalwidget.cpp
Tobias Hunger 397e7f4843 Update License according to agreement with Free Qt Foundation
* Update files in src/plugins

Change-Id: Ia5d77fad7d19d4bb3498e78661982f68729adb22
Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
2016-01-19 15:57:01 +00:00

325 lines
9.7 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "functionhintproposalwidget.h"
#include "ifunctionhintproposalmodel.h"
#include "codeassistant.h"
#include <utils/faketooltip.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <QDebug>
#include <QApplication>
#include <QLabel>
#include <QToolButton>
#include <QHBoxLayout>
#include <QDesktopWidget>
#include <QKeyEvent>
#include <QPointer>
namespace TextEditor {
// -------------------------
// HintProposalWidgetPrivate
// -------------------------
struct FunctionHintProposalWidgetPrivate
{
FunctionHintProposalWidgetPrivate();
const QWidget *m_underlyingWidget;
CodeAssistant *m_assistant;
IFunctionHintProposalModel *m_model;
QPointer<Utils::FakeToolTip> m_popupFrame;
QLabel *m_numberLabel;
QLabel *m_hintLabel;
QWidget *m_pager;
QRect m_displayRect;
int m_currentHint;
int m_totalHints;
int m_currentArgument;
bool m_escapePressed;
};
FunctionHintProposalWidgetPrivate::FunctionHintProposalWidgetPrivate()
: m_underlyingWidget(0)
, m_assistant(0)
, m_model(0)
, m_popupFrame(new Utils::FakeToolTip)
, m_numberLabel(new QLabel)
, m_hintLabel(new QLabel)
, m_pager(new QWidget)
, m_currentHint(-1)
, m_totalHints(0)
, m_currentArgument(-1)
, m_escapePressed(false)
{
m_hintLabel->setTextFormat(Qt::RichText);
}
// ------------------
// HintProposalWidget
// ------------------
FunctionHintProposalWidget::FunctionHintProposalWidget()
: d(new FunctionHintProposalWidgetPrivate)
{
QToolButton *downArrow = new QToolButton;
downArrow->setArrowType(Qt::DownArrow);
downArrow->setFixedSize(16, 16);
downArrow->setAutoRaise(true);
QToolButton *upArrow = new QToolButton;
upArrow->setArrowType(Qt::UpArrow);
upArrow->setFixedSize(16, 16);
upArrow->setAutoRaise(true);
QHBoxLayout *pagerLayout = new QHBoxLayout(d->m_pager);
pagerLayout->setMargin(0);
pagerLayout->setSpacing(0);
pagerLayout->addWidget(upArrow);
pagerLayout->addWidget(d->m_numberLabel);
pagerLayout->addWidget(downArrow);
QHBoxLayout *popupLayout = new QHBoxLayout(d->m_popupFrame);
popupLayout->setMargin(0);
popupLayout->setSpacing(0);
popupLayout->addWidget(d->m_pager);
popupLayout->addWidget(d->m_hintLabel);
connect(upArrow, &QAbstractButton::clicked, this, &FunctionHintProposalWidget::previousPage);
connect(downArrow, &QAbstractButton::clicked, this, &FunctionHintProposalWidget::nextPage);
connect(d->m_popupFrame.data(), &QObject::destroyed, this, &FunctionHintProposalWidget::abort);
setFocusPolicy(Qt::NoFocus);
}
FunctionHintProposalWidget::~FunctionHintProposalWidget()
{
delete d->m_model;
delete d;
}
void FunctionHintProposalWidget::setAssistant(CodeAssistant *assistant)
{
d->m_assistant = assistant;
}
void FunctionHintProposalWidget::setReason(AssistReason)
{}
void FunctionHintProposalWidget::setKind(AssistKind)
{}
void FunctionHintProposalWidget::setUnderlyingWidget(const QWidget *underlyingWidget)
{
d->m_underlyingWidget = underlyingWidget;
}
void FunctionHintProposalWidget::setModel(IAssistProposalModel *model)
{
d->m_model = static_cast<IFunctionHintProposalModel *>(model);
}
void FunctionHintProposalWidget::setDisplayRect(const QRect &rect)
{
d->m_displayRect = rect;
}
void FunctionHintProposalWidget::setIsSynchronized(bool)
{}
void FunctionHintProposalWidget::showProposal(const QString &prefix)
{
QTC_ASSERT(d->m_model && d->m_assistant, abort(); return; );
d->m_totalHints = d->m_model->size();
QTC_ASSERT(d->m_totalHints != 0, abort(); return; );
d->m_pager->setVisible(d->m_totalHints > 1);
d->m_currentHint = 0;
QTC_ASSERT(updateAndCheck(prefix), abort(); return; );
qApp->installEventFilter(this);
d->m_popupFrame->show();
}
void FunctionHintProposalWidget::updateProposal(const QString &prefix)
{
updateAndCheck(prefix);
}
void FunctionHintProposalWidget::closeProposal()
{
abort();
}
void FunctionHintProposalWidget::abort()
{
qApp->removeEventFilter(this);
if (d->m_popupFrame->isVisible())
d->m_popupFrame->close();
deleteLater();
}
bool FunctionHintProposalWidget::eventFilter(QObject *obj, QEvent *e)
{
switch (e->type()) {
case QEvent::ShortcutOverride:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
d->m_escapePressed = true;
e->accept();
}
break;
case QEvent::KeyPress:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
d->m_escapePressed = true;
e->accept();
}
QTC_CHECK(d->m_model);
if (d->m_model && d->m_model->size() > 1) {
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
if (ke->key() == Qt::Key_Up) {
previousPage();
return true;
} else if (ke->key() == Qt::Key_Down) {
nextPage();
return true;
}
return false;
}
break;
case QEvent::KeyRelease: {
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
if (ke->key() == Qt::Key_Escape && d->m_escapePressed) {
abort();
emit explicitlyAborted();
return false;
} else if (ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down) {
QTC_CHECK(d->m_model);
if (d->m_model && d->m_model->size() > 1)
return false;
}
QTC_CHECK(d->m_assistant);
if (d->m_assistant)
d->m_assistant->notifyChange();
}
break;
case QEvent::WindowDeactivate:
case QEvent::FocusOut:
if (obj != d->m_underlyingWidget)
break;
abort();
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::Wheel:
if (QWidget *widget = qobject_cast<QWidget *>(obj)) {
if (d->m_popupFrame && !d->m_popupFrame->isAncestorOf(widget)) {
abort();
} else if (e->type() == QEvent::Wheel) {
if (static_cast<QWheelEvent*>(e)->delta() > 0)
previousPage();
else
nextPage();
return true;
}
}
break;
default:
break;
}
return false;
}
void FunctionHintProposalWidget::nextPage()
{
d->m_currentHint = (d->m_currentHint + 1) % d->m_totalHints;
updateContent();
}
void FunctionHintProposalWidget::previousPage()
{
if (d->m_currentHint == 0)
d->m_currentHint = d->m_totalHints - 1;
else
--d->m_currentHint;
updateContent();
}
bool FunctionHintProposalWidget::updateAndCheck(const QString &prefix)
{
const int activeArgument = d->m_model->activeArgument(prefix);
if (activeArgument == -1) {
abort();
return false;
} else if (activeArgument != d->m_currentArgument) {
d->m_currentArgument = activeArgument;
updateContent();
}
return true;
}
void FunctionHintProposalWidget::updateContent()
{
d->m_hintLabel->setText(d->m_model->text(d->m_currentHint));
d->m_numberLabel->setText(tr("%1 of %2").arg(d->m_currentHint + 1).arg(d->m_totalHints));
updatePosition();
}
void FunctionHintProposalWidget::updatePosition()
{
const QDesktopWidget *desktop = QApplication::desktop();
const QRect &screen = Utils::HostOsInfo::isMacHost()
? desktop->availableGeometry(desktop->screenNumber(d->m_underlyingWidget))
: desktop->screenGeometry(desktop->screenNumber(d->m_underlyingWidget));
d->m_pager->setFixedWidth(d->m_pager->minimumSizeHint().width());
d->m_hintLabel->setWordWrap(false);
const int maxDesiredWidth = screen.width() - 10;
const QSize &minHint = d->m_popupFrame->minimumSizeHint();
if (minHint.width() > maxDesiredWidth) {
d->m_hintLabel->setWordWrap(true);
d->m_popupFrame->setFixedWidth(maxDesiredWidth);
const int extra = d->m_popupFrame->contentsMargins().bottom() +
d->m_popupFrame->contentsMargins().top();
d->m_popupFrame->setFixedHeight(
d->m_hintLabel->heightForWidth(maxDesiredWidth - d->m_pager->width()) + extra);
} else {
d->m_popupFrame->setFixedSize(minHint);
}
const QSize &sz = d->m_popupFrame->size();
QPoint pos = d->m_displayRect.topLeft();
pos.setY(pos.y() - sz.height() - 1);
if (pos.x() + sz.width() > screen.right())
pos.setX(screen.right() - sz.width());
d->m_popupFrame->move(pos);
}
} // TextEditor