Add an init function to the Item vtable

This will be called by the run-time and will allow items to set up
bindings that rely on internals that should not be exposed to the
compiler/runtime.
This commit is contained in:
Simon Hausmann 2020-10-12 11:10:12 +02:00
parent 8cf6cdb033
commit a37d42fa0e
9 changed files with 133 additions and 19 deletions

View file

@ -101,6 +101,13 @@ struct ComponentWindow
cbindgen_private::sixtyfps_component_window_set_focus_item(&inner, c, item);
}
template<typename Component, typename ItemTree>
void init_items(Component *c, ItemTree items) const
{
cbindgen_private::sixtyfps_component_init_items(
VRef<ComponentVTable> { &Component::component_type, c }, items, &inner);
}
private:
cbindgen_private::ComponentWindowOpaque inner;
};
@ -120,9 +127,9 @@ using cbindgen_private::NativeButton;
using cbindgen_private::NativeCheckBox;
using cbindgen_private::NativeGroupBox;
using cbindgen_private::NativeLineEdit;
using cbindgen_private::NativeScrollBar;
using cbindgen_private::NativeSlider;
using cbindgen_private::NativeSpinBox;
using cbindgen_private::NativeScrollBar;
using cbindgen_private::NativeStandardListViewItem;
namespace private_api {
@ -270,7 +277,8 @@ using cbindgen_private::solve_grid_layout;
using cbindgen_private::solve_path_layout;
// models
struct AbstractRepeaterView {
struct AbstractRepeaterView
{
~AbstractRepeaterView() = default;
virtual void row_added(int index, int count) = 0;
virtual void row_removed(int index, int count) = 0;
@ -348,7 +356,8 @@ public:
}
int row_count() const override { return Count; }
ModelData row_data(int i) const override { return data[i]; }
void set_row_data(int i, const ModelData &value) override {
void set_row_data(int i, const ModelData &value) override
{
data[i] = value;
this->row_changed(i);
}
@ -388,11 +397,11 @@ public:
}
/// Remove the row at the given index from the model
void erase(int index) {
void erase(int index)
{
data.erase(data.begin() + index);
this->row_removed(index, 1);
}
};
template<typename C, typename ModelData>
@ -400,25 +409,30 @@ class Repeater
{
Property<std::shared_ptr<Model<ModelData>>> model;
struct RepeaterInner : AbstractRepeaterView {
struct RepeaterInner : AbstractRepeaterView
{
enum class State { Clean, Dirty };
struct ComponentWithState {
struct ComponentWithState
{
State state = State::Dirty;
std::unique_ptr<C> ptr;
};
std::vector<ComponentWithState> data;
bool is_dirty = true;
void row_added(int index, int count) override {
void row_added(int index, int count) override
{
is_dirty = true;
data.resize(data.size() + count);
std::rotate(data.begin() + index, data.end() - count, data.end());
}
void row_changed(int index) override {
void row_changed(int index) override
{
is_dirty = true;
data[index].state = State::Dirty;
}
void row_removed(int index, int count) override {
void row_removed(int index, int count) override
{
is_dirty = true;
data.erase(data.begin() + index, data.begin() + index + count);
for (std::size_t i = index; i < data.size(); ++i) {
@ -457,8 +471,7 @@ public:
auto &c = inner->data[i];
if (c.state == RepeaterInner::State::Dirty) {
if (!c.ptr) {
c.ptr = std::make_unique<C>();
c.ptr->parent = parent;
c.ptr = std::make_unique<C>(parent);
}
c.ptr->update_data(i, m->row_data(i));
}
@ -471,7 +484,8 @@ public:
template<typename Parent>
void ensure_updated_listview(const Parent *parent, const Property<float> *viewport_width,
const Property<float> *viewport_height, [[maybe_unused]] const Property<float> *viewport_y,
const Property<float> *viewport_height,
[[maybe_unused]] const Property<float> *viewport_y,
float listview_width, [[maybe_unused]] float listview_height) const
{
// TODO: the rust code in model.rs try to only allocate as many items as visible items
@ -508,7 +522,8 @@ public:
}
}
float compute_layout_listview(const Property<float> *viewport_width, float listview_width) const {
float compute_layout_listview(const Property<float> *viewport_width, float listview_width) const
{
float offset = 0;
viewport_width->set(listview_width);
if (!inner)
@ -519,7 +534,8 @@ public:
return offset;
}
void model_set_row_data(int row, const ModelData &data) const {
void model_set_row_data(int row, const ModelData &data) const
{
if (model.is_dirty()) {
std::abort();
}

View file

@ -126,7 +126,9 @@ pub mod re_exports {
pub use once_cell::unsync::OnceCell;
pub use pin_weak::rc::*;
pub use sixtyfps_corelib::animations::EasingCurve;
pub use sixtyfps_corelib::component::{Component, ComponentRefPin, ComponentVTable};
pub use sixtyfps_corelib::component::{
init_component_items, Component, ComponentRefPin, ComponentVTable,
};
pub use sixtyfps_corelib::eventloop::ComponentWindow;
pub use sixtyfps_corelib::graphics::{
PathArcTo, PathData, PathElement, PathEvent, PathLineTo, Point, Rect, Size,

View file

@ -655,6 +655,8 @@ fn generate_component(
}
}
let mut constructor_parent_arg = String::new();
if !is_root {
let parent_element = component.parent_element.upgrade().unwrap();
@ -692,10 +694,13 @@ fn generate_component(
.upgrade()
.unwrap(),
);
let parent_type = format!("{} const *", parent_component_id);
constructor_parent_arg = format!("{} parent", parent_type);
init.push("this->parent = parent;".into());
component_struct.members.push((
Access::Public, // Because Repeater accesses it
Declaration::Var(Var {
ty: format!("{} const *", parent_component_id),
ty: parent_type,
name: "parent".into(),
init: Some("nullptr".to_owned()),
}),
@ -826,6 +831,11 @@ fn generate_component(
}
});
init.push(format!(
"{}.init_items(this, item_tree());",
window = window_ref_expression(component)
));
for extra_init_code in component.setup_code.borrow().iter() {
init.push(compile_expression(extra_init_code, component));
}
@ -834,7 +844,7 @@ fn generate_component(
Access::Public,
Declaration::Function(Function {
name: component_id.clone(),
signature: "()".to_owned(),
signature: format!("({})", constructor_parent_arg),
is_constructor_or_destructor: true,
statements: Some(init),
..Default::default()

View file

@ -615,6 +615,8 @@ fn generate_component(
init.push(compile_expression(extra_init_code, component));
}
let window_ref = window_ref_expression(component);
Some(quote!(
#(#resource_symbols)*
@ -766,6 +768,7 @@ fn generate_component(
self_pinned.self_weak.set(PinWeak::downgrade(self_pinned.clone())).map_err(|_|())
.expect("Can only be pinned once");
let _self = self_pinned.as_ref();
sixtyfps::re_exports::init_component_items(_self, Self::item_tree(), &#window_ref);
#(#init)*
self_pinned
}

View file

@ -68,3 +68,40 @@ pub type ComponentRef<'a> = vtable::VRef<'a, ComponentVTable>;
/// Type alias to the commonly use `Pin<VRef<ComponentVTable>>>`
pub type ComponentRefPin<'a> = core::pin::Pin<ComponentRef<'a>>;
/// Call init() on the ItemVTable for each item of the component.
pub fn init_component_items<Base>(
base: core::pin::Pin<&Base>,
item_tree: &[crate::item_tree::ItemTreeNode<Base>],
window: &ComponentWindow,
) {
item_tree.iter().for_each(|entry| match entry {
crate::item_tree::ItemTreeNode::Item { item, .. } => {
item.apply_pin(base).as_ref().init(window)
}
crate::item_tree::ItemTreeNode::DynamicTree { .. } => {}
})
}
pub(crate) mod ffi {
#![allow(unsafe_code)]
use super::*;
use crate::item_tree::*;
use crate::slice::Slice;
/// Call init() on the ItemVTable of each item of the component.
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_component_init_items(
component: ComponentRefPin,
item_tree: Slice<ItemTreeNode<u8>>,
window_handle: *const crate::eventloop::ffi::ComponentWindowOpaque,
) {
let window = &*(window_handle as *const ComponentWindow);
super::init_component_items(
core::pin::Pin::new_unchecked(&*(component.as_ptr() as *const u8)),
item_tree.as_slice(),
window,
)
}
}

View file

@ -46,6 +46,11 @@ use vtable::*;
#[vtable]
#[repr(C)]
pub struct ItemVTable {
/// This function is called by the run-time after the memory for the item
/// has been allocated and initialized. It will be called before any user specified
/// bindings are set.
pub init: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, window: &ComponentWindow),
/// Returns the geometry of this item (relative to its parent item)
pub geometry: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> Rect,
@ -110,6 +115,8 @@ pub struct Rectangle {
}
impl Item for Rectangle {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -190,6 +197,8 @@ pub struct BorderRectangle {
}
impl Item for BorderRectangle {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -273,6 +282,8 @@ pub struct Image {
}
impl Item for Image {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -392,6 +403,8 @@ pub struct Text {
}
impl Item for Text {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
// FIXME: width / height. or maybe it doesn't matter? (
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
@ -520,6 +533,8 @@ pub struct TouchArea {
}
impl Item for TouchArea {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -617,6 +632,8 @@ pub struct Path {
}
impl Item for Path {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -696,6 +713,8 @@ pub struct Flickable {
}
impl Item for Flickable {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -822,6 +841,8 @@ pub struct Window {
}
impl Item for Window {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
0.,
@ -903,6 +924,8 @@ pub struct TextInput {
}
impl Item for TextInput {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
// FIXME: width / height. or maybe it doesn't matter? (
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(

View file

@ -85,4 +85,5 @@ pub fn use_modules() -> usize {
+ string::ffi::sixtyfps_shared_string_bytes as usize
+ eventloop::ffi::sixtyfps_component_window_drop as usize
+ input::ffi::sixtyfps_process_ungrabbed_mouse_event as usize
+ component::ffi::sixtyfps_component_init_items as usize
}

View file

@ -716,6 +716,12 @@ pub fn instantiate<'id>(
));
}
sixtyfps_corelib::component::init_component_items(
instance_ref.instance,
instance_ref.component_type.item_tree.as_slice().into(),
&eval::window_ref(instance_ref).unwrap(),
);
for item_within_component in component_type.items.values() {
unsafe {
let item = item_within_component.item_from_component(mem);

View file

@ -104,6 +104,8 @@ pub struct NativeButton {
}
impl Item for NativeButton {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -223,6 +225,8 @@ pub struct NativeCheckBox {
}
impl Item for NativeCheckBox {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -357,6 +361,8 @@ void initQSpinBoxOptions(QStyleOptionSpinBox &option, bool pressed, int active_c
}}
impl Item for NativeSpinBox {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -547,6 +553,8 @@ void initQSliderOptions(QStyleOptionSlider &option, bool pressed, int active_con
}}
impl Item for NativeSlider {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -719,6 +727,8 @@ pub struct NativeGroupBox {
}
impl Item for NativeGroupBox {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -854,6 +864,8 @@ pub struct NativeLineEdit {
}
impl Item for NativeLineEdit {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -970,6 +982,8 @@ pub struct NativeScrollBar {
}
impl Item for NativeScrollBar {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
@ -1192,6 +1206,8 @@ pub struct NativeStandardListViewItem {
}
impl Item for NativeStandardListViewItem {
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::FIELD_OFFSETS.x.apply_pin(self).get(),