summaryrefslogtreecommitdiff
path: root/src/libs/utils/aspects.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils/aspects.cpp')
-rw-r--r--src/libs/utils/aspects.cpp1112
1 files changed, 951 insertions, 161 deletions
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp
index ef0f7b1866..3207497f4a 100644
--- a/src/libs/utils/aspects.cpp
+++ b/src/libs/utils/aspects.cpp
@@ -31,18 +31,24 @@
#include "pathchooser.h"
#include "qtcassert.h"
#include "qtcprocess.h"
+#include "qtcsettings.h"
#include "utilsicons.h"
#include "variablechooser.h"
+#include <QAction>
#include <QButtonGroup>
#include <QCheckBox>
#include <QComboBox>
+#include <QDebug>
#include <QFormLayout>
+#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPointer>
+#include <QPushButton>
#include <QRadioButton>
+#include <QSettings>
#include <QSpinBox>
#include <QTextEdit>
#include <QToolButton>
@@ -56,17 +62,24 @@ public:
Utils::Id m_id;
QVariant m_value;
QVariant m_defaultValue;
+ std::function<QVariant(const QVariant &)> m_toSettings;
+ std::function<QVariant(const QVariant &)> m_fromSettings;
QString m_displayName;
QString m_settingsKey; // Name of data in settings.
QString m_tooltip;
QString m_labelText;
QPixmap m_labelPixmap;
+ QIcon m_icon;
QPointer<QLabel> m_label; // Owned by configuration widget
+ QPointer<QAction> m_action; // Owned by us.
bool m_visible = true;
bool m_enabled = true;
bool m_readOnly = true;
+ bool m_autoApply = true;
+ int m_spanX = 1;
+ int m_spanY = 1;
BaseAspect::ConfigWidgetCreator m_configWidgetCreator;
QList<QPointer<QWidget>> m_subWidgets;
};
@@ -102,7 +115,10 @@ BaseAspect::BaseAspect()
/*!
Destructs a BaseAspect.
*/
-BaseAspect::~BaseAspect() = default;
+BaseAspect::~BaseAspect()
+{
+ delete d->m_action;
+}
Id BaseAspect::id() const
{
@@ -126,8 +142,10 @@ QVariant BaseAspect::value() const
*/
void BaseAspect::setValue(const QVariant &value)
{
- if (setValueQuietly(value))
+ if (setValueQuietly(value)) {
emit changed();
+ emitChangedValue();
+ }
}
/*!
@@ -149,13 +167,18 @@ QVariant BaseAspect::defaultValue() const
}
/*!
- Sets a default value for this aspect.
+ Sets a default value and the current value for this aspect.
+
+ \note The current value will be set silently to the same value.
+ It is reasonable to only set default values in the setup phase
+ of the aspect.
Default values will not be stored in settings.
*/
void BaseAspect::setDefaultValue(const QVariant &value)
{
d->m_defaultValue = value;
+ d->m_value = value;
}
void BaseAspect::setDisplayName(const QString &displayName)
@@ -178,13 +201,19 @@ void BaseAspect::setVisible(bool visible)
d->m_visible = visible;
for (QWidget *w : qAsConst(d->m_subWidgets)) {
QTC_ASSERT(w, continue);
- w->setVisible(visible);
+ // This may happen during layout building. Explicit setting visibility here
+ // may create a show a toplevel widget for a moment until it is parented
+ // to some non-shown widget.
+ if (visible && w->parentWidget())
+ w->setVisible(visible);
}
}
void BaseAspect::setupLabel()
{
QTC_ASSERT(!d->m_label, delete d->m_label);
+ if (d->m_labelText.isEmpty() && d->m_labelPixmap.isNull())
+ return;
d->m_label = new QLabel(d->m_labelText);
d->m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!d->m_labelPixmap.isNull())
@@ -192,6 +221,20 @@ void BaseAspect::setupLabel()
registerSubWidget(d->m_label);
}
+void BaseAspect::addLabeledItem(LayoutBuilder &builder, QWidget *widget)
+{
+ setupLabel();
+ if (QLabel *l = label()) {
+ l->setBuddy(widget);
+ builder.addItem(l);
+ LayoutBuilder::LayoutItem item(widget);
+ item.span = std::max(d->m_spanX - 1, 1);
+ builder.addItem(item);
+ } else {
+ builder.addItem(LayoutBuilder::LayoutItem(widget));
+ }
+}
+
/*!
Sets \a labelText as text for the separate label in the visual
representation of this aspect.
@@ -214,6 +257,13 @@ void BaseAspect::setLabelPixmap(const QPixmap &labelPixmap)
d->m_label->setPixmap(labelPixmap);
}
+void BaseAspect::setIcon(const QIcon &icon)
+{
+ d->m_icon = icon;
+ if (d->m_action)
+ d->m_action->setIcon(icon);
+}
+
/*!
Returns the current text for the separate label in the visual
representation of this aspect.
@@ -245,6 +295,11 @@ void BaseAspect::setToolTip(const QString &tooltip)
}
}
+bool BaseAspect::isEnabled() const
+{
+ return d->m_enabled;
+}
+
void BaseAspect::setEnabled(bool enabled)
{
d->m_enabled = enabled;
@@ -254,6 +309,22 @@ void BaseAspect::setEnabled(bool enabled)
}
}
+/*!
+ Makes the enabled state of this aspect depend on the checked state of \a checker.
+*/
+void BaseAspect::setEnabler(BoolAspect *checker)
+{
+ QTC_ASSERT(checker, return);
+ setEnabled(checker->value());
+ connect(checker, &BoolAspect::volatileValueChanged, this, &BaseAspect::setEnabled);
+ connect(checker, &BoolAspect::valueChanged, this, &BaseAspect::setEnabled);
+}
+
+bool BaseAspect::isReadOnly() const
+{
+ return d->m_readOnly;
+}
+
void BaseAspect::setReadOnly(bool readOnly)
{
d->m_readOnly = readOnly;
@@ -266,6 +337,30 @@ void BaseAspect::setReadOnly(bool readOnly)
}
}
+void BaseAspect::setSpan(int x, int y)
+{
+ d->m_spanX = x;
+ d->m_spanY = y;
+}
+
+bool BaseAspect::isAutoApply() const
+{
+ return d->m_autoApply;
+}
+
+/*!
+ Sets auto-apply mode. When auto-apply mode is on, user interaction to this
+ aspect's widget will not modify the \c value of the aspect until \c apply()
+ is called programmatically.
+
+ \sa setSettingsKey()
+*/
+
+void BaseAspect::setAutoApply(bool on)
+{
+ d->m_autoApply = on;
+}
+
/*!
\internal
*/
@@ -304,7 +399,17 @@ void BaseAspect::setSettingsKey(const QString &group, const QString &key)
d->m_settingsKey = group + "/" + key;
}
-QString BaseAspect::displayName() const { return d->m_displayName; }
+/*!
+ Returns the string that should be used when this action appears in menus
+ or other places that are typically used with Book style capitalization.
+
+ If no display name is set, the label text will be used as fallback.
+*/
+
+QString BaseAspect::displayName() const
+{
+ return d->m_displayName.isEmpty() ? d->m_labelText : d->m_displayName;
+}
/*!
\internal
@@ -314,6 +419,15 @@ QWidget *BaseAspect::createConfigWidget() const
return d->m_configWidgetCreator ? d->m_configWidgetCreator() : nullptr;
}
+QAction *BaseAspect::action()
+{
+ if (!d->m_action) {
+ d->m_action = new QAction(labelText());
+ d->m_action->setIcon(d->m_icon);
+ }
+ return d->m_action;
+}
+
/*!
Adds the visual representation of this aspect to a layout using
a layout builder.
@@ -322,10 +436,72 @@ void BaseAspect::addToLayout(LayoutBuilder &)
{
}
+/*!
+ Updates this aspect's value from user-initiated changes in the widget.
+
+ This has only an effect if \c isAutoApply is false.
+*/
+void BaseAspect::apply()
+{
+ QTC_CHECK(!d->m_autoApply);
+ if (isDirty())
+ setValue(volatileValue());
+}
+
+/*!
+ Discard user changes in the widget and restore widget contents from
+ aspect's value.
+
+ This has only an effect if \c isAutoApply is false.
+*/
+void BaseAspect::cancel()
+{
+ QTC_CHECK(!d->m_autoApply);
+ if (!d->m_subWidgets.isEmpty())
+ setVolatileValue(d->m_value);
+}
+
+void BaseAspect::finish()
+{
+ // No qDeleteAll() possible as long as the connect in registerSubWidget() exist.
+ while (d->m_subWidgets.size())
+ delete d->m_subWidgets.takeLast();
+}
+
+bool BaseAspect::hasAction() const
+{
+ return d->m_action != nullptr;
+}
+
+bool BaseAspect::isDirty() const
+{
+ QTC_CHECK(!isAutoApply());
+ // Aspects that were never shown cannot contain unsaved user changes.
+ if (d->m_subWidgets.isEmpty())
+ return false;
+ return volatileValue() != d->m_value;
+}
+
+QVariant BaseAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ return {};
+}
+
+void BaseAspect::setVolatileValue(const QVariant &val)
+{
+ Q_UNUSED(val);
+}
+
void BaseAspect::registerSubWidget(QWidget *widget)
{
d->m_subWidgets.append(widget);
+ // FIXME: This interferes with qDeleteAll() in finish() and destructor,
+ // it would not be needed when all users actually deleted their subwidgets,
+ // e.g. the SettingsPage::finish() base implementation, but this still
+ // leaves the cases where no such base functionality is available, e.g.
+ // in the run/build config aspects.
connect(widget, &QObject::destroyed, this, [this, widget] {
d->m_subWidgets.removeAll(widget);
});
@@ -340,11 +516,10 @@ void BaseAspect::registerSubWidget(QWidget *widget)
}
void BaseAspect::saveToMap(QVariantMap &data, const QVariant &value,
- const QVariant &defaultValue, const QString &keyExtension) const
+ const QVariant &defaultValue, const QString &key)
{
- if (settingsKey().isEmpty())
+ if (key.isEmpty())
return;
- const QString key = settingsKey() + keyExtension;
if (value == defaultValue)
data.remove(key);
else
@@ -356,7 +531,8 @@ void BaseAspect::saveToMap(QVariantMap &data, const QVariant &value,
*/
void BaseAspect::fromMap(const QVariantMap &map)
{
- setValue(map.value(settingsKey(), defaultValue()));
+ const QVariant val = map.value(settingsKey(), toSettingsValue(defaultValue()));
+ setValue(fromSettingsValue(val));
}
/*!
@@ -364,66 +540,52 @@ void BaseAspect::fromMap(const QVariantMap &map)
*/
void BaseAspect::toMap(QVariantMap &map) const
{
- saveToMap(map, d->m_value, d->m_defaultValue);
+ saveToMap(map, toSettingsValue(d->m_value), toSettingsValue(d->m_defaultValue), settingsKey());
}
-/*!
- \internal
-*/
-void BaseAspect::acquaintSiblings(const BaseAspects &)
-{}
-
-// BaseAspects
-
-/*!
- \class BaseAspects
- \inmodule QtCreator
-
- \brief This class represent a collection of one or more aspects.
-
- A BaseAspects object assumes ownership on its aspects.
-*/
+void BaseAspect::readSettings(const QSettings *settings)
+{
+ if (settingsKey().isEmpty())
+ return;
+ const QVariant &val = settings->value(settingsKey());
+ setValue(val.isValid() ? fromSettingsValue(val) : defaultValue());
+}
-/*!
- Constructs a BaseAspects object.
-*/
-BaseAspects::BaseAspects() = default;
+void BaseAspect::writeSettings(QSettings *settings) const
+{
+ if (settingsKey().isEmpty())
+ return;
+ QtcSettings::setValueWithDefault(settings,
+ settingsKey(),
+ toSettingsValue(value()),
+ toSettingsValue(defaultValue()));
+}
-/*!
- Destructs a BaseAspects object.
-*/
-BaseAspects::~BaseAspects()
+void BaseAspect::setFromSettingsTransformation(const SavedValueTransformation &transform)
{
- qDeleteAll(m_aspects);
+ d->m_fromSettings = transform;
}
-/*!
- Retrieves a BaseAspect with a given \a id, or nullptr if no such aspect is contained.
+void BaseAspect::setToSettingsTransformation(const SavedValueTransformation &transform)
+{
+ d->m_toSettings = transform;
+}
- \sa BaseAspect.
-*/
-BaseAspect *BaseAspects::aspect(Utils::Id id) const
+QVariant BaseAspect::toSettingsValue(const QVariant &val) const
{
- return Utils::findOrDefault(m_aspects, Utils::equal(&BaseAspect::id, id));
+ return d->m_toSettings ? d->m_toSettings(val) : val;
}
-/*!
- \internal
-*/
-void BaseAspects::fromMap(const QVariantMap &map) const
+QVariant BaseAspect::fromSettingsValue(const QVariant &val) const
{
- for (BaseAspect *aspect : m_aspects)
- aspect->fromMap(map);
+ return d->m_fromSettings ? d->m_fromSettings(val) : val;
}
/*!
\internal
*/
-void BaseAspects::toMap(QVariantMap &map) const
-{
- for (BaseAspect *aspect : m_aspects)
- aspect->toMap(map);
-}
+void BaseAspect::acquaintSiblings(const AspectContainer &)
+{}
namespace Internal {
@@ -432,15 +594,17 @@ class BoolAspectPrivate
public:
BoolAspect::LabelPlacement m_labelPlacement = BoolAspect::LabelPlacement::AtCheckBox;
QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
+ QPointer<QGroupBox> m_groupBox; // For BoolAspects handling GroupBox check boxes
};
class SelectionAspectPrivate
{
public:
+ ~SelectionAspectPrivate() { delete m_buttonGroup; }
+
SelectionAspect::DisplayStyle m_displayStyle
= SelectionAspect::DisplayStyle::RadioButtons;
- struct Option { QString displayName; QString tooltip; };
- QVector<Option> m_options;
+ QVector<SelectionAspect::Option> m_options;
// These are all owned by the configuration widget.
QList<QPointer<QRadioButton>> m_buttons;
@@ -490,40 +654,50 @@ public:
std::function<void()> m_openTerminal;
bool m_undoRedoEnabled = true;
+ bool m_acceptRichText = false;
bool m_showToolTipOnLabel = false;
bool m_fileDialogOnly = false;
+ bool m_useResetButton = false;
- template<class Widget> void updateWidgetFromCheckStatus(Widget *w)
+ template<class Widget> void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w)
{
const bool enabled = !m_checker || m_checker->value();
if (m_uncheckedSemantics == StringAspect::UncheckedSemantics::Disabled)
- w->setEnabled(enabled);
+ w->setEnabled(enabled && aspect->isEnabled());
else
- w->setReadOnly(!enabled);
+ w->setReadOnly(!enabled || aspect->isReadOnly());
}
};
class IntegerAspectPrivate
{
public:
- QVariant m_minimumValue;
- QVariant m_maximumValue;
+ Utils::optional<qint64> m_minimumValue;
+ Utils::optional<qint64> m_maximumValue;
int m_displayIntegerBase = 10;
qint64 m_displayScaleFactor = 1;
QString m_prefix;
QString m_suffix;
+ QString m_specialValueText;
+ int m_singleStep = 1;
QPointer<QSpinBox> m_spinBox; // Owned by configuration widget
};
-class StringListAspectPrivate
+class DoubleAspectPrivate
{
public:
+ Utils::optional<double> m_minimumValue;
+ Utils::optional<double> m_maximumValue;
+ QString m_prefix;
+ QString m_suffix;
+ QString m_specialValueText;
+ double m_singleStep = 1;
+ QPointer<QDoubleSpinBox> m_spinBox; // Owned by configuration widget
};
-class AspectContainerPrivate
+class StringListAspectPrivate
{
public:
- QList<BaseAspect *> m_items;
};
class TextDisplayPrivate
@@ -586,7 +760,10 @@ public:
StringAspect::StringAspect()
: d(new Internal::StringAspectPrivate)
-{}
+{
+ setDefaultValue(QString());
+ setSpan(2, 1); // Default: Label + something
+}
/*!
\internal
@@ -631,9 +808,15 @@ void StringAspect::setValue(const QString &val)
if (BaseAspect::setValueQuietly(QVariant(processedValue))) {
update();
emit changed();
+ emit valueChanged(processedValue);
}
}
+void StringAspect::setDefaultValue(const QString &val)
+{
+ BaseAspect::setDefaultValue(val);
+}
+
/*!
\reimp
*/
@@ -650,7 +833,7 @@ void StringAspect::fromMap(const QVariantMap &map)
*/
void StringAspect::toMap(QVariantMap &map) const
{
- saveToMap(map, value(), QString());
+ saveToMap(map, value(), defaultValue(), settingsKey());
if (d->m_checker)
d->m_checker->toMap(map);
}
@@ -679,6 +862,11 @@ void StringAspect::setFilePath(const FilePath &value)
setValue(value.toUserOutput());
}
+PathChooser *StringAspect::pathChooser() const
+{
+ return d->m_pathChooserDisplay.data();
+}
+
/*!
\internal
*/
@@ -798,11 +986,28 @@ void StringAspect::setUndoRedoEnabled(bool undoRedoEnabled)
d->m_textEditDisplay->setUndoRedoEnabled(undoRedoEnabled);
}
+void StringAspect::setAcceptRichText(bool acceptRichText)
+{
+ d->m_acceptRichText = acceptRichText;
+ if (d->m_textEditDisplay)
+ d->m_textEditDisplay->setAcceptRichText(acceptRichText);
+}
+
void StringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
{
d->m_expanderProvider = expanderProvider;
}
+void StringAspect::setUseGlobalMacroExpander()
+{
+ d->m_expanderProvider = &globalMacroExpander;
+}
+
+void StringAspect::setUseResetButton()
+{
+ d->m_useResetButton = true;
+}
+
void StringAspect::setValidationFunction(const FancyLineEdit::ValidationFunction &validator)
{
d->m_validator = validator;
@@ -837,17 +1042,16 @@ void StringAspect::addToLayout(LayoutBuilder &builder)
builder.finishRow();
}
- setupLabel();
- builder.addItem(label());
-
- const auto useMacroExpander = [this, &builder](QWidget *w) {
+ const auto useMacroExpander = [this](QWidget *w) {
if (!d->m_expanderProvider)
return;
- const auto chooser = new VariableChooser(builder.layout()->parentWidget());
+ const auto chooser = new VariableChooser(w);
chooser->addSupportedWidget(w);
chooser->addMacroExpanderProvider(d->m_expanderProvider);
};
+ const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
+
switch (d->m_displayStyle) {
case PathChooserDisplay:
d->m_pathChooserDisplay = createSubWidget<PathChooser>();
@@ -856,12 +1060,16 @@ void StringAspect::addToLayout(LayoutBuilder &builder)
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
d->m_pathChooserDisplay->setEnvironment(d->m_environment);
d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName);
- useMacroExpander(d->m_pathChooserDisplay->lineEdit());
- connect(d->m_pathChooserDisplay, &PathChooser::pathChanged,
- this, &StringAspect::setValue);
- builder.addItem(d->m_pathChooserDisplay.data());
d->m_pathChooserDisplay->setFileDialogOnly(d->m_fileDialogOnly);
d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal);
+ d->m_pathChooserDisplay->setFilePath(FilePath::fromString(displayedString));
+ d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
+ addLabeledItem(builder, d->m_pathChooserDisplay);
+ useMacroExpander(d->m_pathChooserDisplay->lineEdit());
+ if (isAutoApply()) {
+ connect(d->m_pathChooserDisplay, &PathChooser::pathChanged, this, [this] {
+ setValue(d->m_pathChooserDisplay->path()); });
+ }
break;
case LineEditDisplay:
d->m_lineEditDisplay = createSubWidget<FancyLineEdit>();
@@ -870,28 +1078,48 @@ void StringAspect::addToLayout(LayoutBuilder &builder)
d->m_lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey);
if (d->m_validator)
d->m_lineEditDisplay->setValidationFunction(d->m_validator);
+ d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
+ d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data());
+ addLabeledItem(builder, d->m_lineEditDisplay);
useMacroExpander(d->m_lineEditDisplay);
- connect(d->m_lineEditDisplay, &FancyLineEdit::textEdited,
- this, &StringAspect::setValue);
- builder.addItem(d->m_lineEditDisplay.data());
+ if (isAutoApply()) {
+ connect(d->m_lineEditDisplay, &FancyLineEdit::textEdited,
+ this, &StringAspect::setValue);
+ }
+ if (d->m_useResetButton) {
+ auto resetButton = createSubWidget<QPushButton>(tr("Reset"));
+ resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue());
+ connect(resetButton, &QPushButton::clicked, this, [this] {
+ d->m_lineEditDisplay->setText(defaultValue().toString());
+ });
+ connect(d->m_lineEditDisplay, &QLineEdit::textChanged, this, [this, resetButton] {
+ resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue());
+ });
+ builder.addItem(resetButton);
+ }
break;
case TextEditDisplay:
d->m_textEditDisplay = createSubWidget<QTextEdit>();
d->m_textEditDisplay->setPlaceholderText(d->m_placeHolderText);
d->m_textEditDisplay->setUndoRedoEnabled(d->m_undoRedoEnabled);
- d->m_textEditDisplay->setAcceptRichText(false);
+ d->m_textEditDisplay->setAcceptRichText(d->m_acceptRichText);
d->m_textEditDisplay->setTextInteractionFlags(Qt::TextEditorInteraction);
+ d->m_textEditDisplay->setText(displayedString);
+ d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data());
+ addLabeledItem(builder, d->m_textEditDisplay);
useMacroExpander(d->m_textEditDisplay);
- connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] {
- const QString value = d->m_textEditDisplay->document()->toPlainText();
- setValue(value);
- });
- builder.addItem(d->m_textEditDisplay.data());
+ if (isAutoApply()) {
+ connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] {
+ setValue(d->m_textEditDisplay->document()->toPlainText());
+ });
+ }
break;
case LabelDisplay:
d->m_labelDisplay = createSubWidget<QLabel>();
d->m_labelDisplay->setTextInteractionFlags(Qt::TextSelectableByMouse);
- builder.addItem(d->m_labelDisplay.data());
+ d->m_labelDisplay->setText(displayedString);
+ d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip());
+ addLabeledItem(builder, d->m_labelDisplay);
break;
}
@@ -899,8 +1127,50 @@ void StringAspect::addToLayout(LayoutBuilder &builder)
if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Right)
d->m_checker->addToLayout(builder);
+}
- update();
+QVariant StringAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ switch (d->m_displayStyle) {
+ case PathChooserDisplay:
+ QTC_ASSERT(d->m_pathChooserDisplay, return {});
+ return d->m_pathChooserDisplay->path();
+ case LineEditDisplay:
+ QTC_ASSERT(d->m_lineEditDisplay, return {});
+ return d->m_lineEditDisplay->text();
+ case TextEditDisplay:
+ QTC_ASSERT(d->m_textEditDisplay, return {});
+ return d->m_textEditDisplay->document()->toPlainText();
+ case LabelDisplay:
+ break;
+ }
+ return {};
+}
+
+void StringAspect::setVolatileValue(const QVariant &val)
+{
+ switch (d->m_displayStyle) {
+ case PathChooserDisplay:
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setPath(val.toString());
+ break;
+ case LineEditDisplay:
+ if (d->m_lineEditDisplay)
+ d->m_lineEditDisplay->setText(val.toString());
+ break;
+ case TextEditDisplay:
+ if (d->m_textEditDisplay)
+ d->m_textEditDisplay->document()->setPlainText(val.toString());
+ break;
+ case LabelDisplay:
+ break;
+ }
+}
+
+void StringAspect::emitChangedValue()
+{
+ emit valueChanged(value());
}
void StringAspect::update()
@@ -909,19 +1179,19 @@ void StringAspect::update()
if (d->m_pathChooserDisplay) {
d->m_pathChooserDisplay->setFilePath(FilePath::fromString(displayedString));
- d->updateWidgetFromCheckStatus(d->m_pathChooserDisplay.data());
+ d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
}
if (d->m_lineEditDisplay) {
d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
- d->updateWidgetFromCheckStatus(d->m_lineEditDisplay.data());
+ d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data());
}
if (d->m_textEditDisplay) {
const QString old = d->m_textEditDisplay->document()->toPlainText();
if (displayedString != old)
d->m_textEditDisplay->setText(displayedString);
- d->updateWidgetFromCheckStatus(d->m_textEditDisplay.data());
+ d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data());
}
if (d->m_labelDisplay) {
@@ -975,9 +1245,9 @@ void StringAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement,
BoolAspect::BoolAspect(const QString &settingsKey)
: d(new Internal::BoolAspectPrivate)
{
- setValue(false);
setDefaultValue(false);
setSettingsKey(settingsKey);
+ setSpan(2, 1);
}
/*!
@@ -995,23 +1265,73 @@ void BoolAspect::addToLayout(LayoutBuilder &builder)
switch (d->m_labelPlacement) {
case LabelPlacement::AtCheckBoxWithoutDummyLabel:
d->m_checkBox->setText(labelText());
+ builder.addItem(d->m_checkBox.data());
break;
- case LabelPlacement::AtCheckBox:
+ case LabelPlacement::AtCheckBox: {
d->m_checkBox->setText(labelText());
- builder.addItem(createSubWidget<QLabel>());
+ LayoutBuilder::LayoutType type = builder.layoutType();
+ if (type == LayoutBuilder::FormLayout)
+ builder.addItem(createSubWidget<QLabel>());
+ builder.addItem(d->m_checkBox.data());
break;
+ }
case LabelPlacement::InExtraLabel:
- setupLabel();
- builder.addItem(label());
+ addLabeledItem(builder, d->m_checkBox);
break;
}
d->m_checkBox->setChecked(value());
- builder.addItem(d->m_checkBox.data());
- connect(d->m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
- setValue(d->m_checkBox->isChecked());
+ if (isAutoApply()) {
+ connect(d->m_checkBox.data(), &QAbstractButton::clicked,
+ this, [this](bool val) { setValue(val); });
+ }
+ connect(d->m_checkBox.data(), &QAbstractButton::clicked,
+ this, &BoolAspect::volatileValueChanged);
+}
+
+QAction *BoolAspect::action()
+{
+ if (hasAction())
+ return BaseAspect::action();
+ auto act = BaseAspect::action(); // Creates it.
+ act->setCheckable(true);
+ act->setChecked(value());
+ connect(act, &QAction::triggered, this, [this](bool newValue) {
+ // The check would be nice to have in simple conditions, but if we
+ // have an action that's used both on a settings page and as action
+ // in a menu like "Use FakeVim", isAutoApply() is false, and yet this
+ // here can trigger.
+ //QTC_CHECK(isAutoApply());
+ setValue(newValue);
});
+ return act;
}
+QVariant BoolAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ if (d->m_checkBox)
+ return d->m_checkBox->isChecked();
+ if (d->m_groupBox)
+ return d->m_groupBox->isChecked();
+ QTC_CHECK(false);
+ return {};
+}
+
+void BoolAspect::setVolatileValue(const QVariant &val)
+{
+ QTC_CHECK(!isAutoApply());
+ if (d->m_checkBox)
+ d->m_checkBox->setChecked(val.toBool());
+ else if (d->m_groupBox)
+ d->m_groupBox->setChecked(val.toBool());
+}
+
+void BoolAspect::emitChangedValue()
+{
+ emit valueChanged(value());
+}
+
+
/*!
\reimp
*/
@@ -1026,16 +1346,40 @@ void BoolAspect::setValue(bool value)
if (BaseAspect::setValueQuietly(value)) {
if (d->m_checkBox)
d->m_checkBox->setChecked(value);
+ //qDebug() << "SetValue: Changing" << labelText() << " to " << value;
emit changed();
+ //QTC_CHECK(!labelText().isEmpty());
+ emit valueChanged(value);
+ //qDebug() << "SetValue: Changed" << labelText() << " to " << value;
+ if (hasAction()) {
+ //qDebug() << "SetValue: Triggering " << labelText() << "with" << value;
+ emit action()->triggered(value);
+ }
}
}
+void BoolAspect::setDefaultValue(bool val)
+{
+ BaseAspect::setDefaultValue(val);
+}
+
void BoolAspect::setLabel(const QString &labelText, LabelPlacement labelPlacement)
{
BaseAspect::setLabelText(labelText);
d->m_labelPlacement = labelPlacement;
}
+void BoolAspect::setLabelPlacement(BoolAspect::LabelPlacement labelPlacement)
+{
+ d->m_labelPlacement = labelPlacement;
+}
+
+void BoolAspect::setHandlesGroup(QGroupBox *box)
+{
+ registerSubWidget(box);
+ d->m_groupBox = box;
+}
+
/*!
\class Utils::SelectionAspect
\inmodule QtCreator
@@ -1049,7 +1393,9 @@ void BoolAspect::setLabel(const QString &labelText, LabelPlacement labelPlacemen
SelectionAspect::SelectionAspect()
: d(new Internal::SelectionAspectPrivate)
-{}
+{
+ setSpan(2, 1);
+}
/*!
\reimp
@@ -1070,40 +1416,77 @@ void SelectionAspect::addToLayout(LayoutBuilder &builder)
d->m_buttonGroup = new QButtonGroup();
d->m_buttonGroup->setExclusive(true);
for (int i = 0, n = d->m_options.size(); i < n; ++i) {
- const Internal::SelectionAspectPrivate::Option &option = d->m_options.at(i);
- auto button = new QRadioButton(option.displayName);
+ const Option &option = d->m_options.at(i);
+ auto button = createSubWidget<QRadioButton>(option.displayName);
button->setChecked(i == value());
+ button->setEnabled(option.enabled);
button->setToolTip(option.tooltip);
builder.addItems({{}, button});
d->m_buttons.append(button);
- d->m_buttonGroup->addButton(button);
- connect(button, &QAbstractButton::clicked, this, [this, i] {
- setValue(i);
- });
+ d->m_buttonGroup->addButton(button, i);
+ if (isAutoApply()) {
+ connect(button, &QAbstractButton::clicked, this, [this, i] {
+ setValue(i);
+ });
+ }
}
break;
case DisplayStyle::ComboBox:
- setupLabel();
setLabelText(displayName());
d->m_comboBox = createSubWidget<QComboBox>();
for (int i = 0, n = d->m_options.size(); i < n; ++i)
d->m_comboBox->addItem(d->m_options.at(i).displayName);
- connect(d->m_comboBox.data(), QOverload<int>::of(&QComboBox::activated),
- this, &SelectionAspect::setValue);
+ if (isAutoApply()) {
+ connect(d->m_comboBox.data(), QOverload<int>::of(&QComboBox::activated),
+ this, &SelectionAspect::setValue);
+ }
+ connect(d->m_comboBox.data(), QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &SelectionAspect::volatileValueChanged);
d->m_comboBox->setCurrentIndex(value());
- builder.addItems({label(), d->m_comboBox.data()});
+ addLabeledItem(builder, d->m_comboBox);
+ break;
+ }
+}
+
+QVariant SelectionAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ switch (d->m_displayStyle) {
+ case DisplayStyle::RadioButtons:
+ QTC_ASSERT(d->m_buttonGroup, return {});
+ return d->m_buttonGroup->checkedId();
+ case DisplayStyle::ComboBox:
+ QTC_ASSERT(d->m_comboBox, return {});
+ return d->m_comboBox->currentIndex();
+ }
+ return {};
+}
+
+void SelectionAspect::setVolatileValue(const QVariant &val)
+{
+ QTC_CHECK(!isAutoApply());
+ switch (d->m_displayStyle) {
+ case DisplayStyle::RadioButtons: {
+ if (d->m_buttonGroup) {
+ QAbstractButton *button = d->m_buttonGroup->button(val.toInt());
+ QTC_ASSERT(button, return);
+ button->setChecked(true);
+ }
+ break;
+ }
+ case DisplayStyle::ComboBox:
+ if (d->m_comboBox)
+ d->m_comboBox->setCurrentIndex(val.toInt());
break;
}
}
-void SelectionAspect::setVisibleDynamic(bool visible)
+void SelectionAspect::finish()
{
- if (QLabel *l = label())
- l->setVisible(visible);
- if (d->m_comboBox)
- d->m_comboBox->setVisible(visible);
- for (QRadioButton * const button : qAsConst(d->m_buttons))
- button->setVisible(visible);
+ delete d->m_buttonGroup;
+ d->m_buttonGroup = nullptr;
+ BaseAspect::finish();
+ d->m_buttons.clear();
}
void SelectionAspect::setDisplayStyle(SelectionAspect::DisplayStyle style)
@@ -1127,14 +1510,72 @@ void SelectionAspect::setValue(int value)
}
}
+void SelectionAspect::setStringValue(const QString &val)
+{
+ const int index = indexForDisplay(val);
+ QTC_ASSERT(index >= 0, return);
+ setValue(index);
+}
+
+void SelectionAspect::setDefaultValue(int val)
+{
+ BaseAspect::setDefaultValue(val);
+}
+
+// Note: This needs to be set after all options are added.
+void SelectionAspect::setDefaultValue(const QString &val)
+{
+ BaseAspect::setDefaultValue(indexForDisplay(val));
+}
+
QString SelectionAspect::stringValue() const
{
return d->m_options.at(value()).displayName;
}
+QVariant SelectionAspect::itemValue() const
+{
+ return d->m_options.at(value()).itemData;
+}
+
void SelectionAspect::addOption(const QString &displayName, const QString &toolTip)
{
- d->m_options.append({displayName, toolTip});
+ d->m_options.append(Option(displayName, toolTip, {}));
+}
+
+void SelectionAspect::addOption(const Option &option)
+{
+ d->m_options.append(option);
+}
+
+int SelectionAspect::indexForDisplay(const QString &displayName) const
+{
+ for (int i = 0, n = d->m_options.size(); i < n; ++i) {
+ if (d->m_options.at(i).displayName == displayName)
+ return i;
+ }
+ return -1;
+}
+
+QString SelectionAspect::displayForIndex(int index) const
+{
+ QTC_ASSERT(index >= 0 && index < d->m_options.size(), return {});
+ return d->m_options.at(index).displayName;
+}
+
+int SelectionAspect::indexForItemValue(const QVariant &value) const
+{
+ for (int i = 0, n = d->m_options.size(); i < n; ++i) {
+ if (d->m_options.at(i).itemData == value)
+ return i;
+ }
+ return -1;
+}
+
+QVariant SelectionAspect::itemValueForIndex(int index) const
+{
+ QTC_ASSERT(index >= 0 && index < d->m_options.size(), return {});
+ return d->m_options.at(index).itemData;
}
/*!
@@ -1151,8 +1592,8 @@ void SelectionAspect::addOption(const QString &displayName, const QString &toolT
MultiSelectionAspect::MultiSelectionAspect()
: d(new Internal::MultiSelectionAspectPrivate(this))
{
- setValue(QStringList());
setDefaultValue(QStringList());
+ setSpan(2, 1);
}
/*!
@@ -1171,7 +1612,6 @@ void MultiSelectionAspect::addToLayout(LayoutBuilder &builder)
switch (d->m_displayStyle) {
case DisplayStyle::ListView:
- setupLabel();
d->m_listView = createSubWidget<QListWidget>();
for (const QString &val : qAsConst(d->m_allValues)) {
auto item = new QListWidgetItem(val, d->m_listView);
@@ -1183,7 +1623,7 @@ void MultiSelectionAspect::addToLayout(LayoutBuilder &builder)
if (d->setValueSelectedHelper(item->text(), item->checkState() & Qt::Checked))
emit changed();
});
- builder.addItems({label(), d->m_listView.data()});
+ addLabeledItem(builder, d->m_listView);
}
}
@@ -1213,14 +1653,6 @@ void MultiSelectionAspect::setAllValues(const QStringList &val)
d->m_allValues = val;
}
-void MultiSelectionAspect::setVisibleDynamic(bool visible)
-{
- if (QLabel *l = label())
- l->setVisible(visible);
- if (d->m_listView)
- d->m_listView->setVisible(visible);
-}
-
void MultiSelectionAspect::setDisplayStyle(MultiSelectionAspect::DisplayStyle style)
{
d->m_displayStyle = style;
@@ -1268,6 +1700,7 @@ IntegerAspect::IntegerAspect()
: d(new Internal::IntegerAspectPrivate)
{
setDefaultValue(qint64(0));
+ setSpan(2, 1);
}
/*!
@@ -1280,23 +1713,37 @@ IntegerAspect::~IntegerAspect() = default;
*/
void IntegerAspect::addToLayout(LayoutBuilder &builder)
{
- setupLabel();
-
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QSpinBox>();
- d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor));
d->m_spinBox->setDisplayIntegerBase(d->m_displayIntegerBase);
d->m_spinBox->setPrefix(d->m_prefix);
d->m_spinBox->setSuffix(d->m_suffix);
- if (d->m_maximumValue.isValid() && d->m_maximumValue.isValid())
- d->m_spinBox->setRange(int(d->m_minimumValue.toLongLong() / d->m_displayScaleFactor),
- int(d->m_maximumValue.toLongLong() / d->m_displayScaleFactor));
-
- builder.addItems({label(), d->m_spinBox.data()});
- connect(d->m_spinBox.data(), QOverload<int>::of(&QSpinBox::valueChanged),
- this, [this](int value) {
- setValue(value * d->m_displayScaleFactor);
- });
+ d->m_spinBox->setSingleStep(d->m_singleStep);
+ d->m_spinBox->setSpecialValueText(d->m_specialValueText);
+ if (d->m_maximumValue && d->m_maximumValue)
+ d->m_spinBox->setRange(int(d->m_minimumValue.value() / d->m_displayScaleFactor),
+ int(d->m_maximumValue.value() / d->m_displayScaleFactor));
+ d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor)); // Must happen after setRange()
+ addLabeledItem(builder, d->m_spinBox);
+
+ if (isAutoApply()) {
+ connect(d->m_spinBox.data(), QOverload<int>::of(&QSpinBox::valueChanged),
+ this, [this] { setValue(d->m_spinBox->value()); });
+ }
+}
+
+QVariant IntegerAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ QTC_ASSERT(d->m_spinBox, return {});
+ return d->m_spinBox->value() * d->m_displayScaleFactor;
+}
+
+void IntegerAspect::setVolatileValue(const QVariant &val)
+{
+ QTC_CHECK(!isAutoApply());
+ if (d->m_spinBox)
+ d->m_spinBox->setValue(int(val.toLongLong() / d->m_displayScaleFactor));
}
qint64 IntegerAspect::value() const
@@ -1306,11 +1753,7 @@ qint64 IntegerAspect::value() const
void IntegerAspect::setValue(qint64 value)
{
- if (setValueQuietly(value)) {
- if (d->m_spinBox)
- d->m_spinBox->setValue(int(value / d->m_displayScaleFactor));
- emit changed();
- }
+ BaseAspect::setValue(value);
}
void IntegerAspect::setRange(qint64 min, qint64 max)
@@ -1349,6 +1792,121 @@ void IntegerAspect::setDefaultValue(qint64 defaultValue)
BaseAspect::setDefaultValue(defaultValue);
}
+void IntegerAspect::setSpecialValueText(const QString &specialText)
+{
+ d->m_specialValueText = specialText;
+}
+
+void IntegerAspect::setSingleStep(qint64 step)
+{
+ d->m_singleStep = step;
+}
+
+
+/*!
+ \class Utils::DoubleAspect
+ \inmodule QtCreator
+
+ \brief An double aspect is a numerical property of some object, together with
+ a description of its behavior for common operations like visualizing or
+ persisting.
+
+ The double aspect is displayed using a \c QDoubleSpinBox.
+
+ The visual representation often contains a label in front
+ the display of the spin box.
+*/
+
+DoubleAspect::DoubleAspect()
+ : d(new Internal::DoubleAspectPrivate)
+{
+ setDefaultValue(double(0));
+ setSpan(2, 1);
+}
+
+/*!
+ \reimp
+*/
+DoubleAspect::~DoubleAspect() = default;
+
+/*!
+ \reimp
+*/
+void DoubleAspect::addToLayout(LayoutBuilder &builder)
+{
+ QTC_CHECK(!d->m_spinBox);
+ d->m_spinBox = createSubWidget<QDoubleSpinBox>();
+ d->m_spinBox->setPrefix(d->m_prefix);
+ d->m_spinBox->setSuffix(d->m_suffix);
+ d->m_spinBox->setSingleStep(d->m_singleStep);
+ d->m_spinBox->setSpecialValueText(d->m_specialValueText);
+ if (d->m_maximumValue && d->m_maximumValue)
+ d->m_spinBox->setRange(d->m_minimumValue.value(), d->m_maximumValue.value());
+ d->m_spinBox->setValue(value()); // Must happen after setRange()!
+ addLabeledItem(builder, d->m_spinBox);
+
+ if (isAutoApply()) {
+ connect(d->m_spinBox.data(), QOverload<double>::of(&QDoubleSpinBox::valueChanged),
+ this, [this] { setValue(d->m_spinBox->value()); });
+ }
+}
+
+QVariant DoubleAspect::volatileValue() const
+{
+ QTC_CHECK(!isAutoApply());
+ QTC_ASSERT(d->m_spinBox, return {});
+ return d->m_spinBox->value();
+}
+
+void DoubleAspect::setVolatileValue(const QVariant &val)
+{
+ QTC_CHECK(!isAutoApply());
+ if (d->m_spinBox)
+ d->m_spinBox->setValue(val.toDouble());
+}
+
+double DoubleAspect::value() const
+{
+ return BaseAspect::value().toDouble();
+}
+
+void DoubleAspect::setValue(double value)
+{
+ BaseAspect::setValue(value);
+}
+
+void DoubleAspect::setRange(double min, double max)
+{
+ d->m_minimumValue = min;
+ d->m_maximumValue = max;
+}
+
+void DoubleAspect::setPrefix(const QString &prefix)
+{
+ d->m_prefix = prefix;
+}
+
+void DoubleAspect::setSuffix(const QString &suffix)
+{
+ d->m_suffix = suffix;
+}
+
+void DoubleAspect::setDefaultValue(double defaultValue)
+{
+ BaseAspect::setDefaultValue(defaultValue);
+}
+
+void DoubleAspect::setSpecialValueText(const QString &specialText)
+{
+ d->m_specialValueText = specialText;
+}
+
+void DoubleAspect::setSingleStep(double step)
+{
+ d->m_singleStep = step;
+}
+
+
/*!
\class Utils::BaseTristateAspect
\inmodule QtCreator
@@ -1363,7 +1921,6 @@ TriStateAspect::TriStateAspect(const QString onString, const QString &offString,
const QString &defaultString)
{
setDisplayStyle(DisplayStyle::ComboBox);
- setValue(TriState::Default);
setDefaultValue(TriState::Default);
addOption(onString);
addOption(offString);
@@ -1435,6 +1992,88 @@ void StringListAspect::setValue(const QStringList &value)
BaseAspect::setValue(value);
}
+void StringListAspect::appendValue(const QString &s, bool allowDuplicates)
+{
+ QStringList val = value();
+ if (allowDuplicates || !val.contains(s))
+ val.append(s);
+ setValue(val);
+}
+
+void StringListAspect::removeValue(const QString &s)
+{
+ QStringList val = value();
+ val.removeAll(s);
+ setValue(val);
+}
+
+void StringListAspect::appendValues(const QStringList &values, bool allowDuplicates)
+{
+ QStringList val = value();
+ for (const QString &s : values) {
+ if (allowDuplicates || !val.contains(s))
+ val.append(s);
+ }
+ setValue(val);
+}
+
+void StringListAspect::removeValues(const QStringList &values)
+{
+ QStringList val = value();
+ for (const QString &s : values)
+ val.removeAll(s);
+ setValue(val);
+}
+
+/*!
+ \class Utils::IntegerListAspect
+ \inmodule QtCreator
+
+ \brief A string list aspect represents a property of some object
+ that is a list of strings.
+*/
+
+IntegersAspect::IntegersAspect()
+{
+ setDefaultValue({});
+}
+
+/*!
+ \reimp
+*/
+IntegersAspect::~IntegersAspect() = default;
+
+/*!
+ \reimp
+*/
+void IntegersAspect::addToLayout(LayoutBuilder &builder)
+{
+ Q_UNUSED(builder)
+ // TODO - when needed.
+}
+
+void IntegersAspect::emitChangedValue()
+{
+ emit valueChanged(value());
+}
+
+QList<int> IntegersAspect::value() const
+{
+ return Utils::transform(BaseAspect::value().toList(),
+ [](QVariant v) { return v.toInt(); });
+}
+
+void IntegersAspect::setValue(const QList<int> &value)
+{
+ BaseAspect::setValue(Utils::transform(value, &QVariant::fromValue<int>));
+}
+
+void IntegersAspect::setDefaultValue(const QList<int> &value)
+{
+ BaseAspect::setDefaultValue(Utils::transform(value, &QVariant::fromValue<int>));
+}
+
+
/*!
\class Utils::TextDisplay
@@ -1471,9 +2110,12 @@ void TextDisplay::addToLayout(LayoutBuilder &builder)
d->m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
d->m_label->setElideMode(Qt::ElideNone);
d->m_label->setWordWrap(true);
+ // Do not use m_label->setVisible(isVisible()) unconditionally, it does not
+ // have a QWidget parent yet when used in a LayoutBuilder.
+ if (!isVisible())
+ d->m_label->setVisible(false);
}
builder.addItem(d->m_label.data());
- d->m_label->setVisible(isVisible());
}
/*!
@@ -1487,59 +2129,207 @@ void TextDisplay::setIconType(InfoLabel::InfoType t)
d->m_label->setType(t);
}
+void TextDisplay::setText(const QString &message)
+{
+ d->m_message = message;
+}
+
/*!
\class Utils::AspectContainer
\inmodule QtCreator
\brief The AspectContainer class wraps one or more aspects while providing
the interface of a single aspect.
+
+ Sub-aspects ownership can be declared using \a setOwnsSubAspects.
*/
-AspectContainer::AspectContainer()
- : d(new Internal::AspectContainerPrivate)
+namespace Internal {
+
+class AspectContainerPrivate
+{
+public:
+ QList<BaseAspect *> m_items; // Not owned
+ bool m_autoApply = true;
+ bool m_ownsSubAspects = false;
+ QStringList m_settingsGroup;
+};
+
+} // Internal
+
+AspectContainer::AspectContainer(QObject *parent)
+ : QObject(parent), d(new Internal::AspectContainerPrivate)
{}
/*!
\reimp
*/
-AspectContainer::~AspectContainer() = default;
+AspectContainer::~AspectContainer()
+{
+ if (d->m_ownsSubAspects)
+ qDeleteAll(d->m_items);
+}
/*!
\internal
*/
-void AspectContainer::addAspectHelper(BaseAspect *aspect)
+void AspectContainer::registerAspect(BaseAspect *aspect)
{
+ aspect->setAutoApply(d->m_autoApply);
d->m_items.append(aspect);
- connect(aspect, &BaseAspect::changed, this, &BaseAspect::changed);
}
-/*!
- Adds all visible sub-aspects to \a builder.
-*/
-void AspectContainer::addToLayout(LayoutBuilder &builder)
+void AspectContainer::registerAspects(const AspectContainer &aspects)
{
- for (BaseAspect *aspect : qAsConst(d->m_items)) {
- if (aspect->isVisible())
- aspect->addToLayout(builder);
- }
+ for (BaseAspect *aspect : qAsConst(aspects.d->m_items))
+ registerAspect(aspect);
}
/*!
- \reimp
+ Retrieves a BaseAspect with a given \a id, or nullptr if no such aspect is contained.
+
+ \sa BaseAspect.
*/
+BaseAspect *AspectContainer::aspect(Id id) const
+{
+ return Utils::findOrDefault(d->m_items, Utils::equal(&BaseAspect::id, id));
+}
+
+AspectContainer::const_iterator AspectContainer::begin() const
+{
+ return d->m_items.begin();
+}
+
+AspectContainer::const_iterator AspectContainer::end() const
+{
+ return d->m_items.end();
+}
+
+const QList<BaseAspect *> &AspectContainer::aspects() const
+{
+ return d->m_items;
+}
+
void AspectContainer::fromMap(const QVariantMap &map)
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->fromMap(map);
+
+ emit fromMapFinished();
+
}
-/*!
- \reimp
-*/
void AspectContainer::toMap(QVariantMap &map) const
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->toMap(map);
}
+void AspectContainer::readSettings(QSettings *settings)
+{
+ for (const QString &group : d->m_settingsGroup)
+ settings->beginGroup(group);
+
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->readSettings(settings);
+
+ for (int i = 0; i != d->m_settingsGroup.size(); ++i)
+ settings->endGroup();
+}
+
+void AspectContainer::writeSettings(QSettings *settings) const
+{
+ for (const QString &group : d->m_settingsGroup)
+ settings->beginGroup(group);
+
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->writeSettings(settings);
+
+ for (int i = 0; i != d->m_settingsGroup.size(); ++i)
+ settings->endGroup();
+}
+
+void AspectContainer::setSettingsGroup(const QString &groupKey)
+{
+ d->m_settingsGroup = QStringList{groupKey};
+}
+
+void AspectContainer::setSettingsGroups(const QString &groupKey, const QString &subGroupKey)
+{
+ d->m_settingsGroup = QStringList{groupKey, subGroupKey};
+}
+
+void AspectContainer::apply()
+{
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->apply();
+
+ emit applied();
+}
+
+void AspectContainer::cancel()
+{
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->cancel();
+}
+
+void AspectContainer::finish()
+{
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->finish();
+}
+
+void AspectContainer::reset()
+{
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->setValueQuietly(aspect->defaultValue());
+}
+
+void AspectContainer::setAutoApply(bool on)
+{
+ d->m_autoApply = on;
+ for (BaseAspect *aspect : qAsConst(d->m_items))
+ aspect->setAutoApply(on);
+}
+
+void AspectContainer::setOwnsSubAspects(bool on)
+{
+ d->m_ownsSubAspects = on;
+}
+
+bool AspectContainer::isDirty() const
+{
+ for (BaseAspect *aspect : qAsConst(d->m_items)) {
+ if (aspect->isDirty())
+ return true;
+ }
+ return false;
+}
+
+bool AspectContainer::equals(const AspectContainer &other) const
+{
+ // FIXME: Expensive, but should not really be needed in a fully aspectified world.
+ QVariantMap thisMap, thatMap;
+ toMap(thisMap);
+ other.toMap(thatMap);
+ return thisMap == thatMap;
+}
+
+void AspectContainer::copyFrom(const AspectContainer &other)
+{
+ QVariantMap map;
+ other.toMap(map);
+ fromMap(map);
+}
+
+void AspectContainer::forEachAspect(const std::function<void(BaseAspect *)> &run) const
+{
+ for (BaseAspect *aspect : qAsConst(d->m_items)) {
+ if (auto container = dynamic_cast<AspectContainer *>(aspect))
+ container->forEachAspect(run);
+ else
+ run(aspect);
+ }
+}
+
} // namespace Utils