mirror of
https://github.com/slint-ui/slint.git
synced 2025-07-24 13:35:00 +00:00
Add a C++ Map Model class (#1687)
* Add a C++ Map Model class This matches the MapModel in the Rust API.
This commit is contained in:
parent
388cf32770
commit
cfbdce735f
3 changed files with 132 additions and 1 deletions
|
@ -7,7 +7,7 @@ All notable changes to this project are documented in this file.
|
|||
|
||||
### Added
|
||||
|
||||
- Added `slint::FilterModel` to the C++ API.
|
||||
- Added `slint::FilterModel` and `slint::MapModel` to the C++ API.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -982,6 +982,73 @@ private:
|
|||
std::shared_ptr<private_api::FilterModelInner<ModelData>> inner;
|
||||
};
|
||||
|
||||
template<typename SourceModelData, typename MappedModelData>
|
||||
class MapModel;
|
||||
|
||||
namespace private_api {
|
||||
template<typename SourceModelData, typename MappedModelData>
|
||||
struct MapModelInner : private_api::ModelChangeListener
|
||||
{
|
||||
MapModelInner(slint::MapModel<SourceModelData, MappedModelData> &target_model)
|
||||
: target_model(target_model)
|
||||
{
|
||||
}
|
||||
|
||||
void row_added(int index, int count) override { target_model.row_added(index, count); }
|
||||
void row_changed(int index) override { target_model.row_changed(index); }
|
||||
void row_removed(int index, int count) override { target_model.row_removed(index, count); }
|
||||
void reset() override { target_model.reset(); }
|
||||
|
||||
slint::MapModel<SourceModelData, MappedModelData> &target_model;
|
||||
};
|
||||
}
|
||||
|
||||
/// The MapModel acts as an adapter model for a given source model by applying a mapping
|
||||
/// function. The mapping function is called for each row on the source model and allows
|
||||
/// transforming the values on the fly. The MapModel has two template parameters: The
|
||||
/// SourceModelData specifies the data type of the underlying source model, and the
|
||||
/// MappedModelData the data type of this MapModel. This permits not only changing the
|
||||
/// values of the underlying source model, but also changing the data type itself. For
|
||||
/// example a MapModel can be used to adapt a model that provides numbers to be a model
|
||||
/// that exposes all numbers converted to strings, by calling `std::to_string` on each
|
||||
/// value given in the mapping lambda expression.
|
||||
template<typename SourceModelData, typename MappedModelData = SourceModelData>
|
||||
class MapModel : public Model<MappedModelData>
|
||||
{
|
||||
friend struct private_api::MapModelInner<SourceModelData, MappedModelData>;
|
||||
|
||||
public:
|
||||
/// Constructs a new MapModel that provides an altered view on the \a source_model by applying
|
||||
/// \a map_fn on the data in each row.
|
||||
MapModel(std::shared_ptr<Model<SourceModelData>> source_model,
|
||||
std::function<MappedModelData(const SourceModelData &)> map_fn)
|
||||
: inner(std::make_shared<private_api::MapModelInner<SourceModelData, MappedModelData>>(
|
||||
*this)),
|
||||
model(source_model),
|
||||
map_fn(map_fn)
|
||||
{
|
||||
model->attach_peer(inner);
|
||||
}
|
||||
|
||||
int row_count() const override { return model->row_count(); }
|
||||
|
||||
std::optional<MappedModelData> row_data(int i) const override
|
||||
{
|
||||
if (auto source_data = model->row_data(i))
|
||||
return map_fn(*source_data);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Returns the source model of this filter model.
|
||||
std::shared_ptr<Model<SourceModelData>> source_model() const { return model; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<private_api::MapModelInner<SourceModelData, MappedModelData>> inner;
|
||||
std::shared_ptr<slint::Model<SourceModelData>> model;
|
||||
std::function<MappedModelData(const SourceModelData &)> map_fn;
|
||||
};
|
||||
|
||||
namespace private_api {
|
||||
|
||||
template<typename C, typename ModelData>
|
||||
|
|
|
@ -217,3 +217,67 @@ SCENARIO("Filtering Model Remove")
|
|||
REQUIRE(even_rows->row_data(0) == 2);
|
||||
REQUIRE(even_rows->row_data(1) == 4);
|
||||
}
|
||||
|
||||
SCENARIO("Mapped Model")
|
||||
{
|
||||
auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4 });
|
||||
|
||||
auto plus_one_model = std::make_shared<slint::MapModel<int, int>>(
|
||||
vec_model, [](auto value) { return value + 1; });
|
||||
|
||||
auto observer = std::make_shared<ModelObserver>();
|
||||
plus_one_model->attach_peer(observer);
|
||||
|
||||
REQUIRE(plus_one_model->row_count() == 4);
|
||||
REQUIRE(plus_one_model->row_data(0) == 2);
|
||||
REQUIRE(plus_one_model->row_data(1) == 3);
|
||||
REQUIRE(plus_one_model->row_data(2) == 4);
|
||||
REQUIRE(plus_one_model->row_data(3) == 5);
|
||||
|
||||
vec_model->insert(0, 100);
|
||||
|
||||
REQUIRE(observer->added_rows.size() == 1);
|
||||
REQUIRE(observer->added_rows[0] == ModelObserver::Range { 0, 1 });
|
||||
REQUIRE(observer->changed_rows.empty());
|
||||
REQUIRE(observer->removed_rows.empty());
|
||||
REQUIRE(!observer->model_reset);
|
||||
observer->clear();
|
||||
|
||||
REQUIRE(plus_one_model->row_count() == 5);
|
||||
REQUIRE(plus_one_model->row_data(0) == 101);
|
||||
REQUIRE(plus_one_model->row_data(1) == 2);
|
||||
REQUIRE(plus_one_model->row_data(2) == 3);
|
||||
REQUIRE(plus_one_model->row_data(3) == 4);
|
||||
REQUIRE(plus_one_model->row_data(4) == 5);
|
||||
|
||||
vec_model->set_row_data(1, 3);
|
||||
|
||||
REQUIRE(observer->added_rows.empty());
|
||||
REQUIRE(observer->changed_rows.size() == 1);
|
||||
REQUIRE(observer->changed_rows[0] == 1);
|
||||
REQUIRE(observer->removed_rows.empty());
|
||||
REQUIRE(!observer->model_reset);
|
||||
observer->clear();
|
||||
|
||||
REQUIRE(plus_one_model->row_count() == 5);
|
||||
REQUIRE(plus_one_model->row_data(0) == 101);
|
||||
REQUIRE(plus_one_model->row_data(1) == 4);
|
||||
REQUIRE(plus_one_model->row_data(2) == 3);
|
||||
REQUIRE(plus_one_model->row_data(3) == 4);
|
||||
REQUIRE(plus_one_model->row_data(4) == 5);
|
||||
|
||||
vec_model->erase(3);
|
||||
|
||||
REQUIRE(observer->added_rows.empty());
|
||||
REQUIRE(observer->changed_rows.empty());
|
||||
REQUIRE(observer->removed_rows.size() == 1);
|
||||
REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 3, 1 });
|
||||
REQUIRE(!observer->model_reset);
|
||||
observer->clear();
|
||||
|
||||
REQUIRE(plus_one_model->row_count() == 4);
|
||||
REQUIRE(plus_one_model->row_data(0) == 101);
|
||||
REQUIRE(plus_one_model->row_data(1) == 4);
|
||||
REQUIRE(plus_one_model->row_data(2) == 3);
|
||||
REQUIRE(plus_one_model->row_data(3) == 5);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue