slint_interpreter/
ffi.rs

1// Copyright © SixtyFPS GmbH <[email protected]>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::dynamic_item_tree::ErasedItemTreeBox;
5
6use super::*;
7use core::ptr::NonNull;
8use i_slint_core::model::{Model, ModelNotify, SharedVectorModel};
9use i_slint_core::slice::Slice;
10use i_slint_core::window::WindowAdapter;
11use std::ffi::c_void;
12use vtable::VRef;
13
14/// Construct a new Value in the given memory location
15#[unsafe(no_mangle)]
16pub unsafe extern "C" fn slint_interpreter_value_new() -> Box<Value> {
17    Box::new(Value::default())
18}
19
20/// Construct a new Value in the given memory location
21#[unsafe(no_mangle)]
22pub unsafe extern "C" fn slint_interpreter_value_clone(other: &Value) -> Box<Value> {
23    Box::new(other.clone())
24}
25
26/// Destruct the value in that memory location
27#[unsafe(no_mangle)]
28pub unsafe extern "C" fn slint_interpreter_value_destructor(val: Box<Value>) {
29    drop(val);
30}
31
32#[unsafe(no_mangle)]
33pub extern "C" fn slint_interpreter_value_eq(a: &Value, b: &Value) -> bool {
34    a == b
35}
36
37/// Construct a new Value in the given memory location as string
38#[unsafe(no_mangle)]
39pub unsafe extern "C" fn slint_interpreter_value_new_string(str: &SharedString) -> Box<Value> {
40    Box::new(Value::String(str.clone()))
41}
42
43/// Construct a new Value in the given memory location as double
44#[unsafe(no_mangle)]
45pub unsafe extern "C" fn slint_interpreter_value_new_double(double: f64) -> Box<Value> {
46    Box::new(Value::Number(double))
47}
48
49/// Construct a new Value in the given memory location as bool
50#[unsafe(no_mangle)]
51pub unsafe extern "C" fn slint_interpreter_value_new_bool(b: bool) -> Box<Value> {
52    Box::new(Value::Bool(b))
53}
54
55/// Construct a new Value in the given memory location as array model
56#[unsafe(no_mangle)]
57pub unsafe extern "C" fn slint_interpreter_value_new_array_model(
58    a: &SharedVector<Box<Value>>,
59) -> Box<Value> {
60    let vec = a.iter().map(|vb| vb.as_ref().clone()).collect::<SharedVector<_>>();
61    Box::new(Value::Model(ModelRc::new(SharedVectorModel::from(vec))))
62}
63
64/// Construct a new Value in the given memory location as Brush
65#[unsafe(no_mangle)]
66pub unsafe extern "C" fn slint_interpreter_value_new_brush(brush: &Brush) -> Box<Value> {
67    Box::new(Value::Brush(brush.clone()))
68}
69
70/// Construct a new Value in the given memory location as Struct
71#[unsafe(no_mangle)]
72pub unsafe extern "C" fn slint_interpreter_value_new_struct(struc: &StructOpaque) -> Box<Value> {
73    Box::new(Value::Struct(struc.as_struct().clone()))
74}
75
76/// Construct a new Value in the given memory location as image
77#[unsafe(no_mangle)]
78pub unsafe extern "C" fn slint_interpreter_value_new_image(img: &Image) -> Box<Value> {
79    Box::new(Value::Image(img.clone()))
80}
81
82/// Construct a new Value containing a model in the given memory location
83#[unsafe(no_mangle)]
84pub unsafe extern "C" fn slint_interpreter_value_new_model(
85    model: NonNull<u8>,
86    vtable: &ModelAdaptorVTable,
87) -> Box<Value> {
88    Box::new(Value::Model(ModelRc::new(ModelAdaptorWrapper(vtable::VBox::from_raw(
89        NonNull::from(vtable),
90        model,
91    )))))
92}
93
94#[unsafe(no_mangle)]
95pub unsafe extern "C" fn slint_interpreter_value_type(val: &Value) -> ValueType {
96    val.value_type()
97}
98
99#[unsafe(no_mangle)]
100pub extern "C" fn slint_interpreter_value_to_string(val: &Value) -> Option<&SharedString> {
101    match val {
102        Value::String(v) => Some(v),
103        _ => None,
104    }
105}
106
107#[unsafe(no_mangle)]
108pub extern "C" fn slint_interpreter_value_to_number(val: &Value) -> Option<&f64> {
109    match val {
110        Value::Number(v) => Some(v),
111        _ => None,
112    }
113}
114
115#[unsafe(no_mangle)]
116pub extern "C" fn slint_interpreter_value_to_bool(val: &Value) -> Option<&bool> {
117    match val {
118        Value::Bool(v) => Some(v),
119        _ => None,
120    }
121}
122
123/// Extracts a `SharedVector<ValueOpaque>` out of the given value `val`, writes that into the
124/// `out` parameter and returns true; returns false if the value does not hold an extractable
125/// array.
126#[unsafe(no_mangle)]
127pub extern "C" fn slint_interpreter_value_to_array(
128    val: &Box<Value>,
129    out: *mut SharedVector<Box<Value>>,
130) -> bool {
131    match val.as_ref() {
132        Value::Model(m) => {
133            let vec = m.iter().map(|vb| Box::new(vb)).collect::<SharedVector<_>>();
134            unsafe {
135                std::ptr::write(out, vec);
136            }
137
138            true
139        }
140        _ => false,
141    }
142}
143
144#[unsafe(no_mangle)]
145pub extern "C" fn slint_interpreter_value_to_brush(val: &Value) -> Option<&Brush> {
146    match val {
147        Value::Brush(b) => Some(b),
148        _ => None,
149    }
150}
151
152#[unsafe(no_mangle)]
153pub extern "C" fn slint_interpreter_value_to_struct(val: &Value) -> *const StructOpaque {
154    match val {
155        Value::Struct(s) => s as *const Struct as *const StructOpaque,
156        _ => std::ptr::null(),
157    }
158}
159
160#[unsafe(no_mangle)]
161pub extern "C" fn slint_interpreter_value_to_image(val: &Value) -> Option<&Image> {
162    match val {
163        Value::Image(img) => Some(img),
164        _ => None,
165    }
166}
167
168#[repr(C)]
169#[cfg(target_pointer_width = "64")]
170pub struct StructOpaque([usize; 6]);
171#[repr(C)]
172#[cfg(target_pointer_width = "32")]
173pub struct StructOpaque([u64; 4]);
174const _: [(); std::mem::size_of::<StructOpaque>()] = [(); std::mem::size_of::<Struct>()];
175const _: [(); std::mem::align_of::<StructOpaque>()] = [(); std::mem::align_of::<Struct>()];
176
177impl StructOpaque {
178    fn as_struct(&self) -> &Struct {
179        // Safety: there should be no way to construct a StructOpaque without it holding an actual Struct
180        unsafe { std::mem::transmute::<&StructOpaque, &Struct>(self) }
181    }
182    fn as_struct_mut(&mut self) -> &mut Struct {
183        // Safety: there should be no way to construct a StructOpaque without it holding an actual Struct
184        unsafe { std::mem::transmute::<&mut StructOpaque, &mut Struct>(self) }
185    }
186}
187
188/// Construct a new Struct in the given memory location
189#[unsafe(no_mangle)]
190pub unsafe extern "C" fn slint_interpreter_struct_new(val: *mut StructOpaque) {
191    std::ptr::write(val as *mut Struct, Struct::default())
192}
193
194/// Construct a new Struct in the given memory location
195#[unsafe(no_mangle)]
196pub unsafe extern "C" fn slint_interpreter_struct_clone(
197    other: &StructOpaque,
198    val: *mut StructOpaque,
199) {
200    std::ptr::write(val as *mut Struct, other.as_struct().clone())
201}
202
203/// Destruct the struct in that memory location
204#[unsafe(no_mangle)]
205pub unsafe extern "C" fn slint_interpreter_struct_destructor(val: *mut StructOpaque) {
206    drop(std::ptr::read(val as *mut Struct))
207}
208
209#[unsafe(no_mangle)]
210pub extern "C" fn slint_interpreter_struct_get_field(
211    stru: &StructOpaque,
212    name: Slice<u8>,
213) -> *mut Value {
214    if let Some(value) = stru.as_struct().get_field(std::str::from_utf8(&name).unwrap()) {
215        Box::into_raw(Box::new(value.clone()))
216    } else {
217        std::ptr::null_mut()
218    }
219}
220
221#[unsafe(no_mangle)]
222pub extern "C" fn slint_interpreter_struct_set_field<'a>(
223    stru: &'a mut StructOpaque,
224    name: Slice<u8>,
225    value: &Value,
226) {
227    stru.as_struct_mut().set_field(std::str::from_utf8(&name).unwrap().into(), value.clone())
228}
229
230type StructIterator<'a> = std::collections::hash_map::Iter<'a, SmolStr, Value>;
231#[repr(C)]
232pub struct StructIteratorOpaque<'a>([usize; 5], std::marker::PhantomData<StructIterator<'a>>);
233const _: [(); std::mem::size_of::<StructIteratorOpaque>()] =
234    [(); std::mem::size_of::<StructIterator>()];
235const _: [(); std::mem::align_of::<StructIteratorOpaque>()] =
236    [(); std::mem::align_of::<StructIterator>()];
237
238#[unsafe(no_mangle)]
239pub unsafe extern "C" fn slint_interpreter_struct_iterator_destructor(
240    val: *mut StructIteratorOpaque,
241) {
242    drop(std::ptr::read(val as *mut StructIterator))
243}
244
245/// Advance the iterator and return the next value, or a null pointer
246#[unsafe(no_mangle)]
247pub unsafe extern "C" fn slint_interpreter_struct_iterator_next<'a>(
248    iter: &'a mut StructIteratorOpaque,
249    k: &mut Slice<'a, u8>,
250) -> *mut Value {
251    if let Some((str, val)) = (*(iter as *mut StructIteratorOpaque as *mut StructIterator)).next() {
252        *k = Slice::from_slice(str.as_bytes());
253        Box::into_raw(Box::new(val.clone()))
254    } else {
255        *k = Slice::default();
256        std::ptr::null_mut()
257    }
258}
259
260#[unsafe(no_mangle)]
261pub extern "C" fn slint_interpreter_struct_make_iter(
262    stru: &StructOpaque,
263) -> StructIteratorOpaque<'_> {
264    let ret_it: StructIterator = stru.as_struct().0.iter();
265    unsafe {
266        let mut r = std::mem::MaybeUninit::<StructIteratorOpaque>::uninit();
267        std::ptr::write(r.as_mut_ptr() as *mut StructIterator, ret_it);
268        r.assume_init()
269    }
270}
271
272/// Get a property. Returns a null pointer if the property does not exist.
273#[unsafe(no_mangle)]
274pub unsafe extern "C" fn slint_interpreter_component_instance_get_property(
275    inst: &ErasedItemTreeBox,
276    name: Slice<u8>,
277) -> *mut Value {
278    generativity::make_guard!(guard);
279    let comp = inst.unerase(guard);
280    match comp
281        .description()
282        .get_property(comp.borrow(), &normalize_identifier(std::str::from_utf8(&name).unwrap()))
283    {
284        Ok(val) => Box::into_raw(Box::new(val)),
285        Err(_) => std::ptr::null_mut(),
286    }
287}
288
289#[unsafe(no_mangle)]
290pub extern "C" fn slint_interpreter_component_instance_set_property(
291    inst: &ErasedItemTreeBox,
292    name: Slice<u8>,
293    val: &Value,
294) -> bool {
295    generativity::make_guard!(guard);
296    let comp = inst.unerase(guard);
297    comp.description()
298        .set_property(
299            comp.borrow(),
300            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
301            val.clone(),
302        )
303        .is_ok()
304}
305
306/// Invoke a callback or function. Returns raw boxed value on success and null ptr on failure.
307#[unsafe(no_mangle)]
308pub unsafe extern "C" fn slint_interpreter_component_instance_invoke(
309    inst: &ErasedItemTreeBox,
310    name: Slice<u8>,
311    args: Slice<Box<Value>>,
312) -> *mut Value {
313    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
314    generativity::make_guard!(guard);
315    let comp = inst.unerase(guard);
316    match comp.description().invoke(
317        comp.borrow(),
318        &normalize_identifier(std::str::from_utf8(&name).unwrap()),
319        args.as_slice(),
320    ) {
321        Ok(val) => Box::into_raw(Box::new(val)),
322        Err(_) => std::ptr::null_mut(),
323    }
324}
325
326/// Wrap the user_data provided by the native code and call the drop function on Drop.
327///
328/// Safety: user_data must be a pointer that can be destroyed by the drop_user_data function.
329/// callback must be a valid callback that initialize the `ret`
330struct CallbackUserData {
331    user_data: *mut c_void,
332    drop_user_data: Option<extern "C" fn(*mut c_void)>,
333    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
334}
335
336impl Drop for CallbackUserData {
337    fn drop(&mut self) {
338        if let Some(x) = self.drop_user_data {
339            x(self.user_data)
340        }
341    }
342}
343
344impl CallbackUserData {
345    fn call(&self, args: &[Value]) -> Value {
346        let args = args.iter().map(|v| v.clone().into()).collect::<Vec<_>>();
347        (self.callback)(self.user_data, Slice::from_slice(args.as_ref())).as_ref().clone()
348    }
349}
350
351/// Set a handler for the callback.
352/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
353#[unsafe(no_mangle)]
354pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
355    inst: &ErasedItemTreeBox,
356    name: Slice<u8>,
357    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
358    user_data: *mut c_void,
359    drop_user_data: Option<extern "C" fn(*mut c_void)>,
360) -> bool {
361    let ud = CallbackUserData { user_data, drop_user_data, callback };
362
363    generativity::make_guard!(guard);
364    let comp = inst.unerase(guard);
365    comp.description()
366        .set_callback_handler(
367            comp.borrow(),
368            &normalize_identifier(std::str::from_utf8(&name).unwrap()),
369            Box::new(move |args| ud.call(args)),
370        )
371        .is_ok()
372}
373
374/// Get a global property. Returns a raw boxed value on success; nullptr otherwise.
375#[unsafe(no_mangle)]
376pub unsafe extern "C" fn slint_interpreter_component_instance_get_global_property(
377    inst: &ErasedItemTreeBox,
378    global: Slice<u8>,
379    property_name: Slice<u8>,
380) -> *mut Value {
381    generativity::make_guard!(guard);
382    let comp = inst.unerase(guard);
383    match comp
384        .description()
385        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
386        .and_then(|g| {
387            g.as_ref()
388                .get_property(&normalize_identifier(std::str::from_utf8(&property_name).unwrap()))
389        }) {
390        Ok(val) => Box::into_raw(Box::new(val)),
391        Err(_) => std::ptr::null_mut(),
392    }
393}
394
395#[unsafe(no_mangle)]
396pub extern "C" fn slint_interpreter_component_instance_set_global_property(
397    inst: &ErasedItemTreeBox,
398    global: Slice<u8>,
399    property_name: Slice<u8>,
400    val: &Value,
401) -> bool {
402    generativity::make_guard!(guard);
403    let comp = inst.unerase(guard);
404    comp.description()
405        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
406        .and_then(|g| {
407            g.as_ref()
408                .set_property(
409                    &normalize_identifier(std::str::from_utf8(&property_name).unwrap()),
410                    val.clone(),
411                )
412                .map_err(|_| ())
413        })
414        .is_ok()
415}
416
417/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
418#[unsafe(no_mangle)]
419pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callback(
420    inst: &ErasedItemTreeBox,
421    global: Slice<u8>,
422    name: Slice<u8>,
423    callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
424    user_data: *mut c_void,
425    drop_user_data: Option<extern "C" fn(*mut c_void)>,
426) -> bool {
427    let ud = CallbackUserData { user_data, drop_user_data, callback };
428
429    generativity::make_guard!(guard);
430    let comp = inst.unerase(guard);
431    comp.description()
432        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
433        .and_then(|g| {
434            g.as_ref().set_callback_handler(
435                &normalize_identifier(std::str::from_utf8(&name).unwrap()),
436                Box::new(move |args| ud.call(args)),
437            )
438        })
439        .is_ok()
440}
441
442/// Invoke a global callback or function. Returns raw boxed value on success; nullptr otherwise.
443#[unsafe(no_mangle)]
444pub unsafe extern "C" fn slint_interpreter_component_instance_invoke_global(
445    inst: &ErasedItemTreeBox,
446    global: Slice<u8>,
447    callable_name: Slice<u8>,
448    args: Slice<Box<Value>>,
449) -> *mut Value {
450    let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
451    generativity::make_guard!(guard);
452    let comp = inst.unerase(guard);
453    let callable_name = std::str::from_utf8(&callable_name).unwrap();
454    match comp
455        .description()
456        .get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
457        .and_then(|g| {
458            if matches!(
459                comp.description()
460                    .original
461                    .root_element
462                    .borrow()
463                    .lookup_property(callable_name)
464                    .property_type,
465                i_slint_compiler::langtype::Type::Function { .. }
466            ) {
467                g.as_ref().eval_function(
468                    &normalize_identifier(callable_name),
469                    args.as_slice().iter().cloned().collect(),
470                )
471            } else {
472                g.as_ref().invoke_callback(&normalize_identifier(callable_name), args.as_slice())
473            }
474        }) {
475        Ok(val) => Box::into_raw(Box::new(val)),
476        Err(_) => std::ptr::null_mut(),
477    }
478}
479
480/// Show or hide
481#[unsafe(no_mangle)]
482pub extern "C" fn slint_interpreter_component_instance_show(
483    inst: &ErasedItemTreeBox,
484    is_visible: bool,
485) {
486    generativity::make_guard!(guard);
487    let comp = inst.unerase(guard);
488    match is_visible {
489        true => comp.borrow_instance().window_adapter().window().show().unwrap(),
490        false => comp.borrow_instance().window_adapter().window().hide().unwrap(),
491    }
492}
493
494/// Return a window for the component
495///
496/// The out pointer must be uninitialized and must be destroyed with
497/// slint_windowrc_drop after usage
498#[unsafe(no_mangle)]
499pub unsafe extern "C" fn slint_interpreter_component_instance_window(
500    inst: &ErasedItemTreeBox,
501    out: *mut *const i_slint_core::window::ffi::WindowAdapterRcOpaque,
502) {
503    assert_eq!(
504        core::mem::size_of::<Rc<dyn WindowAdapter>>(),
505        core::mem::size_of::<i_slint_core::window::ffi::WindowAdapterRcOpaque>()
506    );
507    core::ptr::write(
508        out as *mut *const Rc<dyn WindowAdapter>,
509        inst.window_adapter_ref().unwrap() as *const _,
510    )
511}
512
513/// Instantiate an instance from a definition.
514///
515/// The `out` must be uninitialized and is going to be initialized after the call
516/// and need to be destroyed with slint_interpreter_component_instance_destructor
517#[unsafe(no_mangle)]
518pub unsafe extern "C" fn slint_interpreter_component_instance_create(
519    def: &ComponentDefinitionOpaque,
520    out: *mut ComponentInstance,
521) {
522    std::ptr::write(out, def.as_component_definition().create().unwrap())
523}
524
525#[unsafe(no_mangle)]
526pub unsafe extern "C" fn slint_interpreter_component_instance_component_definition(
527    inst: &ErasedItemTreeBox,
528    component_definition_ptr: *mut ComponentDefinitionOpaque,
529) {
530    generativity::make_guard!(guard);
531    let definition = ComponentDefinition { inner: inst.unerase(guard).description().into() };
532    std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition);
533}
534
535#[vtable::vtable]
536#[repr(C)]
537pub struct ModelAdaptorVTable {
538    pub row_count: extern "C" fn(VRef<ModelAdaptorVTable>) -> usize,
539    pub row_data: unsafe extern "C" fn(VRef<ModelAdaptorVTable>, row: usize) -> *mut Value,
540    pub set_row_data: extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, value: Box<Value>),
541    pub get_notify: extern "C" fn(VRef<'_, ModelAdaptorVTable>) -> &ModelNotifyOpaque,
542    pub drop: extern "C" fn(VRefMut<ModelAdaptorVTable>),
543}
544
545struct ModelAdaptorWrapper(vtable::VBox<ModelAdaptorVTable>);
546impl Model for ModelAdaptorWrapper {
547    type Data = Value;
548
549    fn row_count(&self) -> usize {
550        self.0.row_count()
551    }
552
553    fn row_data(&self, row: usize) -> Option<Value> {
554        let val_ptr = unsafe { self.0.row_data(row) };
555        if val_ptr.is_null() {
556            None
557        } else {
558            Some(*unsafe { Box::from_raw(val_ptr) })
559        }
560    }
561
562    fn model_tracker(&self) -> &dyn i_slint_core::model::ModelTracker {
563        self.0.get_notify().as_model_notify()
564    }
565
566    fn set_row_data(&self, row: usize, data: Value) {
567        let val = Box::new(data);
568        self.0.set_row_data(row, val);
569    }
570}
571
572#[repr(C)]
573#[cfg(target_pointer_width = "64")]
574pub struct ModelNotifyOpaque([usize; 8]);
575#[repr(C)]
576#[cfg(target_pointer_width = "32")]
577pub struct ModelNotifyOpaque([usize; 12]);
578/// Asserts that ModelNotifyOpaque is at least as large as ModelNotify, otherwise this would overflow
579const _: usize = std::mem::size_of::<ModelNotifyOpaque>() - std::mem::size_of::<ModelNotify>();
580const _: usize = std::mem::align_of::<ModelNotifyOpaque>() - std::mem::align_of::<ModelNotify>();
581
582impl ModelNotifyOpaque {
583    fn as_model_notify(&self) -> &ModelNotify {
584        // Safety: there should be no way to construct a ModelNotifyOpaque without it holding an actual ModelNotify
585        unsafe { std::mem::transmute::<&ModelNotifyOpaque, &ModelNotify>(self) }
586    }
587}
588
589/// Construct a new ModelNotifyNotify in the given memory region
590#[unsafe(no_mangle)]
591pub unsafe extern "C" fn slint_interpreter_model_notify_new(val: *mut ModelNotifyOpaque) {
592    std::ptr::write(val as *mut ModelNotify, ModelNotify::default());
593}
594
595/// Destruct the value in that memory location
596#[unsafe(no_mangle)]
597pub unsafe extern "C" fn slint_interpreter_model_notify_destructor(val: *mut ModelNotifyOpaque) {
598    drop(std::ptr::read(val as *mut ModelNotify))
599}
600
601#[unsafe(no_mangle)]
602pub unsafe extern "C" fn slint_interpreter_model_notify_row_changed(
603    notify: &ModelNotifyOpaque,
604    row: usize,
605) {
606    notify.as_model_notify().row_changed(row);
607}
608
609#[unsafe(no_mangle)]
610pub unsafe extern "C" fn slint_interpreter_model_notify_row_added(
611    notify: &ModelNotifyOpaque,
612    row: usize,
613    count: usize,
614) {
615    notify.as_model_notify().row_added(row, count);
616}
617
618#[unsafe(no_mangle)]
619pub unsafe extern "C" fn slint_interpreter_model_notify_reset(notify: &ModelNotifyOpaque) {
620    notify.as_model_notify().reset();
621}
622
623#[unsafe(no_mangle)]
624pub unsafe extern "C" fn slint_interpreter_model_notify_row_removed(
625    notify: &ModelNotifyOpaque,
626    row: usize,
627    count: usize,
628) {
629    notify.as_model_notify().row_removed(row, count);
630}
631
632// FIXME: Figure out how to re-export the one from compilerlib
633/// DiagnosticLevel describes the severity of a diagnostic.
634#[derive(Clone)]
635#[repr(u8)]
636pub enum DiagnosticLevel {
637    /// The diagnostic belongs to an error.
638    Error,
639    /// The diagnostic belongs to a warning.
640    Warning,
641}
642
643/// Diagnostic describes the aspects of either a warning or an error, along
644/// with its location and a description. Diagnostics are typically returned by
645/// slint::interpreter::ComponentCompiler::diagnostics() in a vector.
646#[derive(Clone)]
647#[repr(C)]
648pub struct Diagnostic {
649    /// The message describing the warning or error.
650    message: SharedString,
651    /// The path to the source file where the warning or error is located.
652    source_file: SharedString,
653    /// The line within the source file. Line numbers start at 1.
654    line: usize,
655    /// The column within the source file. Column numbers start at 1.
656    column: usize,
657    /// The level of the diagnostic, such as a warning or an error.
658    level: DiagnosticLevel,
659}
660
661#[repr(transparent)]
662pub struct ComponentCompilerOpaque(#[allow(deprecated)] NonNull<ComponentCompiler>);
663
664#[allow(deprecated)]
665impl ComponentCompilerOpaque {
666    fn as_component_compiler(&self) -> &ComponentCompiler {
667        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
668        unsafe { self.0.as_ref() }
669    }
670    fn as_component_compiler_mut(&mut self) -> &mut ComponentCompiler {
671        // Safety: there should be no way to construct a ComponentCompilerOpaque without it holding an actual ComponentCompiler
672        unsafe { self.0.as_mut() }
673    }
674}
675
676#[unsafe(no_mangle)]
677#[allow(deprecated)]
678pub unsafe extern "C" fn slint_interpreter_component_compiler_new(
679    compiler: *mut ComponentCompilerOpaque,
680) {
681    *compiler = ComponentCompilerOpaque(NonNull::new_unchecked(Box::into_raw(Box::new(
682        ComponentCompiler::default(),
683    ))));
684}
685
686#[unsafe(no_mangle)]
687pub unsafe extern "C" fn slint_interpreter_component_compiler_destructor(
688    compiler: *mut ComponentCompilerOpaque,
689) {
690    drop(Box::from_raw((*compiler).0.as_ptr()))
691}
692
693#[unsafe(no_mangle)]
694pub unsafe extern "C" fn slint_interpreter_component_compiler_set_include_paths(
695    compiler: &mut ComponentCompilerOpaque,
696    paths: &SharedVector<SharedString>,
697) {
698    compiler
699        .as_component_compiler_mut()
700        .set_include_paths(paths.iter().map(|path| path.as_str().into()).collect())
701}
702
703#[unsafe(no_mangle)]
704pub unsafe extern "C" fn slint_interpreter_component_compiler_set_style(
705    compiler: &mut ComponentCompilerOpaque,
706    style: Slice<u8>,
707) {
708    compiler.as_component_compiler_mut().set_style(std::str::from_utf8(&style).unwrap().to_string())
709}
710
711#[unsafe(no_mangle)]
712pub unsafe extern "C" fn slint_interpreter_component_compiler_set_translation_domain(
713    compiler: &mut ComponentCompilerOpaque,
714    translation_domain: Slice<u8>,
715) {
716    compiler
717        .as_component_compiler_mut()
718        .set_translation_domain(std::str::from_utf8(&translation_domain).unwrap().to_string())
719}
720
721#[unsafe(no_mangle)]
722pub unsafe extern "C" fn slint_interpreter_component_compiler_get_style(
723    compiler: &ComponentCompilerOpaque,
724    style_out: &mut SharedString,
725) {
726    *style_out =
727        compiler.as_component_compiler().style().map_or(SharedString::default(), |s| s.into());
728}
729
730#[unsafe(no_mangle)]
731pub unsafe extern "C" fn slint_interpreter_component_compiler_get_include_paths(
732    compiler: &ComponentCompilerOpaque,
733    paths: &mut SharedVector<SharedString>,
734) {
735    paths.extend(
736        compiler
737            .as_component_compiler()
738            .include_paths()
739            .iter()
740            .map(|path| path.to_str().map_or_else(Default::default, |str| str.into())),
741    );
742}
743
744#[unsafe(no_mangle)]
745pub unsafe extern "C" fn slint_interpreter_component_compiler_get_diagnostics(
746    compiler: &ComponentCompilerOpaque,
747    out_diags: &mut SharedVector<Diagnostic>,
748) {
749    #[allow(deprecated)]
750    out_diags.extend(compiler.as_component_compiler().diagnostics.iter().map(|diagnostic| {
751        let (line, column) = diagnostic.line_column();
752        Diagnostic {
753            message: diagnostic.message().into(),
754            source_file: diagnostic
755                .source_file()
756                .and_then(|path| path.to_str())
757                .map_or_else(Default::default, |str| str.into()),
758            line,
759            column,
760            level: match diagnostic.level() {
761                i_slint_compiler::diagnostics::DiagnosticLevel::Error => DiagnosticLevel::Error,
762                i_slint_compiler::diagnostics::DiagnosticLevel::Warning => DiagnosticLevel::Warning,
763                _ => DiagnosticLevel::Warning,
764            },
765        }
766    }));
767}
768
769#[unsafe(no_mangle)]
770pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_source(
771    compiler: &mut ComponentCompilerOpaque,
772    source_code: Slice<u8>,
773    path: Slice<u8>,
774    component_definition_ptr: *mut ComponentDefinitionOpaque,
775) -> bool {
776    match spin_on::spin_on(compiler.as_component_compiler_mut().build_from_source(
777        std::str::from_utf8(&source_code).unwrap().to_string(),
778        std::str::from_utf8(&path).unwrap().to_string().into(),
779    )) {
780        Some(definition) => {
781            std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition);
782            true
783        }
784        None => false,
785    }
786}
787
788#[unsafe(no_mangle)]
789pub unsafe extern "C" fn slint_interpreter_component_compiler_build_from_path(
790    compiler: &mut ComponentCompilerOpaque,
791    path: Slice<u8>,
792    component_definition_ptr: *mut ComponentDefinitionOpaque,
793) -> bool {
794    use std::str::FromStr;
795    match spin_on::spin_on(
796        compiler
797            .as_component_compiler_mut()
798            .build_from_path(PathBuf::from_str(std::str::from_utf8(&path).unwrap()).unwrap()),
799    ) {
800        Some(definition) => {
801            std::ptr::write(component_definition_ptr as *mut ComponentDefinition, definition);
802            true
803        }
804        None => false,
805    }
806}
807
808/// PropertyDescriptor is a simple structure that's used to describe a property declared in .slint
809/// code. It is returned from in a vector from
810/// slint::interpreter::ComponentDefinition::properties().
811#[derive(Clone)]
812#[repr(C)]
813pub struct PropertyDescriptor {
814    /// The name of the declared property.
815    property_name: SharedString,
816    /// The type of the property.
817    property_type: ValueType,
818}
819
820#[repr(C)]
821// Note: This needs to stay the size of 1 pointer to allow for the null pointer definition
822// in the C++ wrapper to allow for the null state.
823pub struct ComponentDefinitionOpaque([usize; 1]);
824/// Asserts that ComponentCompilerOpaque is as large as ComponentCompiler and has the same alignment, to make transmute safe.
825const _: [(); std::mem::size_of::<ComponentDefinitionOpaque>()] =
826    [(); std::mem::size_of::<ComponentDefinition>()];
827const _: [(); std::mem::align_of::<ComponentDefinitionOpaque>()] =
828    [(); std::mem::align_of::<ComponentDefinition>()];
829
830impl ComponentDefinitionOpaque {
831    fn as_component_definition(&self) -> &ComponentDefinition {
832        // Safety: there should be no way to construct a ComponentDefinitionOpaque without it holding an actual ComponentDefinition
833        unsafe { std::mem::transmute::<&ComponentDefinitionOpaque, &ComponentDefinition>(self) }
834    }
835}
836
837/// Construct a new Value in the given memory location
838#[unsafe(no_mangle)]
839pub unsafe extern "C" fn slint_interpreter_component_definition_clone(
840    other: &ComponentDefinitionOpaque,
841    def: *mut ComponentDefinitionOpaque,
842) {
843    std::ptr::write(def as *mut ComponentDefinition, other.as_component_definition().clone())
844}
845
846/// Destruct the component definition in that memory location
847#[unsafe(no_mangle)]
848pub unsafe extern "C" fn slint_interpreter_component_definition_destructor(
849    val: *mut ComponentDefinitionOpaque,
850) {
851    drop(std::ptr::read(val as *mut ComponentDefinition))
852}
853
854/// Returns the list of properties of the component the component definition describes
855#[unsafe(no_mangle)]
856pub unsafe extern "C" fn slint_interpreter_component_definition_properties(
857    def: &ComponentDefinitionOpaque,
858    props: &mut SharedVector<PropertyDescriptor>,
859) {
860    props.extend((&*def).as_component_definition().properties().map(
861        |(property_name, property_type)| PropertyDescriptor {
862            property_name: property_name.into(),
863            property_type,
864        },
865    ))
866}
867
868/// Returns the list of callback names of the component the component definition describes
869#[unsafe(no_mangle)]
870pub unsafe extern "C" fn slint_interpreter_component_definition_callbacks(
871    def: &ComponentDefinitionOpaque,
872    callbacks: &mut SharedVector<SharedString>,
873) {
874    callbacks.extend((&*def).as_component_definition().callbacks().map(|name| name.into()))
875}
876
877/// Returns the list of function names of the component the component definition describes
878#[unsafe(no_mangle)]
879pub unsafe extern "C" fn slint_interpreter_component_definition_functions(
880    def: &ComponentDefinitionOpaque,
881    functions: &mut SharedVector<SharedString>,
882) {
883    functions.extend((&*def).as_component_definition().functions().map(|name| name.into()))
884}
885
886/// Return the name of the component definition
887#[unsafe(no_mangle)]
888pub unsafe extern "C" fn slint_interpreter_component_definition_name(
889    def: &ComponentDefinitionOpaque,
890    name: &mut SharedString,
891) {
892    *name = (&*def).as_component_definition().name().into()
893}
894
895/// Returns a vector of strings with the names of all exported global singletons.
896#[unsafe(no_mangle)]
897pub unsafe extern "C" fn slint_interpreter_component_definition_globals(
898    def: &ComponentDefinitionOpaque,
899    names: &mut SharedVector<SharedString>,
900) {
901    names.extend((&*def).as_component_definition().globals().map(|name| name.into()))
902}
903
904/// Returns a vector of the property descriptors of the properties of the specified publicly exported global
905/// singleton. Returns true if a global exists under the specified name; false otherwise.
906#[unsafe(no_mangle)]
907pub unsafe extern "C" fn slint_interpreter_component_definition_global_properties(
908    def: &ComponentDefinitionOpaque,
909    global_name: Slice<u8>,
910    properties: &mut SharedVector<PropertyDescriptor>,
911) -> bool {
912    if let Some(property_it) = (&*def)
913        .as_component_definition()
914        .global_properties(std::str::from_utf8(&global_name).unwrap())
915    {
916        properties.extend(property_it.map(|(property_name, property_type)| PropertyDescriptor {
917            property_name: property_name.into(),
918            property_type,
919        }));
920        true
921    } else {
922        false
923    }
924}
925
926/// Returns a vector of the names of the callbacks of the specified publicly exported global
927/// singleton. Returns true if a global exists under the specified name; false otherwise.
928#[unsafe(no_mangle)]
929pub unsafe extern "C" fn slint_interpreter_component_definition_global_callbacks(
930    def: &ComponentDefinitionOpaque,
931    global_name: Slice<u8>,
932    names: &mut SharedVector<SharedString>,
933) -> bool {
934    if let Some(name_it) = (&*def)
935        .as_component_definition()
936        .global_callbacks(std::str::from_utf8(&global_name).unwrap())
937    {
938        names.extend(name_it.map(|name| name.into()));
939        true
940    } else {
941        false
942    }
943}
944
945/// Returns a vector of the names of the functions of the specified publicly exported global
946/// singleton. Returns true if a global exists under the specified name; false otherwise.
947#[unsafe(no_mangle)]
948pub unsafe extern "C" fn slint_interpreter_component_definition_global_functions(
949    def: &ComponentDefinitionOpaque,
950    global_name: Slice<u8>,
951    names: &mut SharedVector<SharedString>,
952) -> bool {
953    if let Some(name_it) = (&*def)
954        .as_component_definition()
955        .global_functions(std::str::from_utf8(&global_name).unwrap())
956    {
957        names.extend(name_it.map(|name| name.into()));
958        true
959    } else {
960        false
961    }
962}