mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
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:
parent
8cf6cdb033
commit
a37d42fa0e
9 changed files with 133 additions and 19 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue