diff --git a/api/sixtyfps-cpp/include/sixtyfps_interpreter.h b/api/sixtyfps-cpp/include/sixtyfps_interpreter.h index 10be2ca4a..4cedc2602 100644 --- a/api/sixtyfps-cpp/include/sixtyfps_interpreter.h +++ b/api/sixtyfps-cpp/include/sixtyfps_interpreter.h @@ -73,6 +73,9 @@ public: iterator end() const; #endif + inline std::optional get_field(std::string_view name) const; + inline void set_field(std::string_view name, const Value &value); + // internal Struct(const sixtyfps::cbindgen_private::StructOpaque &other) { @@ -117,11 +120,6 @@ public: using Type = cbindgen_private::ValueType; - // only works on Type::Struct - std::optional get_field(std::string_view) const; - // only works on Type::Struct - bool set_field(std::string_view, Value); // returns false if Value is not a Struct - // optional to_int() const; // optional to_float() const; std::optional to_number() const @@ -187,9 +185,13 @@ public: Type type() const { return cbindgen_private::sixtyfps_interpreter_value_type(&inner); } + // internal + Value(const sixtyfps::cbindgen_private::ValueOpaque &inner) : inner(inner) { } + private: using ValueOpaque = sixtyfps::cbindgen_private::ValueOpaque; ValueOpaque inner; + friend class Struct; }; inline Value::Value(const sixtyfps::SharedVector &array) @@ -206,17 +208,18 @@ inline std::optional> Value::to_array() const return {}; } } -inline Value::Value(const std::shared_ptr> &model) { +inline Value::Value(const std::shared_ptr> &model) +{ using cbindgen_private::ModelAdaptorVTable; using vtable::VRef; - struct ModelWrapper : AbstractRepeaterView { + struct ModelWrapper : AbstractRepeaterView + { std::shared_ptr> model; cbindgen_private::ModelNotifyOpaque notify; - // This kind of mean that the rust code has ownership of "this" until the drop funciton is called + // This kind of mean that the rust code has ownership of "this" until the drop funciton is + // called std::shared_ptr self; - ~ModelWrapper() { - cbindgen_private::sixtyfps_interpreter_model_notify_destructor(¬ify); - } + ~ModelWrapper() { cbindgen_private::sixtyfps_interpreter_model_notify_destructor(¬ify); } void row_added(int index, int count) override { @@ -239,27 +242,49 @@ inline Value::Value(const std::shared_ptr> &model) { model->attach_peer(wrapper); auto row_count = [](VRef self) -> uintptr_t { - return reinterpret_cast(self.instance)->model->row_count(); + return reinterpret_cast(self.instance)->model->row_count(); }; auto row_data = [](VRef self, uintptr_t row, ValueOpaque *out) { - Value v = reinterpret_cast(self.instance)->model->row_data(row); + Value v = reinterpret_cast(self.instance)->model->row_data(row); *out = v.inner; cbindgen_private::sixtyfps_interpreter_value_new(&v.inner); }; auto set_row_data = [](VRef self, uintptr_t row, const ValueOpaque *value) { - Value v = *reinterpret_cast(value); - reinterpret_cast(self.instance)->model->set_row_data(row, v); + Value v = *reinterpret_cast(value); + reinterpret_cast(self.instance)->model->set_row_data(row, v); }; - auto get_notify = [](VRef self) -> const cbindgen_private::ModelNotifyOpaque* { - return &reinterpret_cast(self.instance)->notify; + auto get_notify = + [](VRef self) -> const cbindgen_private::ModelNotifyOpaque * { + return &reinterpret_cast(self.instance)->notify; }; auto drop = [](vtable::VRefMut self) { - reinterpret_cast(self.instance)->self = nullptr; + reinterpret_cast(self.instance)->self = nullptr; }; static const ModelAdaptorVTable vt { row_count, row_data, set_row_data, get_notify, drop }; - vtable::VBox wrap{ &vt, wrapper.get() }; + vtable::VBox wrap { &vt, wrapper.get() }; cbindgen_private::sixtyfps_interpreter_value_new_model(wrap, &inner); } +inline std::optional Struct::get_field(std::string_view name) const +{ + cbindgen_private::Slice name_view { + const_cast(reinterpret_cast(name.data())), + name.size() + }; + if (auto *value = cbindgen_private::sixtyfps_interpreter_struct_get_field(&inner, name_view)) { + return *value; + } else { + return {}; + } } +inline void Struct::set_field(std::string_view name, const Value &value) +{ + cbindgen_private::Slice name_view { + const_cast(reinterpret_cast(name.data())), + name.size() + }; + cbindgen_private::sixtyfps_interpreter_struct_set_field(&inner, name_view, &value.inner); +} + +} \ No newline at end of file diff --git a/api/sixtyfps-cpp/tests/tests.cpp b/api/sixtyfps-cpp/tests/tests.cpp index 367c04614..a09fd4bfc 100644 --- a/api/sixtyfps-cpp/tests/tests.cpp +++ b/api/sixtyfps-cpp/tests/tests.cpp @@ -128,10 +128,12 @@ SCENARIO("Value API") SECTION("Construct a model") { // And test that it is properly destroyed when the value is destroyed - struct M : sixtyfps::VectorModel { + struct M : sixtyfps::VectorModel + { bool *destroyed; - explicit M(bool *destroyed) : destroyed(destroyed) {} - void play() { + explicit M(bool *destroyed) : destroyed(destroyed) { } + void play() + { this->push_back(Value(4.)); this->set_row_data(0, Value(9.)); } @@ -155,3 +157,18 @@ SCENARIO("Value API") } } +SCENARIO("Struct API") +{ + using namespace sixtyfps::interpreter; + Struct struc; + + REQUIRE(!struc.get_field("not_there")); + + struc.set_field("field_a", Value(true)); + + auto value_opt = struc.get_field("field_a"); + REQUIRE(value_opt.has_value()); + auto value = value_opt.value(); + REQUIRE(value.to_bool().has_value()); + REQUIRE(value.to_bool().value() == true); +} \ No newline at end of file