forked from qt-creator/qt-creator
Core: Allow numeric options in OptionsPopup
For numeric options a spinbox is added to the popup. The text shown before and behind the spinbox is controlled via the action's label: "Show {} preceding lines" A label with the text "Show " is created before the spinbox, and another label with the text " preceding lines" is created behind. Task-number: QTCREATORBUG-30167 Change-Id: Id0fba97c3a674ae41d4ab9d767694f5f5674ef18 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -5,34 +5,111 @@
|
||||
|
||||
#include "../actionmanager/actionmanager.h"
|
||||
|
||||
#include <utils/proxyaction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/stylehelper.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QCheckBox>
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <QLabel>
|
||||
#include <QScreen>
|
||||
#include <QSpinBox>
|
||||
#include <QStyleOption>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
const char kNumericOptionProperty[] = "qtc_numericOption";
|
||||
|
||||
namespace Core {
|
||||
|
||||
static QCheckBox *createCheckboxForCommand(QObject *owner, Id id)
|
||||
static QCheckBox *createCheckboxForAction(QObject *owner, QAction *action)
|
||||
{
|
||||
QAction *action = ActionManager::command(id)->action();
|
||||
QCheckBox *checkbox = new QCheckBox(action->text());
|
||||
checkbox->setToolTip(action->toolTip());
|
||||
checkbox->setChecked(action->isChecked());
|
||||
checkbox->setEnabled(action->isEnabled());
|
||||
checkbox->installEventFilter(owner); // enter key handling
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, action, &QAction::setChecked);
|
||||
QObject::connect(action, &QAction::changed, checkbox, [action, checkbox] {
|
||||
checkbox->setEnabled(action->isEnabled());
|
||||
});
|
||||
QObject::connect(action, &QAction::enabledChanged, checkbox, &QCheckBox::setEnabled);
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
static QWidget *createSpinboxForAction(QObject *owner, QAction *action)
|
||||
{
|
||||
const std::optional<NumericOption> option = NumericOption::get(action);
|
||||
QTC_ASSERT(option, return nullptr);
|
||||
const auto proxyAction = qobject_cast<ProxyAction *>(action);
|
||||
QTC_ASSERT(proxyAction, return nullptr);
|
||||
|
||||
const auto prefix = action->text().section("{}", 0, 0);
|
||||
const auto suffix = action->text().section("{}", 1);
|
||||
|
||||
const auto widget = new QWidget{};
|
||||
widget->setEnabled(action->isEnabled());
|
||||
|
||||
auto styleOption = QStyleOptionButton{};
|
||||
const QRect box = widget->style()->subElementRect(QStyle::SE_CheckBoxContents, &styleOption);
|
||||
|
||||
const auto spinbox = new QSpinBox{widget};
|
||||
spinbox->installEventFilter(owner); // enter key handling
|
||||
spinbox->setMinimum(option->minimumValue);
|
||||
spinbox->setMaximum(option->maximumValue);
|
||||
spinbox->setValue(option->currentValue);
|
||||
|
||||
QObject::connect(spinbox, &QSpinBox::valueChanged, action, [action](int value) {
|
||||
std::optional<NumericOption> option = NumericOption::get(action);
|
||||
QTC_ASSERT(option, return);
|
||||
option->currentValue = value;
|
||||
NumericOption::set(action, *option);
|
||||
emit action->changed();
|
||||
});
|
||||
const auto updateCurrentAction = [proxyAction] {
|
||||
if (!proxyAction->action())
|
||||
return;
|
||||
const std::optional<NumericOption> option = NumericOption::get(proxyAction);
|
||||
QTC_ASSERT(option, return);
|
||||
NumericOption::set(proxyAction->action(), *option);
|
||||
emit proxyAction->action()->changed();
|
||||
};
|
||||
QObject::connect(proxyAction, &ProxyAction::currentActionChanged, updateCurrentAction);
|
||||
QObject::connect(proxyAction, &QAction::changed, updateCurrentAction);
|
||||
QObject::connect(action, &QAction::enabledChanged, widget, &QWidget::setEnabled);
|
||||
|
||||
const auto layout = new QHBoxLayout{widget};
|
||||
layout->setContentsMargins(box.left(), 0, 0, 0);
|
||||
layout->setSpacing(0); // the label will have spaces before and after the placeholder
|
||||
|
||||
if (!prefix.isEmpty()) {
|
||||
const auto prefixLabel = new QLabel{prefix, widget};
|
||||
const auto prefixSpan = suffix.isEmpty() ? 1 : 0;
|
||||
layout->addWidget(prefixLabel, prefixSpan);
|
||||
prefixLabel->setBuddy(spinbox);
|
||||
}
|
||||
|
||||
layout->addWidget(spinbox);
|
||||
|
||||
if (!suffix.isEmpty()) {
|
||||
const auto suffixLabel = new QLabel{suffix, widget};
|
||||
layout->addWidget(suffixLabel, 1);
|
||||
suffixLabel->setBuddy(spinbox);
|
||||
}
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
static QWidget *createWidgetForCommand(QObject *owner, Id id)
|
||||
{
|
||||
const auto action = ActionManager::command(id)->action();
|
||||
|
||||
if (NumericOption::get(action))
|
||||
return createSpinboxForAction(owner, action);
|
||||
else
|
||||
return createCheckboxForAction(owner, action);
|
||||
}
|
||||
|
||||
/*!
|
||||
\class Core::OptionsPopup
|
||||
\inmodule QtCreator
|
||||
@@ -50,12 +127,12 @@ OptionsPopup::OptionsPopup(QWidget *parent, const QVector<Id> &commands)
|
||||
|
||||
bool first = true;
|
||||
for (const Id &command : commands) {
|
||||
QCheckBox *checkBox = createCheckboxForCommand(this, command);
|
||||
const auto widget = createWidgetForCommand(this, command);
|
||||
if (first) {
|
||||
checkBox->setFocus();
|
||||
widget->setFocus();
|
||||
first = false;
|
||||
}
|
||||
layout->addWidget(checkBox);
|
||||
layout->addWidget(widget);
|
||||
}
|
||||
const QPoint globalPos = parent->mapToGlobal(QPoint(0, -sizeHint().height()));
|
||||
const QRect screenGeometry = parent->screen()->availableGeometry();
|
||||
@@ -88,4 +165,19 @@ bool OptionsPopup::eventFilter(QObject *obj, QEvent *ev)
|
||||
return QWidget::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
std::optional<NumericOption> NumericOption::get(QObject *o)
|
||||
{
|
||||
const QVariant opt = o->property(kNumericOptionProperty);
|
||||
if (opt.isValid()) {
|
||||
QTC_ASSERT(opt.canConvert<NumericOption>(), return {});
|
||||
return opt.value<NumericOption>();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void NumericOption::set(QObject *o, const NumericOption &opt)
|
||||
{
|
||||
o->setProperty(kNumericOptionProperty, QVariant::fromValue(opt));
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
@@ -9,8 +9,28 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class CORE_EXPORT NumericOption
|
||||
{
|
||||
public:
|
||||
NumericOption() noexcept = default;
|
||||
NumericOption(int current, int minimum, int maximum) noexcept
|
||||
: currentValue{current}
|
||||
, minimumValue{minimum}
|
||||
, maximumValue{maximum}
|
||||
{}
|
||||
|
||||
static std::optional<NumericOption> get(QObject *o);
|
||||
static void set(QObject *o, const NumericOption &opt);
|
||||
|
||||
int currentValue;
|
||||
int minimumValue;
|
||||
int maximumValue;
|
||||
};
|
||||
|
||||
class CORE_EXPORT OptionsPopup : public QWidget
|
||||
{
|
||||
public:
|
||||
|
Reference in New Issue
Block a user