mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-31 15:47:26 +00:00

The README.md contains the warning that used to be in lib.rs. Add README.md files to all internal crates ... pointing to the official public crate to use instead. Rename internal crates fixup: README files fixup rename
181 lines
7.2 KiB
Rust
181 lines
7.2 KiB
Rust
// Copyright © SixtyFPS GmbH <info@sixtyfps.io>
|
|
// SPDX-License-Identifier: (GPL-3.0-only OR LicenseRef-SixtyFPS-commercial)
|
|
|
|
#![doc = include_str!("README.md")]
|
|
#![doc(html_logo_url = "https://sixtyfps.io/resources/logo.drawio.svg")]
|
|
|
|
extern crate proc_macro;
|
|
use proc_macro::TokenStream;
|
|
use quote::quote;
|
|
|
|
/// This derive macro is used with structures in the run-time library that are meant
|
|
/// to be exposed to the language. The structure is introspected for properties and fields
|
|
/// marked with the `rtti_field` attribute and generates run-time type information for use
|
|
/// with the interpreter.
|
|
/// In addition all `Property<T> foo` fields get a convenient getter function generated
|
|
/// that works on a `Pin<&Self>` receiver.
|
|
#[proc_macro_derive(SlintElement, attributes(rtti_field))]
|
|
pub fn slint_element(input: TokenStream) -> TokenStream {
|
|
let input = syn::parse_macro_input!(input as syn::DeriveInput);
|
|
|
|
let fields = match &input.data {
|
|
syn::Data::Struct(syn::DataStruct { fields: f @ syn::Fields::Named(..), .. }) => f,
|
|
_ => {
|
|
return syn::Error::new(
|
|
input.ident.span(),
|
|
"Only `struct` with named field are supported",
|
|
)
|
|
.to_compile_error()
|
|
.into()
|
|
}
|
|
};
|
|
|
|
let mut pub_prop_field_names = Vec::new();
|
|
let mut pub_prop_field_names_normalized = Vec::new();
|
|
let mut pub_prop_field_types = Vec::new();
|
|
let mut property_names = Vec::new();
|
|
let mut property_visibility = Vec::new();
|
|
let mut property_types = Vec::new();
|
|
for field in fields {
|
|
if let Some(property_type) = property_type(&field.ty) {
|
|
let name = field.ident.as_ref().unwrap();
|
|
if matches!(field.vis, syn::Visibility::Public(_)) {
|
|
pub_prop_field_names_normalized.push(normalize_identifier(name));
|
|
pub_prop_field_names.push(name);
|
|
pub_prop_field_types.push(&field.ty);
|
|
}
|
|
property_names.push(name);
|
|
property_visibility.push(field.vis.clone());
|
|
property_types.push(property_type);
|
|
}
|
|
}
|
|
|
|
let (plain_field_names, plain_field_types): (Vec<_>, Vec<_>) = fields
|
|
.iter()
|
|
.filter(|f| {
|
|
f.attrs.iter().any(|attr| {
|
|
attr.parse_meta()
|
|
.ok()
|
|
.map(|meta| match meta {
|
|
syn::Meta::Path(path) => {
|
|
path.get_ident().map(|ident| *ident == "rtti_field").unwrap_or(false)
|
|
}
|
|
_ => false,
|
|
})
|
|
.unwrap_or(false)
|
|
})
|
|
})
|
|
.map(|f| (f.ident.as_ref().unwrap(), &f.ty))
|
|
.unzip();
|
|
let plain_field_names_normalized =
|
|
plain_field_names.iter().map(|f| normalize_identifier(*f)).collect::<Vec<_>>();
|
|
|
|
let mut callback_field_names = Vec::new();
|
|
let mut callback_field_names_normalized = Vec::new();
|
|
let mut callback_args = Vec::new();
|
|
let mut callback_rets = Vec::new();
|
|
for field in fields {
|
|
if let Some((arg, ret)) = callback_arg(&field.ty) {
|
|
if matches!(field.vis, syn::Visibility::Public(_)) {
|
|
let name = field.ident.as_ref().unwrap();
|
|
callback_field_names_normalized.push(normalize_identifier(name));
|
|
callback_field_names.push(name);
|
|
callback_args.push(arg);
|
|
callback_rets.push(ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
let item_name = &input.ident;
|
|
|
|
quote!(
|
|
#[allow(clippy::nonstandard_macro_braces)]
|
|
#[cfg(feature = "rtti")]
|
|
impl BuiltinItem for #item_name {
|
|
fn name() -> &'static str {
|
|
stringify!(#item_name)
|
|
}
|
|
fn properties<Value: ValueType>() -> ::alloc::vec::Vec<(&'static str, &'static dyn PropertyInfo<Self, Value>)> {
|
|
::alloc::vec![#( {
|
|
const O : MaybeAnimatedPropertyInfoWrapper<#item_name, #pub_prop_field_types> =
|
|
MaybeAnimatedPropertyInfoWrapper(#item_name::FIELD_OFFSETS.#pub_prop_field_names);
|
|
(#pub_prop_field_names_normalized, (&O).as_property_info())
|
|
} ),*]
|
|
}
|
|
fn fields<Value: ValueType>() -> ::alloc::vec::Vec<(&'static str, &'static dyn FieldInfo<Self, Value>)> {
|
|
::alloc::vec![#( {
|
|
const O : const_field_offset::FieldOffset<#item_name, #plain_field_types, const_field_offset::AllowPin> =
|
|
#item_name::FIELD_OFFSETS.#plain_field_names;
|
|
(#plain_field_names_normalized, &O as &'static dyn FieldInfo<Self, Value>)
|
|
} ),*]
|
|
}
|
|
fn callbacks<Value: ValueType>() -> ::alloc::vec::Vec<(&'static str, &'static dyn CallbackInfo<Self, Value>)> {
|
|
::alloc::vec![#( {
|
|
const O : const_field_offset::FieldOffset<#item_name, Callback<#callback_args, #callback_rets>, const_field_offset::AllowPin> =
|
|
#item_name::FIELD_OFFSETS.#callback_field_names;
|
|
(#callback_field_names_normalized, &O as &'static dyn CallbackInfo<Self, Value>)
|
|
} ),*]
|
|
}
|
|
}
|
|
|
|
impl #item_name {
|
|
#(
|
|
#property_visibility fn #property_names(self: core::pin::Pin<&Self>) -> #property_types {
|
|
Self::FIELD_OFFSETS.#property_names.apply_pin(self).get()
|
|
}
|
|
)*
|
|
}
|
|
)
|
|
.into()
|
|
}
|
|
|
|
fn normalize_identifier(name: &syn::Ident) -> String {
|
|
name.to_string().replace('_', "-")
|
|
}
|
|
|
|
// Try to match `Property<Foo>` on the syn tree and return Foo if found
|
|
fn property_type(ty: &syn::Type) -> Option<&syn::Type> {
|
|
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty {
|
|
if let Some(syn::PathSegment {
|
|
ident,
|
|
arguments:
|
|
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
|
|
}) = segments.first()
|
|
{
|
|
match args.first() {
|
|
Some(syn::GenericArgument::Type(property_type)) if *ident == "Property" => {
|
|
return Some(property_type)
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
// Try to match `Callback<Args, Ret>` on the syn tree and return Args and Ret if found
|
|
fn callback_arg(ty: &syn::Type) -> Option<(&syn::Type, Option<&syn::Type>)> {
|
|
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty {
|
|
if let Some(syn::PathSegment {
|
|
ident,
|
|
arguments:
|
|
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
|
|
}) = segments.first()
|
|
{
|
|
if ident != "Callback" {
|
|
return None;
|
|
}
|
|
let mut it = args.iter();
|
|
let first = match it.next() {
|
|
Some(syn::GenericArgument::Type(ty)) => ty,
|
|
_ => return None,
|
|
};
|
|
let sec = match it.next() {
|
|
Some(syn::GenericArgument::Type(ty)) => Some(ty),
|
|
_ => None,
|
|
};
|
|
return Some((first, sec));
|
|
}
|
|
}
|
|
None
|
|
}
|