use core::ptr::NonNull; use vtable::*; pub type Rect = euclid::default::Rect; pub type Point = euclid::default::Point2D; /// Expand Rect so that cbindgen can see it. ( is in fact euclid::default::Rect) #[cfg(cbindgen)] #[repr(C)] struct Rect { x: f32, y: f32, width: f32, height: f32, } /// Expand Point so that cbindgen can see it. ( is in fact euclid::default::PointD2) #[cfg(cbindgen)] #[repr(C)] struct Point { x: f32, y: f32, } #[vtable] #[repr(C)] pub struct ComponentVTable { /// Allocate an instance of this component pub create: extern "C" fn(&ComponentVTable) -> VBox, /// Destruct this component. pub drop: extern "C" fn(VRefMut), /// Returns an array that represent the item tree pub item_tree: extern "C" fn(VRef) -> *const ItemTreeNode, } #[repr(C)] #[derive(Default)] pub struct CachedRenderingData { /// Used and modified by the backend, should be initialized to 0 by the user code pub(crate) cache_index: usize, /// Set to false initially and when changes happen that require updating the cache pub(crate) cache_ok: bool, } /// The item tree is an array of ItemTreeNode representing a static tree of items /// within a component. #[repr(C)] pub enum ItemTreeNode { /// Static item Item { /// byte offset where we can find the item (from the *ComponentImpl) offset: isize, /// virtual table of the item vtable: *const ItemVTable, /// number of children chilren_count: u32, /// index of the first children within the item tree children_index: u32, }, /// A placeholder for many instance of item in their own component which /// are instentiated according to a model. DynamicTree { /// Component vtable. /// This component is going to be instantiated as many time as the model tells component_type: *const ComponentVTable, /// vtable of the model model_type: *const super::model::ModelType, /// byte offset of the ModelImpl within the component. /// The model is an instance of the model described by model_type and must be /// stored within the component model_offset: isize, /// byte offset of the vector of components within the parent component /// (ComponentVec) /// a ComponentVec must be stored within the component to represent this tree components_holder_offset: isize, }, } /// It is supposed to be in static array unsafe impl Sync for ItemTreeNode {} #[vtable] #[repr(C)] pub struct ItemVTable { /// pub geometry: extern "C" fn(VRef<'_, ItemVTable>) -> Rect, /// offset in bytes fromthe *const ItemImpl. /// isize::MAX means None #[allow(non_upper_case_globals)] #[offset(CachedRenderingData)] pub cached_rendering_data_offset: usize, /// Return a rendering info pub rendering_info: extern "C" fn(VRef<'_, ItemVTable>) -> RenderingInfo, /// We would need max/min/preferred size, and all layout info pub layouting_info: extern "C" fn(VRef<'_, ItemVTable>) -> LayoutInfo, /// input event pub input_event: extern "C" fn(VRef<'_, ItemVTable>, MouseEvent), } // given an ItemImpl & ItemVTable // (1) Identify that the item *is* a rectangle or has everything a rectangle would have // (2) change the width #[repr(C)] pub struct LayoutInfo { min_size: f32, //... width_offset: isize, } #[repr(C)] #[derive(Clone, Debug)] pub enum RenderingInfo { NoContents, Rectangle(f32, f32, f32, f32, u32), // Should be a beret structure Image(f32, f32, crate::SharedString), Text(f32, f32, crate::SharedString, u32), /*Path(Vec), Image(OpaqueImageHandle, AspectRatio)*/ } #[repr(C)] #[derive(Debug, Clone, Copy)] pub enum MouseEventType { MousePressed, MouseReleased, MouseMoved, } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct MouseEvent { pub pos: Point, pub what: MouseEventType, } /* -- Safe wrappers*/ /// Visit each items recursively /// /// The state parametter returned by the visitor is passed to each children. pub fn visit_items( component: VRef<'_, ComponentVTable>, mut visitor: impl FnMut(ItemRef<'_>, &State) -> State, state: State, ) { visit_internal(component, &mut visitor, 0, &state) } fn visit_internal( component: VRef<'_, ComponentVTable>, visitor: &mut impl FnMut(ItemRef<'_>, &State) -> State, index: isize, state: &State, ) { let item_tree = component.item_tree(); match unsafe { &*item_tree.offset(index) } { ItemTreeNode::Item { vtable, offset, children_index, chilren_count } => { let item = unsafe { ItemRef::from_raw( NonNull::new_unchecked(*vtable as *mut _), NonNull::new_unchecked(component.as_ptr().offset(*offset) as *mut _), ) }; let state = visitor(item, state); for c in *children_index..(*children_index + *chilren_count) { visit_internal(component, visitor, c as isize, &state) } } ItemTreeNode::DynamicTree { .. } => todo!(), } } pub fn visit_items_mut( component: VRefMut<'_, ComponentVTable>, mut visitor: impl FnMut(ItemRefMut<'_>, &State) -> State, state: State, ) { visit_internal_mut(component, &mut visitor, 0, &state) } fn visit_internal_mut( mut component: VRefMut<'_, ComponentVTable>, visitor: &mut impl FnMut(ItemRefMut<'_>, &State) -> State, index: isize, state: &State, ) { let item_tree = component.item_tree(); match unsafe { &*item_tree.offset(index) } { ItemTreeNode::Item { vtable, offset, children_index, chilren_count } => { let mut item = unsafe { ItemRefMut::from_raw( NonNull::new_unchecked(*vtable as *mut _), NonNull::new_unchecked( (component.as_ptr() as *mut u8).offset(*offset) as *mut _ ), ) }; let state = visitor(item.borrow_mut(), state); for c in *children_index..(*children_index + *chilren_count) { visit_internal_mut(component.borrow_mut(), visitor, c as isize, &state) } } ItemTreeNode::DynamicTree { .. } => todo!(), } } /* /* Button { visible: false; text: "foo"} -> ProxyWithVisibility // Qt style selected: fn render_button(item: *const ItemImpl) -> RenderingInfo { let button = reinterpret_cast<&Button>(button) let text = b.text(); let isPressed = b.isPressed(); // ... let image = qt_render_button(width, height, isPressed, text) return RenderingInfo::Image(image) } // Basic style selected: -> Rectangle / Text fn render_rectangle(item: *const ItemImpl) -> RenderingInfo { let rect = reinterpret_cast<&Rectangle>(item) ... return RenderingInfo::Path(rect_path) } fn render_text(item: *const ItemImpl) -> RenderInfo { } */ // in corelib/primitives.rs bitflags! { enum ItemExtensions { HasVisibility, HasOpacity } } struct ItemBase { extensions: ItemExtensions, x: Property, y: Property, // visible, opacity, ? extraData: Vec<...> } impl ItemBase { pub fn is_visible(&self) -> bool { if self.extensions & HasVisibility { return self.extraData } else { return true; } } } #[derive(SixtyFpsItem)] /// ``` /// width: f32 /// height: f32 /// ``` fn render_rectangle(item: *const ItemImpl) -> RenderingInfo { //let width = property_at_offset(item, 1); let rect : &Rectnalge = unsafe { std::mem::transmute(item) }; let width = rect.width.get() } pub static RECTANGLE_VTABLE: ItemVTable = ItemVTable { rendering_info: render_rectangle, } // in styles/qt.rs //#[derive(SixtyFpsItem)] struct QtButton { text: String, is_pressed: bool, } pub static QT_BUTTON_VTABLE: ItemVTable = ItemVTable { rendering_info: render_qt_button, }; */ // This is here because for some reason (rust bug?) the ItemVTable_static is not accessible in the other modules ItemVTable_static!(crate::abi::primitives::Image); ItemVTable_static!(crate::abi::primitives::Rectangle); ItemVTable_static!(crate::abi::primitives::Text); ItemVTable_static!(crate::abi::primitives::TouchArea);