Commit 3119d93b authored by hjk's avatar hjk
Browse files

VariableChooser: Rework



Allow multiple expanders to be registered for lineedits, e.g. a
local and the global ones, and actually show them.

Use a tree view in the chooser for somewhat more structured display.

Change-Id: I769f92144e5249f45e54381de52aa6973eb20118
Reviewed-by: default avatarTobias Hunger <tobias.hunger@theqtcompany.com>
parent e279c7e0
......@@ -49,6 +49,7 @@ public:
QHash<QByteArray, MacroExpander::StringFunction> m_map;
QHash<QByteArray, MacroExpander::PrefixFunction> m_prefixMap;
QMap<QByteArray, QString> m_descriptions;
QString m_displayName;
};
} // Internal
......@@ -320,4 +321,37 @@ QString MacroExpander::variableDescription(const QByteArray &variable)
return d->m_descriptions.value(variable);
}
QString MacroExpander::displayName() const
{
return d->m_displayName;
}
void MacroExpander::setDisplayName(const QString &displayName)
{
d->m_displayName = displayName;
}
class GlobalMacroExpander : public MacroExpander
{
Q_DECLARE_TR_FUNCTIONS(Utils::MacroExpander)
public:
GlobalMacroExpander()
{
setDisplayName(tr("Global variables"));
registerPrefix("Env", tr("Access environment variables."),
[](const QString &value) { return QString::fromLocal8Bit(qgetenv(value.toLocal8Bit())); });
}
};
/*!
* Returns the expander for globally registered variables.
*/
MacroExpander *globalMacroExpander()
{
static GlobalMacroExpander theGlobalExpander;
return &theGlobalExpander;
}
} // namespace Utils
......@@ -70,6 +70,9 @@ public:
QList<QByteArray> variables();
QString variableDescription(const QByteArray &variable);
QString displayName() const;
void setDisplayName(const QString &displayName);
private:
MacroExpander(const MacroExpander &) Q_DECL_EQ_DELETE;
void operator=(const MacroExpander &) Q_DECL_EQ_DELETE;
......@@ -77,6 +80,8 @@ private:
Internal::MacroExpanderPrivate *d;
};
QTCREATOR_UTILS_EXPORT MacroExpander *globalMacroExpander();
} // namespace Utils
#endif // UTILS_MACROEXPANDER_H
......@@ -79,9 +79,9 @@ BareMetalDeviceConfigurationWidget::BareMetalDeviceConfigurationWidget(
formLayout->addRow(tr("Init commands:"), m_gdbInitCommandsTextEdit);
formLayout->addRow(tr("Reset commands:"), m_gdbResetCommandsTextEdit);
VariableChooser::addVariableSupport(m_gdbResetCommandsTextEdit);
VariableChooser::addVariableSupport(m_gdbInitCommandsTextEdit);
(void)new VariableChooser(this);
auto chooser = new VariableChooser(this);
chooser->addSupportedWidget(m_gdbResetCommandsTextEdit);
chooser->addSupportedWidget(m_gdbInitCommandsTextEdit);
connect(m_gdbHostLineEdit, SIGNAL(editingFinished()), SLOT(hostnameChanged()));
connect(m_gdbPortSpinBox, SIGNAL(valueChanged(int)), SLOT(portChanged()));
......
......@@ -88,9 +88,9 @@ BareMetalDeviceConfigurationWizardSetupPage::BareMetalDeviceConfigurationWizardS
connect(m_gdbResetCommandsTextEdit, SIGNAL(textChanged()), SIGNAL(completeChanged()));
connect(m_gdbInitCommandsPlainTextEdit, SIGNAL(textChanged()), SIGNAL(completeChanged()));
VariableChooser::addVariableSupport(m_gdbResetCommandsTextEdit);
VariableChooser::addVariableSupport(m_gdbInitCommandsPlainTextEdit);
(void)new VariableChooser(this);
auto chooser = new VariableChooser(this);
chooser->addSupportedWidget(m_gdbResetCommandsTextEdit);
chooser->addSupportedWidget(m_gdbInitCommandsPlainTextEdit);
}
void BareMetalDeviceConfigurationWizardSetupPage::initializePage()
......
......@@ -64,7 +64,6 @@
#include <coreplugin/infobar.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/variablemanager.h>
#include <QDebug>
#include <QDir>
......
......@@ -53,7 +53,6 @@ SOURCES += corejsextensions.cpp \
progressmanager/futureprogress.cpp \
statusbarwidget.cpp \
coreplugin.cpp \
variablemanager.cpp \
modemanager.cpp \
basefilewizard.cpp \
basefilewizardfactory.cpp \
......@@ -165,7 +164,6 @@ HEADERS += corejsextensions.h \
core_global.h \
statusbarwidget.h \
coreplugin.h \
variablemanager.h \
modemanager.h \
basefilewizard.h \
basefilewizardfactory.h \
......
......@@ -100,7 +100,6 @@ QtcPlugin {
"textdocument.cpp", "textdocument.h",
"toolsettings.cpp", "toolsettings.h",
"variablechooser.cpp", "variablechooser.h",
"variablemanager.cpp", "variablemanager.h",
"vcsmanager.cpp", "vcsmanager.h",
"versiondialog.cpp", "versiondialog.h",
"windowsupport.cpp", "windowsupport.h"
......
......@@ -33,13 +33,13 @@
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/fancylineedit.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/variablechooser.h>
#include <coreplugin/variablemanager.h>
#include <QTextStream>
#include <QMimeData>
......@@ -410,10 +410,11 @@ ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
connect(ui->toolTree->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
this, SLOT(handleCurrentChanged(QModelIndex,QModelIndex)));
Core::VariableChooser::addVariableSupport(ui->executable->lineEdit());
Core::VariableChooser::addVariableSupport(ui->arguments);
Core::VariableChooser::addVariableSupport(ui->workingDirectory->lineEdit());
Core::VariableChooser::addVariableSupport(ui->inputText);
auto chooser = new VariableChooser(this);
chooser->addSupportedWidget(ui->executable->lineEdit());
chooser->addSupportedWidget(ui->arguments);
chooser->addSupportedWidget(ui->workingDirectory->lineEdit());
chooser->addSupportedWidget(ui->inputText);
connect(ui->description, SIGNAL(editingFinished()), this, SLOT(updateCurrentItem()));
connect(ui->executable, SIGNAL(editingFinished()), this, SLOT(updateCurrentItem()));
......@@ -441,7 +442,6 @@ ExternalToolConfig::ExternalToolConfig(QWidget *parent) :
showInfoForItem(QModelIndex());
new VariableChooser(this);
}
ExternalToolConfig::~ExternalToolConfig()
......@@ -595,5 +595,5 @@ void ExternalToolConfig::addCategory()
void ExternalToolConfig::updateEffectiveArguments()
{
ui->arguments->setToolTip(Utils::QtcProcess::expandMacros(ui->arguments->text(),
globalMacroExpander()));
Utils::globalMacroExpander()));
}
......@@ -59,7 +59,6 @@
#include <coreplugin/outputpanemanager.h>
#include <coreplugin/rightpane.h>
#include <coreplugin/settingsdatabase.h>
#include <coreplugin/variablemanager.h>
#include <coreplugin/vcsmanager.h>
#include <extensionsystem/pluginmanager.h>
......@@ -67,6 +66,7 @@
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
#include <QClipboard>
......@@ -87,6 +87,8 @@
#include <QPushButton>
#include <QSplitter>
using namespace Utils;
enum { debugEditorManager=0 };
static const char kCurrentDocumentPrefix[] = "CurrentDocument";
......
......@@ -33,7 +33,6 @@
#include "actionmanager/actionmanager.h"
#include "actionmanager/actioncontainer.h"
#include "coreconstants.h"
#include "variablemanager.h"
#include <app/app_version.h>
#include <coreplugin/icore.h>
......@@ -41,8 +40,10 @@
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <utils/qtcassert.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QCoreApplication>
......@@ -54,6 +55,7 @@
#include <QDebug>
using namespace Utils;
using namespace Core;
using namespace Core::Internal;
......
......@@ -30,8 +30,8 @@
#include "jsexpander.h"
#include "corejsextensions.h"
#include "variablemanager.h"
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
......@@ -90,7 +90,7 @@ QString JsExpander::evaluate(const QString &expression, QString *errorMessage)
JsExpander::JsExpander()
{
d = new Internal::JsExpanderPrivate;
globalMacroExpander()->registerPrefix("JS",
Utils::globalMacroExpander()->registerPrefix("JS",
QCoreApplication::translate("Core::JsExpander",
"Evaluate simple Javascript statements.\n"
"The statements may not contain '{' nor '}' characters."),
......
......@@ -32,7 +32,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/variablemanager.h>
#include <utils/macroexpander.h>
#include <QMessageBox>
......@@ -91,9 +91,9 @@ void ExecuteFilter::accept(LocatorFilterEntry selection) const
p->m_commandHistory.prepend(value);
bool found;
QString workingDirectory = globalMacroExpander()->value("CurrentDocument:Path", &found);
QString workingDirectory = Utils::globalMacroExpander()->value("CurrentDocument:Path", &found);
if (!found || workingDirectory.isEmpty())
workingDirectory = globalMacroExpander()->value("CurrentProject:Path", &found);
workingDirectory = Utils::globalMacroExpander()->value("CurrentProject:Path", &found);
ExecuteData d;
d.workingDirectory = workingDirectory;
......
......@@ -45,7 +45,6 @@
#include "outputpanemanager.h"
#include "plugindialog.h"
#include "vcsmanager.h"
#include "variablemanager.h"
#include "versiondialog.h"
#include "statusbarmanager.h"
#include "id.h"
......
......@@ -29,67 +29,36 @@
****************************************************************************/
#include "variablechooser.h"
#include "variablemanager.h"
#include "coreconstants.h"
#include <utils/fancylineedit.h> // IconButton
#include <utils/macroexpander.h>
#include <utils/treemodel.h>
#include <utils/qtcassert.h>
#include <QApplication>
#include <QAbstractItemModel>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPlainTextEdit>
#include <QPointer>
#include <QTextEdit>
#include <QTimer>
#include <QTreeView>
#include <QVBoxLayout>
#include <QVector>
using namespace Utils;
namespace Core {
namespace Internal {
/*!
* \internal
*/
class VariableChooserPrivate : public QObject
{
Q_OBJECT
public:
VariableChooserPrivate(VariableChooser *parent)
: q(parent),
m_defaultDescription(tr("Select a variable to insert.")),
m_lineEdit(0),
m_textEdit(0),
m_plainTextEdit(0)
{
m_variableList = new QListWidget(q);
m_variableList->setAttribute(Qt::WA_MacSmallSize);
m_variableList->setAttribute(Qt::WA_MacShowFocusRect, false);
foreach (const QByteArray &variable, globalMacroExpander()->variables())
m_variableList->addItem(QString::fromLatin1(variable));
m_variableDescription = new QLabel(q);
m_variableDescription->setText(m_defaultDescription);
m_variableDescription->setMinimumSize(QSize(0, 60));
m_variableDescription->setAlignment(Qt::AlignLeft|Qt::AlignTop);
m_variableDescription->setWordWrap(true);
m_variableDescription->setAttribute(Qt::WA_MacSmallSize);
QVBoxLayout *verticalLayout = new QVBoxLayout(q);
verticalLayout->setContentsMargins(3, 3, 3, 12);
verticalLayout->addWidget(m_variableList);
verticalLayout->addWidget(m_variableDescription);
connect(m_variableList, SIGNAL(currentTextChanged(QString)),
this, SLOT(updateDescription(QString)));
connect(m_variableList, SIGNAL(itemActivated(QListWidgetItem*)),
this, SLOT(handleItemActivated(QListWidgetItem*)));
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(updateCurrentEditor(QWidget*,QWidget*)));
updateCurrentEditor(0, qApp->focusWidget());
}
VariableChooserPrivate(VariableChooser *parent);
void createIconButton()
{
......@@ -97,30 +66,140 @@ public:
m_iconButton->setPixmap(QPixmap(QLatin1String(":/core/images/replace.png")));
m_iconButton->setToolTip(tr("Insert variable"));
m_iconButton->hide();
connect(m_iconButton, SIGNAL(clicked()), this, SLOT(updatePositionAndShow()));
connect(m_iconButton.data(), static_cast<void(QAbstractButton::*)(bool)>(&QAbstractButton::clicked),
this, &VariableChooserPrivate::updatePositionAndShow);
}
public slots:
void updateDescription(const QString &variable);
void updateDescription(const QModelIndex &index);
void updateCurrentEditor(QWidget *old, QWidget *widget);
void handleItemActivated(QListWidgetItem *item);
void handleItemActivated(const QModelIndex &index);
void insertVariable(const QString &variable);
void updatePositionAndShow();
void updatePositionAndShow(bool);
public:
QWidget *currentWidget();
public:
VariableChooser *q;
QString m_defaultDescription;
TreeModel m_model;
QPointer<QLineEdit> m_lineEdit;
QPointer<QTextEdit> m_textEdit;
QPointer<QPlainTextEdit> m_plainTextEdit;
QPointer<Utils::IconButton> m_iconButton;
QListWidget *m_variableList;
QTreeView *m_variableTree;
QLabel *m_variableDescription;
QString m_defaultDescription;
QByteArray m_currentVariableName; // Prevent recursive insertion of currently expanded item
};
class VariableItem : public TreeItem
{
public:
VariableItem()
{}
QVariant data(int column, int role) const
{
if (role == Qt::DisplayRole || role == Qt::EditRole) {
if (column == 0)
return m_display;
}
if (role == Qt::ToolTipRole)
return m_description;
return QVariant();
}
public:
QString m_display;
QString m_description;
};
class VariableGroupItem : public TreeItem
{
public:
VariableGroupItem(VariableChooserPrivate *chooser)
: m_chooser(chooser), m_expander(0)
{
setLazy(true);
}
bool ensureExpander() const
{
if (!m_expander)
m_expander = m_provider();
return m_expander != 0;
}
QVariant data(int column, int role) const
{
if (role == Qt::DisplayRole || role == Qt::EditRole) {
if (column == 0 && ensureExpander())
return m_expander->displayName();
}
return QVariant();
}
void populate()
{
if (ensureExpander()) {
foreach (const QByteArray &variable, m_expander->variables()) {
auto item = new VariableItem;
item->m_display = QString::fromLatin1(variable);
item->m_description = m_expander->variableDescription(variable);
if (variable == m_chooser->m_currentVariableName)
item->setFlags(Qt::ItemIsSelectable); // not ItemIsEnabled
appendChild(item);
}
}
}
public:
VariableChooserPrivate *m_chooser; // Not owned.
MacroExpanderProvider m_provider;
mutable MacroExpander *m_expander; // Not owned.
QString m_displayName;
};
VariableChooserPrivate::VariableChooserPrivate(VariableChooser *parent)
: q(parent),
m_lineEdit(0),
m_textEdit(0),
m_plainTextEdit(0)
{
m_defaultDescription = VariableChooser::tr("Select a variable to insert.");
m_variableTree = new QTreeView(q);
m_variableTree->setAttribute(Qt::WA_MacSmallSize);
m_variableTree->setAttribute(Qt::WA_MacShowFocusRect, false);
m_variableTree->setModel(&m_model);
m_variableTree->header()->hide();
m_variableTree->header()->setStretchLastSection(true);
m_variableDescription = new QLabel(q);
m_variableDescription->setText(m_defaultDescription);
m_variableDescription->setMinimumSize(QSize(0, 60));
m_variableDescription->setAlignment(Qt::AlignLeft|Qt::AlignTop);
m_variableDescription->setWordWrap(true);
m_variableDescription->setAttribute(Qt::WA_MacSmallSize);
QVBoxLayout *verticalLayout = new QVBoxLayout(q);
verticalLayout->setContentsMargins(3, 3, 3, 12);
verticalLayout->addWidget(m_variableTree);
verticalLayout->addWidget(m_variableDescription);
// connect(m_variableList, &QTreeView::currentChanged,
// this, &VariableChooserPrivate::updateDescription);
connect(m_variableTree, &QTreeView::clicked,
this, &VariableChooserPrivate::updateDescription);
connect(m_variableTree, &QTreeView::activated,
this, &VariableChooserPrivate::handleItemActivated);
connect(qobject_cast<QApplication *>(qApp), &QApplication::focusChanged,
this, &VariableChooserPrivate::updateCurrentEditor);
updateCurrentEditor(0, qApp->focusWidget());
}
} // namespace Internal
using namespace Internal;
......@@ -140,8 +219,7 @@ using namespace Internal;
*
* The variable chooser monitors focus changes of all children of its parent widget.
* When a text control gets focus, the variable chooser checks if it has variable support set,
* either through the addVariableSupport() function or by manually setting the
* custom kVariableSupportProperty on the control. If the control supports variables,
* either through the addVariableSupport() function. If the control supports variables,
* a tool button which opens the variable chooser is shown in it while it has focus.
*
* Supported text controls are QLineEdit, QTextEdit and QPlainTextEdit.
......@@ -159,13 +237,15 @@ using namespace Internal;
*/
/*!
* \internal
* \variable VariableChooser::kVariableSupportProperty
* Property name that is checked for deciding if a widget supports \QC variables.
* Can be manually set with
* \c{textcontrol->setProperty(VariableChooser::kVariableSupportProperty, true)}
* \sa addVariableSupport()
*/
const char VariableChooser::kVariableSupportProperty[] = "QtCreator.VariableSupport";
const char kVariableSupportProperty[] = "QtCreator.VariableSupport";
const char kVariableNameProperty[] = "QtCreator.VariableName";
/*!
* Creates a variable chooser that tracks all children of \a parent for variable support.
......@@ -179,7 +259,8 @@ VariableChooser::VariableChooser(QWidget *parent) :
setWindowTitle(tr("Variables"));
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
setFocusPolicy(Qt::StrongFocus);
setFocusProxy(d->m_variableList);
setFocusProxy(d->m_variableTree);
addMacroExpanderProvider([]() { return globalMacroExpander(); });
}
/*!
......@@ -191,26 +272,30 @@ VariableChooser::~VariableChooser()
delete d;
}
void VariableChooser::addMacroExpanderProvider(const MacroExpanderProvider &provider)
{
auto *item = new VariableGroupItem(d);
item->m_provider = provider;
d->m_model.rootItem()->prependChild(item);
}
/*!
* Marks the control as supporting variables.
* \sa kVariableSupportProperty
*/
void VariableChooser::addVariableSupport(QWidget *textcontrol)
void VariableChooser::addSupportedWidget(QWidget *textcontrol, const QByteArray &ownName)
{
QTC_ASSERT(textcontrol, return);
textcontrol->setProperty(kVariableSupportProperty, true);
textcontrol->setProperty(kVariableSupportProperty, QVariant::fromValue<QWidget *>(this));
textcontrol->setProperty(kVariableNameProperty, ownName);
}
/*!
* \internal
*/
void VariableChooserPrivate::updateDescription(const QString &variable)
void VariableChooserPrivate::updateDescription(const QModelIndex &index)
{
if (variable.isNull())
m_variableDescription->setText(m_defaultDescription);
else
m_variableDescription->setText(globalMacroExpander()->variableDescription(variable.toUtf8())
+ QLatin1String("<p>") + tr("Current Value: %1").arg(globalMacroExpander()->value(variable.toUtf8())));
m_variableDescription->setText(m_model.data(index, Qt::ToolTipRole).toString());
}
/*!
......@@ -236,15 +321,16 @@ void VariableChooserPrivate::updateCurrentEditor(QWidget *old, QWidget *widget)
}
if (!handle)
return;
widget->installEventFilter(this); // for intercepting escape key presses
QLineEdit *previousLineEdit = m_lineEdit;
QWidget *previousWidget = currentWidget();
m_lineEdit = 0;
m_textEdit = 0;
m_plainTextEdit = 0;
QVariant variablesSupportProperty = widget->property(VariableChooser::kVariableSupportProperty);
bool supportsVariables = (variablesSupportProperty.isValid()
? variablesSupportProperty.toBool() : false);
QWidget *chooser = widget->property(kVariableSupportProperty).value<QWidget *>();
m_currentVariableName = widget->property(kVariableNameProperty).value<QByteArray>();
bool supportsVariables = chooser == q;
if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget))
m_lineEdit = (supportsVariables ? lineEdit : 0);
else if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(widget))
......@@ -283,7 +369,7 @@ void VariableChooserPrivate::updateCurrentEditor(QWidget *old, QWidget *widget)
/*!
* \internal
*/
void VariableChooserPrivate::updatePositionAndShow()
void VariableChooserPrivate::updatePositionAndShow(bool)
{