mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 04:45:13 +00:00
Refactor the C++ Repeater to do the same as the Rust one
This commit is contained in:
parent
2337eb097b
commit
f4f4775a19
3 changed files with 75 additions and 34 deletions
|
@ -262,7 +262,13 @@ using cbindgen_private::solve_grid_layout;
|
||||||
using cbindgen_private::solve_path_layout;
|
using cbindgen_private::solve_path_layout;
|
||||||
|
|
||||||
// models
|
// models
|
||||||
using ModelPeer = std::weak_ptr<bool>;
|
struct AbstractRepeaterView {
|
||||||
|
~AbstractRepeaterView() = default;
|
||||||
|
virtual void row_added(int index, int count) = 0;
|
||||||
|
virtual void row_removed(int index, int count) = 0;
|
||||||
|
virtual void row_changed(int index) = 0;
|
||||||
|
};
|
||||||
|
using ModelPeer = std::weak_ptr<AbstractRepeaterView>;
|
||||||
|
|
||||||
template<typename ModelData>
|
template<typename ModelData>
|
||||||
class Model
|
class Model
|
||||||
|
@ -291,29 +297,27 @@ protected:
|
||||||
/// Notify the views that a specific row was changed
|
/// Notify the views that a specific row was changed
|
||||||
void row_changed(int row)
|
void row_changed(int row)
|
||||||
{
|
{
|
||||||
(void)row;
|
for_each_peers([=](auto peer) { peer->row_changed(row); });
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
/// Notify the views that rows were added
|
/// Notify the views that rows were added
|
||||||
void row_added(int index, int count)
|
void row_added(int index, int count)
|
||||||
{
|
{
|
||||||
(void)(index + count);
|
for_each_peers([=](auto peer) { peer->row_added(index, count); });
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
/// Notify the views that rows were removed
|
/// Notify the views that rows were removed
|
||||||
void row_removed(int index, int count)
|
void row_removed(int index, int count)
|
||||||
{
|
{
|
||||||
(void)(index + count);
|
for_each_peers([=](auto peer) { peer->row_removed(index, count); });
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void notify()
|
template<typename F>
|
||||||
|
void for_each_peers(const F &f)
|
||||||
{
|
{
|
||||||
peers.erase(std::remove_if(peers.begin(), peers.end(),
|
peers.erase(std::remove_if(peers.begin(), peers.end(),
|
||||||
[](const auto &p) {
|
[&](const auto &p) {
|
||||||
if (auto pp = p.lock()) {
|
if (auto pp = p.lock()) {
|
||||||
*pp = true;
|
f(pp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -376,12 +380,34 @@ public:
|
||||||
template<typename C, typename ModelData>
|
template<typename C, typename ModelData>
|
||||||
class Repeater
|
class Repeater
|
||||||
{
|
{
|
||||||
mutable std::shared_ptr<bool> is_dirty;
|
|
||||||
Property<std::shared_ptr<Model<ModelData>>> model;
|
Property<std::shared_ptr<Model<ModelData>>> model;
|
||||||
|
|
||||||
|
struct RepeaterInner : AbstractRepeaterView {
|
||||||
|
enum class State { Clean, Dirty };
|
||||||
|
struct ComponentWithState {
|
||||||
|
State state = State::Dirty;
|
||||||
|
std::unique_ptr<C> ptr;
|
||||||
|
};
|
||||||
|
std::vector<ComponentWithState> data;
|
||||||
|
bool is_dirty = true;
|
||||||
|
|
||||||
|
void row_added(int index, int count) override {
|
||||||
|
is_dirty = true;
|
||||||
|
data.resize(data.size() + count);
|
||||||
|
std::rotate(data.begin() + index, data.end() - count, data.end());
|
||||||
|
}
|
||||||
|
void row_changed(int index) override {
|
||||||
|
is_dirty = true;
|
||||||
|
data[index].state = State::Dirty;
|
||||||
|
}
|
||||||
|
void row_removed(int index, int count) override {
|
||||||
|
data.erase(data.begin() + index, data.begin() + index + count);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// FIXME: should be private, but compute_layout uses it.
|
// FIXME: should be private, but compute_layout uses it.
|
||||||
std::vector<std::unique_ptr<C>> data;
|
mutable std::shared_ptr<RepeaterInner> inner;
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void set_model_binding(F &&binding) const
|
void set_model_binding(F &&binding) const
|
||||||
|
@ -393,30 +419,37 @@ public:
|
||||||
void ensure_updated(const Parent *parent) const
|
void ensure_updated(const Parent *parent) const
|
||||||
{
|
{
|
||||||
if (model.is_dirty()) {
|
if (model.is_dirty()) {
|
||||||
is_dirty = std::make_shared<bool>(true);
|
inner = std::make_shared<RepeaterInner>();
|
||||||
if (auto m = model.get()) {
|
if (auto m = model.get()) {
|
||||||
m->attach_peer(is_dirty);
|
m->attach_peer(inner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_dirty && *is_dirty) {
|
|
||||||
auto &data = const_cast<Repeater *>(this)->data;
|
if (inner && inner->is_dirty) {
|
||||||
data.clear();
|
inner->is_dirty = false;
|
||||||
auto m = model.get();
|
if (auto m = model.get()) {
|
||||||
auto count = m->row_count();
|
int count = m->row_count();
|
||||||
for (auto i = 0; i < count; ++i) {
|
inner->data.resize(count);
|
||||||
auto x = std::make_unique<C>();
|
for (int i = 0; i < count; ++i) {
|
||||||
x->parent = parent;
|
auto &c = inner->data[i];
|
||||||
x->update_data(i, m->row_data(i));
|
if (c.state == RepeaterInner::State::Dirty) {
|
||||||
data.push_back(std::move(x));
|
if (!c.ptr) {
|
||||||
|
c.ptr = std::make_unique<C>();
|
||||||
|
c.ptr->parent = parent;
|
||||||
|
}
|
||||||
|
c.ptr->update_data(i, m->row_data(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inner->data.clear();
|
||||||
}
|
}
|
||||||
*is_dirty = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
intptr_t visit(TraversalOrder order, private_api::ItemVisitorRefMut visitor) const
|
intptr_t visit(TraversalOrder order, private_api::ItemVisitorRefMut visitor) const
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < data.size(); ++i) {
|
for (std::size_t i = 0; i < inner->data.size(); ++i) {
|
||||||
int index = order == TraversalOrder::BackToFront ? i : data.size() - 1 - i;
|
int index = order == TraversalOrder::BackToFront ? i : inner->data.size() - 1 - i;
|
||||||
auto ref = item_at(index);
|
auto ref = item_at(index);
|
||||||
if (ref.vtable->visit_children_item(ref, -1, order, visitor) != -1) {
|
if (ref.vtable->visit_children_item(ref, -1, order, visitor) != -1) {
|
||||||
return index;
|
return index;
|
||||||
|
@ -427,14 +460,16 @@ public:
|
||||||
|
|
||||||
VRef<private_api::ComponentVTable> item_at(int i) const
|
VRef<private_api::ComponentVTable> item_at(int i) const
|
||||||
{
|
{
|
||||||
const auto &x = data.at(i);
|
const auto &x = inner->data.at(i);
|
||||||
return { &C::component_type, x.get() };
|
return { &C::component_type, x.ptr.get() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_layout() const
|
void compute_layout() const
|
||||||
{
|
{
|
||||||
for (auto &x : data) {
|
if (!inner)
|
||||||
x->compute_layout({ &C::component_type, x.get() });
|
return;
|
||||||
|
for (auto &x : inner->data) {
|
||||||
|
x.ptr->compute_layout({ &C::component_type, x.ptr.get() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1483,15 +1483,15 @@ impl<'a> LayoutTreeItem<'a> {
|
||||||
let root_element =
|
let root_element =
|
||||||
elem.borrow().base_type.as_component().root_element.clone();
|
elem.borrow().base_type.as_component().root_element.clone();
|
||||||
code_stream.push(format!(
|
code_stream.push(format!(
|
||||||
" for (auto &&sub_comp : self->repeater_{}.data)",
|
" for (auto &&sub_comp : self->repeater_{}.inner->data)",
|
||||||
elem.borrow().id
|
elem.borrow().id
|
||||||
));
|
));
|
||||||
code_stream.push(format!(
|
code_stream.push(format!(
|
||||||
" items.push_back({});",
|
" items.push_back({});",
|
||||||
path_layout_item_data(
|
path_layout_item_data(
|
||||||
&root_element,
|
&root_element,
|
||||||
&format!("sub_comp->{}", root_element.borrow().id),
|
&format!("sub_comp.ptr->{}", root_element.borrow().id),
|
||||||
"sub_comp",
|
"sub_comp.ptr",
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -99,9 +99,11 @@ TestCase instance;
|
||||||
// there should be nothing there
|
// there should be nothing there
|
||||||
sixtyfps::testing::send_mouse_click(instance, 25., 5.);
|
sixtyfps::testing::send_mouse_click(instance, 25., 5.);
|
||||||
assert_eq(instance.get_clicked_score(), 0);
|
assert_eq(instance.get_clicked_score(), 0);
|
||||||
|
assert_eq(instance.get_clicked_internal_state(), 0);
|
||||||
|
|
||||||
sixtyfps::testing::send_mouse_click(instance, 15., 5.);
|
sixtyfps::testing::send_mouse_click(instance, 15., 5.);
|
||||||
assert_eq(instance.get_clicked_score(), 789000);
|
assert_eq(instance.get_clicked_score(), 789000);
|
||||||
|
assert_eq(instance.get_clicked_internal_state(), 1);
|
||||||
|
|
||||||
using ModelData = std::tuple<sixtyfps::SharedString, sixtyfps::SharedString, float>;
|
using ModelData = std::tuple<sixtyfps::SharedString, sixtyfps::SharedString, float>;
|
||||||
sixtyfps::SharedArray<ModelData> array;
|
sixtyfps::SharedArray<ModelData> array;
|
||||||
|
@ -113,20 +115,24 @@ instance.set_model(another_model);
|
||||||
|
|
||||||
sixtyfps::testing::send_mouse_click(instance, 25., 5.);
|
sixtyfps::testing::send_mouse_click(instance, 25., 5.);
|
||||||
assert_eq(instance.get_clicked_score(), 333000);
|
assert_eq(instance.get_clicked_score(), 333000);
|
||||||
|
assert_eq(instance.get_clicked_internal_state(), 1);
|
||||||
|
|
||||||
sixtyfps::testing::send_mouse_click(instance, 15., 5.);
|
sixtyfps::testing::send_mouse_click(instance, 15., 5.);
|
||||||
assert_eq(instance.get_clicked_score(), 222000);
|
assert_eq(instance.get_clicked_score(), 222000);
|
||||||
assert_eq(instance.get_clicked_name(), "cruel");
|
assert_eq(instance.get_clicked_name(), "cruel");
|
||||||
|
assert_eq(instance.get_clicked_internal_state(), 1);
|
||||||
|
|
||||||
another_model->push_back({"a4", "!", 444.});
|
another_model->push_back({"a4", "!", 444.});
|
||||||
sixtyfps::testing::send_mouse_click(instance, 35., 5.);
|
sixtyfps::testing::send_mouse_click(instance, 35., 5.);
|
||||||
assert_eq(instance.get_clicked_score(), 444000);
|
assert_eq(instance.get_clicked_score(), 444000);
|
||||||
assert_eq(instance.get_clicked_name(), "!");
|
assert_eq(instance.get_clicked_name(), "!");
|
||||||
|
assert_eq(instance.get_clicked_internal_state(), 1);
|
||||||
|
|
||||||
another_model->set_row_data(1, {"a2", "idyllic", 555.});
|
another_model->set_row_data(1, {"a2", "idyllic", 555.});
|
||||||
sixtyfps::testing::send_mouse_click(instance, 15., 5.);
|
sixtyfps::testing::send_mouse_click(instance, 15., 5.);
|
||||||
assert_eq(instance.get_clicked_score(), 555000);
|
assert_eq(instance.get_clicked_score(), 555000);
|
||||||
assert_eq(instance.get_clicked_name(), "idyllic");
|
assert_eq(instance.get_clicked_name(), "idyllic");
|
||||||
|
assert_eq(instance.get_clicked_internal_state(), 2);
|
||||||
```
|
```
|
||||||
|
|
||||||
*/
|
*/
|
Loading…
Add table
Add a link
Reference in a new issue