Fix re-instentiating if elements when the condition is dirty

Fixes #3953
This commit is contained in:
Olivier Goffart 2025-03-27 13:43:49 +01:00
parent 30dca1423e
commit a80f14e7d8
8 changed files with 412 additions and 75 deletions

View file

@ -816,8 +816,6 @@ namespace private_api {
template<typename C, typename ModelData>
class Repeater
{
private_api::Property<std::shared_ptr<Model<ModelData>>> model;
struct RepeaterInner : ModelChangeListener
{
enum class State { Clean, Dirty };
@ -866,10 +864,16 @@ class Repeater
}
};
public:
// FIXME: should be private, but layouting code uses it.
private_api::Property<std::shared_ptr<Model<ModelData>>> model;
mutable std::shared_ptr<RepeaterInner> inner;
vtable::VRef<private_api::ItemTreeVTable> item_at(int i) const
{
const auto &x = inner->data.at(i);
return { &C::static_vtable, const_cast<C *>(&(**x.ptr)) };
}
public:
template<typename F>
void set_model_binding(F &&binding) const
{
@ -947,12 +951,6 @@ public:
return std::numeric_limits<uint64_t>::max();
}
vtable::VRef<private_api::ItemTreeVTable> item_at(int i) const
{
const auto &x = inner->data.at(i);
return { &C::static_vtable, const_cast<C *>(&(**x.ptr)) };
}
vtable::VWeak<private_api::ItemTreeVTable> instance_at(std::size_t i) const
{
if (i >= inner->data.size()) {
@ -967,6 +965,8 @@ public:
return private_api::IndexRange { 0, inner->data.size() };
}
std::size_t len() const { return inner ? inner->data.size() : 0; }
float compute_layout_listview(const private_api::Property<float> *viewport_width,
float listview_width, float viewport_y) const
{
@ -992,6 +992,72 @@ public:
}
}
}
void for_each(auto &&f) const
{
if (inner) {
for (auto &&x : inner->data) {
f(*x.ptr);
}
}
}
};
template<typename C>
class Conditional
{
private_api::Property<bool> model;
mutable std::optional<ComponentHandle<C>> instance;
public:
template<typename F>
void set_model_binding(F &&binding) const
{
model.set_binding(std::forward<F>(binding));
}
template<typename Parent>
void ensure_updated(const Parent *parent) const
{
if (!model.get()) {
instance = std::nullopt;
} else if (!instance) {
instance = C::create(parent);
(*instance)->init();
}
}
uint64_t visit(TraversalOrder order, private_api::ItemVisitorRefMut visitor) const
{
if (instance) {
vtable::VRef<private_api::ItemTreeVTable> ref { &C::static_vtable,
const_cast<C *>(&(**instance)) };
if (ref.vtable->visit_children_item(ref, -1, order, visitor)
!= std::numeric_limits<uint64_t>::max()) {
return 0;
}
}
return std::numeric_limits<uint64_t>::max();
}
vtable::VWeak<private_api::ItemTreeVTable> instance_at(std::size_t i) const
{
if (i != 0 || !instance) {
return {};
}
return vtable::VWeak<private_api::ItemTreeVTable> { instance->into_dyn() };
}
private_api::IndexRange index_range() const { return private_api::IndexRange { 0, len() }; }
std::size_t len() const { return instance ? 1 : 0; }
void for_each(auto &&f) const
{
if (instance) {
f(*instance);
}
}
};
} // namespace private_api