Utils: Add AspectList aspect

Change-Id: Id164828f4ba46c973edbcd123c855bb7b70ad8f7
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Marcus Tillmanns
2023-09-13 14:47:49 +02:00
parent 8627dab89d
commit d44afce0f0
6 changed files with 271 additions and 160 deletions

View File

@@ -2969,4 +2969,191 @@ SettingsGroupNester::~SettingsGroupNester()
theSettings->endGroup();
}
class AddItemCommand : public QUndoCommand
{
public:
AddItemCommand(AspectList *aspect, const std::shared_ptr<BaseAspect> &item)
: m_aspect(aspect)
, m_item(item)
{}
void undo() override { m_aspect->actualRemoveItem(m_item); }
void redo() override { m_aspect->actualAddItem(m_item); }
private:
AspectList *m_aspect;
std::shared_ptr<BaseAspect> m_item;
};
class RemoveItemCommand : public QUndoCommand
{
public:
RemoveItemCommand(AspectList *aspect, const std::shared_ptr<BaseAspect> &item)
: m_aspect(aspect)
, m_item(item)
{}
void undo() override { m_aspect->actualAddItem(m_item); }
void redo() override { m_aspect->actualRemoveItem(m_item); }
private:
AspectList *m_aspect;
std::shared_ptr<BaseAspect> m_item;
};
class Internal::AspectListPrivate
{
public:
QList<std::shared_ptr<BaseAspect>> items;
QList<std::shared_ptr<BaseAspect>> volatileItems;
AspectList::CreateItem createItem;
AspectList::ItemCallback itemAdded;
AspectList::ItemCallback itemRemoved;
};
AspectList::AspectList(Utils::AspectContainer *container)
: Utils::BaseAspect(container)
, d(std::make_unique<Internal::AspectListPrivate>())
{}
AspectList::~AspectList() = default;
void AspectList::fromMap(const Utils::Store &map)
{
QTC_ASSERT(!settingsKey().isEmpty(), return);
QVariantList list = map[settingsKey()].toList();
d->volatileItems.clear();
for (const QVariant &entry : list) {
auto item = d->createItem();
item->setAutoApply(isAutoApply());
item->setUndoStack(undoStack());
item->fromMap(Utils::storeFromVariant(entry));
d->volatileItems.append(item);
}
d->items = d->volatileItems;
}
QVariantList AspectList::toList(bool v) const
{
QVariantList list;
const auto &items = v ? d->volatileItems : d->items;
for (const auto &item : items) {
Utils::Store childStore;
if (v)
item->volatileToMap(childStore);
else
item->toMap(childStore);
list.append(Utils::variantFromStore(childStore));
}
return list;
}
void AspectList::toMap(Utils::Store &map) const
{
QTC_ASSERT(!settingsKey().isEmpty(), return);
const Utils::Key key = settingsKey();
map[key] = toList(false);
}
void AspectList::volatileToMap(Utils::Store &map) const
{
QTC_ASSERT(!settingsKey().isEmpty(), return);
const Utils::Key key = settingsKey();
map[key] = toList(true);
}
std::shared_ptr<BaseAspect> AspectList::actualAddItem(const std::shared_ptr<BaseAspect> &item)
{
item->setAutoApply(isAutoApply());
item->setUndoStack(undoStack());
d->volatileItems.append(item);
if (d->itemAdded)
d->itemAdded(item);
emit volatileValueChanged();
if (isAutoApply())
d->items = d->volatileItems;
return item;
}
QList<std::shared_ptr<BaseAspect>> AspectList::items() const
{
return d->items;
}
QList<std::shared_ptr<BaseAspect>> AspectList::volatileItems() const
{
return d->volatileItems;
}
std::shared_ptr<BaseAspect> AspectList::addItem(std::shared_ptr<BaseAspect> item)
{
if (undoStack())
pushUndo(new AddItemCommand(this, item));
else
return actualAddItem(item);
return item;
}
void AspectList::actualRemoveItem(std::shared_ptr<BaseAspect> item)
{
d->volatileItems.removeOne(item);
if (d->itemRemoved)
d->itemRemoved(item);
emit volatileValueChanged();
if (isAutoApply())
d->items = d->volatileItems;
}
void AspectList::removeItem(std::shared_ptr<BaseAspect> item)
{
if (undoStack())
pushUndo(new RemoveItemCommand(this, item));
else
actualRemoveItem(item);
}
void AspectList::apply()
{
d->items = d->volatileItems;
forEachItem<BaseAspect>([](const std::shared_ptr<BaseAspect> &aspect) { aspect->apply(); });
emit changed();
}
void AspectList::setCreateItemFunction(CreateItem createItem)
{
d->createItem = createItem;
}
void AspectList::setItemAddedCallback(const ItemCallback &callback)
{
d->itemAdded = callback;
}
void AspectList::setItemRemovedCallback(const ItemCallback &callback)
{
d->itemRemoved = callback;
}
qsizetype AspectList::size() const
{
return d->volatileItems.size();
}
bool AspectList::isDirty()
{
if (d->items != d->volatileItems)
return true;
for (const auto &item : d->volatileItems) {
if (item->isDirty())
return true;
}
return false;
}
} // namespace Utils

View File

@@ -8,8 +8,8 @@
#include "infolabel.h"
#include "macroexpander.h"
#include "pathchooser.h"
#include "store.h"
#include "qtcsettings.h"
#include "store.h"
#include <functional>
#include <memory>
@@ -46,6 +46,7 @@ class StringAspectPrivate;
class StringListAspectPrivate;
class TextDisplayPrivate;
class CheckableAspectImplementation;
class AspectListPrivate;
} // Internal
class QTCREATOR_UTILS_EXPORT BaseAspect : public QObject
@@ -994,4 +995,76 @@ private:
T m_value;
};
class QTCREATOR_UTILS_EXPORT AspectList : public Utils::BaseAspect
{
public:
using CreateItem = std::function<std::shared_ptr<BaseAspect>()>;
using ItemCallback = std::function<void(std::shared_ptr<BaseAspect>)>;
AspectList(Utils::AspectContainer *container = nullptr);
~AspectList() override;
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
void volatileToMap(Utils::Store &map) const override;
QVariantList toList(bool v) const;
QList<std::shared_ptr<BaseAspect>> items() const;
QList<std::shared_ptr<BaseAspect>> volatileItems() const;
std::shared_ptr<BaseAspect> addItem(std::shared_ptr<BaseAspect> item);
std::shared_ptr<BaseAspect> actualAddItem(const std::shared_ptr<BaseAspect> &item);
void removeItem(std::shared_ptr<BaseAspect> item);
void actualRemoveItem(std::shared_ptr<BaseAspect> item);
void apply() override;
void setCreateItemFunction(CreateItem createItem);
template<class T>
void forEachItem(std::function<void(const std::shared_ptr<T> &)> callback)
{
for (const auto &item : volatileItems())
callback(std::static_pointer_cast<T>(item));
}
template<class T>
void forEachItem(std::function<void(const std::shared_ptr<T> &, int)> callback)
{
int idx = 0;
for (const auto &item : volatileItems())
callback(std::static_pointer_cast<T>(item), idx++);
}
void setItemAddedCallback(const ItemCallback &callback);
void setItemRemovedCallback(const ItemCallback &callback);
template<class T>
void setItemAddedCallback(const std::function<void(const std::shared_ptr<T>)> &callback)
{
setItemAddedCallback([callback](const std::shared_ptr<BaseAspect> &item) {
callback(std::static_pointer_cast<T>(item));
});
}
template<class T>
void setItemRemovedCallback(const std::function<void(const std::shared_ptr<T>)> &callback)
{
setItemRemovedCallback([callback](const std::shared_ptr<BaseAspect> &item) {
callback(std::static_pointer_cast<T>(item));
});
}
qsizetype size() const;
bool isDirty() override;
QVariant volatileVariantValue() const override { return {}; }
private:
std::unique_ptr<Internal::AspectListPrivate> d;
};
} // namespace Utils

View File

@@ -70,138 +70,4 @@ private:
QStandardItemModel *m_model{nullptr};
};
template<class T>
class AspectListAspect : public Utils::BaseAspect
{
public:
using ToBaseAspectPtr = std::function<Utils::BaseAspect *(const T &)>;
using CreateItem = std::function<T()>;
using ItemCallback = std::function<void(const T &)>;
using IsDirty = std::function<bool(const T &)>;
using Apply = std::function<void(const T &)>;
AspectListAspect(Utils::AspectContainer *container = nullptr)
: Utils::BaseAspect(container)
{}
void fromMap(const Utils::Store &map) override
{
QTC_ASSERT(!settingsKey().isEmpty(), return);
QVariantList list = map[settingsKey()].toList();
for (const QVariant &entry : list) {
T item = m_createItem();
m_toBaseAspect(item)->fromMap(Utils::storeFromVariant(entry));
m_volatileItems.append(item);
}
m_items = m_volatileItems;
}
QVariantList toList(bool v) const
{
QVariantList list;
const auto &items = v ? m_volatileItems : m_items;
for (const auto &item : items) {
Utils::Store childMap;
if (v)
m_toBaseAspect(item)->volatileToMap(childMap);
else
m_toBaseAspect(item)->toMap(childMap);
list.append(Utils::variantFromStore(childMap));
}
return list;
}
void toMap(Utils::Store &map) const override
{
QTC_ASSERT(!settingsKey().isEmpty(), return);
const Utils::Key key = settingsKey();
map[key] = toList(false);
}
void volatileToMap(Utils::Store &map) const override
{
QTC_ASSERT(!settingsKey().isEmpty(), return);
const Utils::Key key = settingsKey();
map[key] = toList(true);
}
T addItem(T item)
{
m_volatileItems.append(item);
if (m_itemAdded)
m_itemAdded(item);
emit volatileValueChanged();
if (isAutoApply())
apply();
return item;
}
void removeItem(T item)
{
m_volatileItems.removeOne(item);
if (m_itemRemoved)
m_itemRemoved(item);
emit volatileValueChanged();
if (isAutoApply())
apply();
}
void apply() override
{
m_items = m_volatileItems;
if (m_apply)
forEachItem(m_apply);
emit changed();
}
void setToBaseAspectFunction(ToBaseAspectPtr toBaseAspect) { m_toBaseAspect = toBaseAspect; }
void setCreateItemFunction(CreateItem createItem) { m_createItem = createItem; }
void setIsDirtyFunction(IsDirty isDirty) { m_isDirty = isDirty; }
void setApplyFunction(Apply apply) { m_apply = apply; }
void forEachItem(std::function<void(const T &)> callback)
{
for (const auto &item : m_volatileItems)
callback(item);
}
void forEachItem(std::function<void(const T &, int)> callback)
{
int idx = 0;
for (const auto &item : m_volatileItems)
callback(item, idx++);
}
void setItemAddedCallback(const ItemCallback &callback) { m_itemAdded = callback; }
void setItemRemovedCallback(const ItemCallback &callback) { m_itemRemoved = callback; }
qsizetype size() { return m_volatileItems.size(); }
bool isDirty() override
{
if (m_isDirty) {
for (const auto &item : m_volatileItems) {
if (m_isDirty(item))
return true;
}
}
return false;
}
QVariant volatileVariantValue() const override { return {}; }
private:
QList<T> m_items;
QList<T> m_volatileItems;
ToBaseAspectPtr m_toBaseAspect;
CreateItem m_createItem;
IsDirty m_isDirty;
Apply m_apply;
ItemCallback m_itemAdded;
ItemCallback m_itemRemoved;
};
} // namespace CompilerExplorer

View File

@@ -515,13 +515,13 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
sourceSettings->compilers.forEachItem(
sourceSettings->compilers.forEachItem<CompilerSettings>(
[addCompiler, sourceSettings](const std::shared_ptr<CompilerSettings> &compilerSettings,
int idx) {
addCompiler(sourceSettings, compilerSettings, idx + 1);
});
sourceSettings->compilers.setItemAddedCallback(
sourceSettings->compilers.setItemAddedCallback<CompilerSettings>(
[addCompiler, sourceSettings = sourceSettings.get()](
const std::shared_ptr<CompilerSettings> &compilerSettings) {
addCompiler(sourceSettings->shared_from_this(),
@@ -529,7 +529,7 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
sourceSettings->compilers.size());
});
sourceSettings->compilers.setItemRemovedCallback(
sourceSettings->compilers.setItemRemovedCallback<CompilerSettings>(
[this](const std::shared_ptr<CompilerSettings> &compilerSettings) {
m_compilerWidgets.removeIf([compilerSettings](const QDockWidget *c) {
return static_cast<CompilerWidget *>(c->widget())->m_compilerSettings
@@ -563,7 +563,7 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
m_sourceWidgets.clear();
m_compilerWidgets.clear();
m_document->settings()->m_sources.forEachItem(addSourceEditor);
m_document->settings()->m_sources.forEachItem<SourceSettings>(addSourceEditor);
QVariantMap windowState = m_document->settings()->windowState.value();
if (!windowState.isEmpty()) {
@@ -589,8 +589,8 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
}
};
document->settings()->m_sources.setItemAddedCallback(addSourceEditor);
document->settings()->m_sources.setItemRemovedCallback(removeSourceEditor);
document->settings()->m_sources.setItemAddedCallback<SourceSettings>(addSourceEditor);
document->settings()->m_sources.setItemRemovedCallback<SourceSettings>(removeSourceEditor);
connect(document.get(), &JsonSettingsDocument::settingsChanged, this, recreateEditors);
m_context = new Core::IContext(this);

View File

@@ -87,14 +87,6 @@ SourceSettings::SourceSettings(const ApiConfigFunction &apiConfigFunction)
return result;
});
compilers.setToBaseAspectFunction([](const std::shared_ptr<CompilerSettings> &item) {
return static_cast<Utils::BaseAspect *>(item.get());
});
compilers.setIsDirtyFunction(
[](const std::shared_ptr<CompilerSettings> &settings) { return settings->isDirty(); });
compilers.setApplyFunction(
[](const std::shared_ptr<CompilerSettings> &settings) { settings->apply(); });
for (const auto &aspect : this->aspects())
connect(aspect,
&Utils::BaseAspect::volatileValueChanged,
@@ -108,7 +100,7 @@ void SourceSettings::refresh()
cachedLanguages().clear();
languageId.refill();
compilers.forEachItem(&CompilerSettings::refresh);
compilers.forEachItem<CompilerSettings>(&CompilerSettings::refresh);
}
QString SourceSettings::languageExtension() const
@@ -328,16 +320,9 @@ CompilerExplorerSettings::CompilerExplorerSettings()
&CompilerExplorerSettings::changed);
return newSourceSettings;
});
m_sources.setIsDirtyFunction(
[](const std::shared_ptr<SourceSettings> &settings) { return settings->isDirty(); });
m_sources.setApplyFunction(
[](const std::shared_ptr<SourceSettings> &settings) { settings->apply(); });
m_sources.setToBaseAspectFunction([](const std::shared_ptr<SourceSettings> &item) {
return static_cast<Utils::BaseAspect *>(item.get());
});
connect(&compilerExplorerUrl, &Utils::StringAspect::volatileValueChanged, this, [this] {
m_sources.forEachItem(&SourceSettings::refresh);
m_sources.forEachItem<SourceSettings>(&SourceSettings::refresh);
});
for (const auto &aspect : this->aspects())

View File

@@ -34,7 +34,7 @@ public:
Utils::StringAspect compilerExplorerUrl{this};
Utils::TypedAspect<QVariantMap> windowState{this};
AspectListAspect<std::shared_ptr<SourceSettings>> m_sources{this};
Utils::AspectList m_sources{this};
Api::Config apiConfig() const
{
@@ -61,7 +61,7 @@ public:
public:
StringSelectionAspect languageId{this};
Utils::StringAspect source{this};
AspectListAspect<std::shared_ptr<CompilerSettings>> compilers{this};
Utils::AspectList compilers{this};
public:
QString languageExtension() const;