diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp index 329975afdbe..abceff7b2df 100644 --- a/src/libs/utils/detailsbutton.cpp +++ b/src/libs/utils/detailsbutton.cpp @@ -200,3 +200,61 @@ QPixmap DetailsButton::cacheRendering(const QSize &size, bool checked) style()->drawPrimitive(checked ? QStyle::PE_IndicatorArrowUp : QStyle::PE_IndicatorArrowDown, &arrowOpt, &p, this); return pixmap; } + +ExpandButton::ExpandButton(QWidget *parent) : QAbstractButton(parent) +{ + setCheckable(true); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); +} + +QSize ExpandButton::sizeHint() const +{ + return {fontMetrics().horizontalAdvance(text()) + 26, HostOsInfo::isMacHost() ? 34 : 22}; +} + +void ExpandButton::paintEvent(QPaintEvent *e) +{ + QWidget::paintEvent(e); + QPainter p(this); + + QPixmap &pixmap = isChecked() ? m_checkedPixmap : m_uncheckedPixmap; + if (pixmap.isNull() || pixmap.size() / pixmap.devicePixelRatio() != contentsRect().size()) + pixmap = cacheRendering(); + p.drawPixmap(contentsRect(), pixmap); + + if (isDown()) { + p.setPen(Qt::NoPen); + p.setBrush(QColor(0, 0, 0, 20)); + p.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 1, 1); + } + if (hasFocus()) { + QStyleOptionFocusRect option; + option.initFrom(this); + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &p, this); + } +} + +QPixmap ExpandButton::cacheRendering() +{ + const QSize size = contentsRect().size(); + const qreal pixelRatio = devicePixelRatio(); + QPixmap pixmap(size * pixelRatio); + pixmap.setDevicePixelRatio(pixelRatio); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + p.setRenderHint(QPainter::Antialiasing, true); + p.translate(0.5, 0.5); + p.setPen(Qt::NoPen); + p.drawRoundedRect(0, 0, size.width(), size.height(), 1, 1); + int arrowsize = 15; + QStyleOption arrowOpt; + arrowOpt.initFrom(this); + QPalette pal = arrowOpt.palette; + pal.setBrush(QPalette::All, QPalette::Text, QColor(0, 0, 0)); + arrowOpt.rect = QRect(size.width() - arrowsize - 6, height() / 2 - arrowsize / 2, + arrowsize, arrowsize); + arrowOpt.palette = pal; + style()->drawPrimitive(isChecked() ? QStyle::PE_IndicatorArrowUp + : QStyle::PE_IndicatorArrowDown, &arrowOpt, &p, this); + return pixmap; +} diff --git a/src/libs/utils/detailsbutton.h b/src/libs/utils/detailsbutton.h index f66fa2ad4b6..dabbe75918d 100644 --- a/src/libs/utils/detailsbutton.h +++ b/src/libs/utils/detailsbutton.h @@ -78,4 +78,22 @@ private: QPixmap m_uncheckedPixmap; float m_fader; }; + +class QTCREATOR_UTILS_EXPORT ExpandButton : public QAbstractButton +{ + Q_OBJECT + +public: + ExpandButton(QWidget *parent = nullptr); + +private: + void paintEvent(QPaintEvent *e) override; + QSize sizeHint() const override; + + QPixmap cacheRendering(); + + QPixmap m_checkedPixmap; + QPixmap m_uncheckedPixmap; +}; + } // namespace Utils diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 38b080c370b..1b41c3246fa 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -32,15 +32,17 @@ #include "runconfiguration.h" #include "target.h" -#include +#include #include #include #include +#include #include #include #include #include +#include #include using namespace Utils; @@ -286,6 +288,8 @@ void ArgumentsAspect::setArguments(const QString &arguments) } if (m_chooser && m_chooser->text() != arguments) m_chooser->setText(arguments); + if (m_multiLineChooser && m_multiLineChooser->toPlainText() != arguments) + m_multiLineChooser->setPlainText(arguments); } void ArgumentsAspect::fromMap(const QVariantMap &map) @@ -297,25 +301,77 @@ void ArgumentsAspect::fromMap(const QVariantMap &map) else m_arguments = args.toString(); - if (m_chooser) + m_multiLine = map.value(settingsKey() + ".multi", false).toBool(); + + if (m_multiLineButton) + m_multiLineButton->setChecked(m_multiLine); + if (!m_multiLine && m_chooser) m_chooser->setText(m_arguments); + if (m_multiLine && m_multiLineChooser) + m_multiLineChooser->setPlainText(m_arguments); } void ArgumentsAspect::toMap(QVariantMap &map) const { map.insert(settingsKey(), m_arguments); + map.insert(settingsKey() + ".multi", m_multiLine); +} + +QWidget *ArgumentsAspect::setupChooser() +{ + if (m_multiLine) { + if (!m_multiLineChooser) { + m_multiLineChooser = new QPlainTextEdit; + connect(m_multiLineChooser.data(), &QPlainTextEdit::textChanged, + this, [this] { setArguments(m_multiLineChooser->toPlainText()); }); + } + m_multiLineChooser->setPlainText(m_arguments); + return m_multiLineChooser.data(); + } + if (!m_chooser) { + m_chooser = new FancyLineEdit; + m_chooser->setHistoryCompleter(settingsKey()); + connect(m_chooser.data(), &QLineEdit::textChanged, this, &ArgumentsAspect::setArguments); + } + m_chooser->setText(m_arguments); + return m_chooser.data(); } void ArgumentsAspect::addToConfigurationLayout(QFormLayout *layout) { - QTC_CHECK(!m_chooser); - m_chooser = new FancyLineEdit(layout->parentWidget()); - m_chooser->setHistoryCompleter(settingsKey()); - m_chooser->setText(m_arguments); + QTC_CHECK(!m_chooser && !m_multiLineChooser && !m_multiLineButton); - connect(m_chooser.data(), &QLineEdit::textChanged, this, &ArgumentsAspect::setArguments); - - layout->addRow(tr("Command line arguments:"), m_chooser); + const auto container = new QWidget; + const auto containerLayout = new QHBoxLayout(container); + containerLayout->setContentsMargins(0, 0, 0, 0); + containerLayout->addWidget(setupChooser()); + m_multiLineButton = new ExpandButton; + m_multiLineButton->setToolTip(tr("Toggle multi-line mode")); + m_multiLineButton->setChecked(m_multiLine); + connect(m_multiLineButton, &QCheckBox::clicked, this, [this](bool checked) { + if (m_multiLine == checked) + return; + m_multiLine = checked; + setupChooser(); + QWidget *oldWidget = nullptr; + QWidget *newWidget = nullptr; + if (m_multiLine) { + oldWidget = m_chooser.data(); + newWidget = m_multiLineChooser.data(); + } else { + oldWidget = m_multiLineChooser.data(); + newWidget = m_chooser.data(); + } + QTC_ASSERT(!oldWidget == !newWidget, return); + if (oldWidget) { + QTC_ASSERT(oldWidget->parentWidget()->layout(), return); + oldWidget->parentWidget()->layout()->replaceWidget(oldWidget, newWidget); + delete oldWidget; + } + }); + containerLayout->addWidget(m_multiLineButton); + containerLayout->setAlignment(m_multiLineButton, Qt::AlignTop); + layout->addRow(tr("Command line arguments:"), container); } /*! diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index 78a4e5b965f..2792054176c 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -31,9 +31,12 @@ QT_BEGIN_NAMESPACE class QCheckBox; +class QPlainTextEdit; class QToolButton; QT_END_NAMESPACE +namespace Utils { class ExpandButton; } + namespace ProjectExplorer { class PROJECTEXPLORER_EXPORT TerminalAspect : public ProjectConfigurationAspect @@ -113,8 +116,13 @@ private: void fromMap(const QVariantMap &map) override; void toMap(QVariantMap &map) const override; + QWidget *setupChooser(); + QString m_arguments; QPointer m_chooser; + QPointer m_multiLineChooser; + QPointer m_multiLineButton; + bool m_multiLine = false; mutable bool m_currentlyExpanding = false; };