Add support for tracking the length of a model in C++

Similar to the parent commit, the model tracks changes to the rows and
marks an internal property dirty. Since we have a base class this is a
little less intrusive.

cc #98
This commit is contained in:
Simon Hausmann 2021-10-20 14:11:16 +02:00 committed by Simon Hausmann
parent 63bf1af093
commit 7d12fd7b4e
6 changed files with 49 additions and 2 deletions

View file

@ -456,6 +456,11 @@ public:
/// Internal function called by the view to register itself
void attach_peer(private_api::ModelPeer p) { peers.push_back(std::move(p)); }
/// \private
/// Internal function called from within bindings to register with the currently
/// evaluating dependency and get notified when this model's row count changes.
void track_row_count_changes() { model_dirty_property.get(); }
protected:
/// Notify the views that a specific row was changed
void row_changed(int row)
@ -465,11 +470,13 @@ protected:
/// Notify the views that rows were added
void row_added(int index, int count)
{
model_dirty_property.set_dirty();
for_each_peers([=](auto peer) { peer->row_added(index, count); });
}
/// Notify the views that rows were removed
void row_removed(int index, int count)
{
model_dirty_property.set_dirty();
for_each_peers([=](auto peer) { peer->row_removed(index, count); });
}
@ -489,6 +496,7 @@ private:
peers.end());
}
std::vector<private_api::ModelPeer> peers;
private_api::Property<bool> model_dirty_property;
};
namespace private_api {

View file

@ -142,6 +142,7 @@ struct Property
}
bool is_dirty() const { return cbindgen_private::sixtyfps_property_is_dirty(&inner); }
void set_dirty() const { cbindgen_private::sixtyfps_property_set_dirty(&inner); }
static void link_two_way(const Property<T> *p1, const Property<T> *p2)
{

View file

@ -73,6 +73,35 @@ TEST_CASE("Property Tracker")
REQUIRE(!tracker1.is_dirty());
}
TEST_CASE("Model row changes")
{
using namespace sixtyfps::private_api;
auto model = std::make_shared<sixtyfps::VectorModel<int>>();
PropertyTracker tracker;
REQUIRE(tracker.evaluate([&]() {
model->track_row_count_changes();
return model->row_count();
}) == 0);
REQUIRE(!tracker.is_dirty());
model->push_back(1);
model->push_back(2);
REQUIRE(tracker.is_dirty());
REQUIRE(tracker.evaluate([&]() {
model->track_row_count_changes();
return model->row_count();
}) == 2);
REQUIRE(!tracker.is_dirty());
model->erase(0);
REQUIRE(tracker.is_dirty());
REQUIRE(tracker.evaluate([&]() {
model->track_row_count_changes();
return model->row_count();
}) == 1);
}
TEST_CASE("Image")
{
using namespace sixtyfps;

View file

@ -1628,7 +1628,7 @@ fn compile_expression(
"[](const sixtyfps::Image &img) { return img.size(); }".into()
}
BuiltinFunction::ArrayLength => {
"[](const auto &model) { return (*model).row_count(); }".into()
"[](const auto &model) { (*model).track_row_count_changes(); return (*model).row_count(); }".into()
}
BuiltinFunction::Rgb => {
"[](int r, int g, int b, float a) {{ return sixtyfps::Color::from_argb_uint8(std::clamp(a * 255., 0., 255.), std::clamp(r, 0, 255), std::clamp(g, 0, 255), std::clamp(b, 0, 255)); }}".into()

View file

@ -1815,6 +1815,12 @@ pub(crate) mod ffi {
handle.0.access(|binding| binding.map_or(false, |b| b.dirty.get()))
}
/// Marks the property as dirty and notifies dependencies.
#[no_mangle]
pub extern "C" fn sixtyfps_property_set_dirty(handle: &PropertyHandleOpaque) {
handle.0.mark_dirty()
}
/// Destroy handle
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_drop(handle: *mut PropertyHandleOpaque) {

View file

@ -20,8 +20,11 @@ const TestCase &instance = *handle;
assert_eq(instance.get_num_ints(), 5);
instance.set_ints(std::make_shared<sixtyfps::VectorModel<int>>(std::vector<int>{1, 2, 3, 4, 5, 6, 7}));
auto model = std::make_shared<sixtyfps::VectorModel<int>>(std::vector<int>{1, 2, 3, 4, 5, 6, 7});
instance.set_ints(model);
assert_eq(instance.get_num_ints(), 7);
model->push_back(8);
assert_eq(instance.get_num_ints(), 8);
```