mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
C++ API to put a model in the interpreter::Value
This commit is contained in:
parent
02c1150fa7
commit
3335ff8da5
4 changed files with 189 additions and 3 deletions
|
@ -206,4 +206,60 @@ inline std::optional<sixtyfps::SharedVector<Value>> Value::to_array() const
|
|||
return {};
|
||||
}
|
||||
}
|
||||
inline Value::Value(const std::shared_ptr<sixtyfps::Model<Value>> &model) {
|
||||
using cbindgen_private::ModelAdaptorVTable;
|
||||
using vtable::VRef;
|
||||
struct ModelWrapper : AbstractRepeaterView {
|
||||
std::shared_ptr<sixtyfps::Model<Value>> model;
|
||||
cbindgen_private::ModelNotifyOpaque notify;
|
||||
// This kind of mean that the rust code has ownership of "this" until the drop funciton is called
|
||||
std::shared_ptr<AbstractRepeaterView> self;
|
||||
~ModelWrapper() {
|
||||
cbindgen_private::sixtyfps_interpreter_model_notify_destructor(¬ify);
|
||||
}
|
||||
|
||||
void row_added(int index, int count) override
|
||||
{
|
||||
cbindgen_private::sixtyfps_interpreter_model_notify_row_added(¬ify, index, count);
|
||||
}
|
||||
void row_changed(int index) override
|
||||
{
|
||||
cbindgen_private::sixtyfps_interpreter_model_notify_row_changed(¬ify, index);
|
||||
}
|
||||
void row_removed(int index, int count) override
|
||||
{
|
||||
cbindgen_private::sixtyfps_interpreter_model_notify_row_removed(¬ify, index, count);
|
||||
}
|
||||
};
|
||||
|
||||
auto wrapper = std::make_shared<ModelWrapper>();
|
||||
wrapper->model = model;
|
||||
wrapper->self = wrapper;
|
||||
cbindgen_private::sixtyfps_interpreter_model_notify_new(&wrapper->notify);
|
||||
model->attach_peer(wrapper);
|
||||
|
||||
auto row_count = [](VRef<ModelAdaptorVTable> self) -> uintptr_t {
|
||||
return reinterpret_cast<ModelWrapper*>(self.instance)->model->row_count();
|
||||
};
|
||||
auto row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row, ValueOpaque *out) {
|
||||
Value v = reinterpret_cast<ModelWrapper*>(self.instance)->model->row_data(row);
|
||||
*out = v.inner;
|
||||
cbindgen_private::sixtyfps_interpreter_value_new(&v.inner);
|
||||
};
|
||||
auto set_row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row, const ValueOpaque *value) {
|
||||
Value v = *reinterpret_cast<const Value*>(value);
|
||||
reinterpret_cast<ModelWrapper*>(self.instance)->model->set_row_data(row, v);
|
||||
};
|
||||
auto get_notify = [](VRef<ModelAdaptorVTable> self) -> const cbindgen_private::ModelNotifyOpaque* {
|
||||
return &reinterpret_cast<ModelWrapper*>(self.instance)->notify;
|
||||
};
|
||||
auto drop = [](vtable::VRefMut<ModelAdaptorVTable> self) {
|
||||
reinterpret_cast<ModelWrapper*>(self.instance)->self = nullptr;
|
||||
};
|
||||
|
||||
static const ModelAdaptorVTable vt { row_count, row_data, set_row_data, get_notify, drop };
|
||||
vtable::VBox<ModelAdaptorVTable> wrap{ &vt, wrapper.get() };
|
||||
cbindgen_private::sixtyfps_interpreter_value_new_model(wrap, &inner);
|
||||
}
|
||||
|
||||
}
|
|
@ -124,4 +124,34 @@ SCENARIO("Value API")
|
|||
auto struct_opt = value.to_struct();
|
||||
REQUIRE(struct_opt.has_value());
|
||||
}
|
||||
|
||||
SECTION("Construct a model")
|
||||
{
|
||||
// And test that it is properly destroyed when the value is destroyed
|
||||
struct M : sixtyfps::VectorModel<Value> {
|
||||
bool *destroyed;
|
||||
explicit M(bool *destroyed) : destroyed(destroyed) {}
|
||||
void play() {
|
||||
this->push_back(Value(4.));
|
||||
this->set_row_data(0, Value(9.));
|
||||
}
|
||||
~M() { *destroyed = true; }
|
||||
};
|
||||
bool destroyed = false;
|
||||
auto m = std::make_shared<M>(&destroyed);
|
||||
{
|
||||
Value value(m);
|
||||
REQUIRE(value.type() == Value::Type::Model);
|
||||
REQUIRE(!destroyed);
|
||||
m->play();
|
||||
m = nullptr;
|
||||
REQUIRE(!destroyed);
|
||||
// play a bit with the value to test the copy and move
|
||||
Value v2 = value;
|
||||
Value v3 = std::move(v2);
|
||||
REQUIRE(!destroyed);
|
||||
}
|
||||
REQUIRE(destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ impl Inner {
|
|||
///
|
||||
/// The VBox implements Deref so one can access all the members of the vtable.
|
||||
///
|
||||
/// This is only valid if the VTable has a `drop` type (so that the `#[vtable]` macro
|
||||
/// This is only valid if the VTable has a `drop` function (so that the `#[vtable]` macro
|
||||
/// implements the `VTableMetaDrop` trait for it)
|
||||
#[repr(transparent)]
|
||||
pub struct VBox<T: ?Sized + VTableMetaDrop> {
|
||||
|
|
|
@ -744,8 +744,10 @@ pub mod testing {
|
|||
#[allow(missing_docs)]
|
||||
pub(crate) mod ffi {
|
||||
use super::*;
|
||||
use sixtyfps_corelib::model::{Model, ModelNotify, ModelPeer};
|
||||
use sixtyfps_corelib::slice::Slice;
|
||||
use std::ffi::c_void;
|
||||
use vtable::VRef;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ValueOpaque([usize; 7]);
|
||||
|
@ -841,6 +843,15 @@ pub(crate) mod ffi {
|
|||
std::ptr::write(val as *mut Value, Value::Struct(struc.as_struct().clone()))
|
||||
}
|
||||
|
||||
/// Construct a new Value containing a model in the given memory location
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_interpreter_value_new_model(
|
||||
model: vtable::VBox<ModelAdaptorVTable>,
|
||||
val: *mut ValueOpaque,
|
||||
) {
|
||||
std::ptr::write(val as *mut Value, Value::Model(Rc::new(ModelAdaptorWrapper(model))))
|
||||
}
|
||||
|
||||
#[repr(i8)]
|
||||
pub enum ValueType {
|
||||
Void,
|
||||
|
@ -1114,4 +1125,93 @@ pub(crate) mod ffi {
|
|||
|
||||
inst.set_callback(std::str::from_utf8(&name).unwrap(), callback).is_ok()
|
||||
}
|
||||
|
||||
#[vtable::vtable]
|
||||
#[repr(C)]
|
||||
pub struct ModelAdaptorVTable {
|
||||
pub row_count: extern "C" fn(VRef<ModelAdaptorVTable>) -> usize,
|
||||
pub row_data:
|
||||
unsafe extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, out: *mut ValueOpaque),
|
||||
pub set_row_data: extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, value: &ValueOpaque),
|
||||
pub get_notify: extern "C" fn(VRef<ModelAdaptorVTable>) -> &ModelNotifyOpaque,
|
||||
pub drop: extern "C" fn(VRefMut<ModelAdaptorVTable>),
|
||||
}
|
||||
|
||||
struct ModelAdaptorWrapper(vtable::VBox<ModelAdaptorVTable>);
|
||||
impl Model for ModelAdaptorWrapper {
|
||||
type Data = Value;
|
||||
|
||||
fn row_count(&self) -> usize {
|
||||
self.0.row_count()
|
||||
}
|
||||
|
||||
fn row_data(&self, row: usize) -> Value {
|
||||
unsafe {
|
||||
let mut v = std::mem::MaybeUninit::<Value>::uninit();
|
||||
self.0.row_data(row, v.as_mut_ptr() as *mut ValueOpaque);
|
||||
v.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
fn attach_peer(&self, peer: ModelPeer) {
|
||||
self.0.get_notify().as_model_notify().attach(peer);
|
||||
}
|
||||
|
||||
fn set_row_data(&self, row: usize, data: Value) {
|
||||
let val: &ValueOpaque = unsafe { std::mem::transmute::<&Value, &ValueOpaque>(&data) };
|
||||
self.0.set_row_data(row, val);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ModelNotifyOpaque([usize; 6]);
|
||||
/// Asserts that ModelNotifyOpaque is at least as large as ModelNotify, otherwise this would overflow
|
||||
const _: usize = std::mem::size_of::<ModelNotifyOpaque>() - std::mem::size_of::<ModelNotify>();
|
||||
|
||||
impl ModelNotifyOpaque {
|
||||
fn as_model_notify(&self) -> &ModelNotify {
|
||||
// Safety: there should be no way to construct a ModelNotifyOpaque without it holding an actual ModelNotify
|
||||
unsafe { std::mem::transmute::<&ModelNotifyOpaque, &ModelNotify>(self) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new ModelNotifyNotify in the given memory region
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_interpreter_model_notify_new(val: *mut ModelNotifyOpaque) {
|
||||
std::ptr::write(val as *mut ModelNotify, ModelNotify::default());
|
||||
}
|
||||
|
||||
/// Destruct the value in that memory location
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_interpreter_model_notify_destructor(
|
||||
val: *mut ModelNotifyOpaque,
|
||||
) {
|
||||
drop(std::ptr::read(val as *mut ModelNotify))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_interpreter_model_notify_row_changed(
|
||||
notify: &ModelNotifyOpaque,
|
||||
row: usize,
|
||||
) {
|
||||
notify.as_model_notify().row_changed(row);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_interpreter_model_notify_row_added(
|
||||
notify: &ModelNotifyOpaque,
|
||||
row: usize,
|
||||
count: usize,
|
||||
) {
|
||||
notify.as_model_notify().row_added(row, count);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sixtyfps_interpreter_model_notify_row_removed(
|
||||
notify: &ModelNotifyOpaque,
|
||||
row: usize,
|
||||
count: usize,
|
||||
) {
|
||||
notify.as_model_notify().row_removed(row, count);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue