mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 06:41:14 +00:00
Add set_global_callback in the C++ interpreter API
This commit is contained in:
parent
a294ab4200
commit
0fad27e23e
3 changed files with 105 additions and 20 deletions
|
@ -687,7 +687,7 @@ public:
|
||||||
/// ```
|
/// ```
|
||||||
/// callback foo(string, int) -> string;
|
/// callback foo(string, int) -> string;
|
||||||
/// ```
|
/// ```
|
||||||
/// Then one can call it with this function
|
/// Then one can set the callback handler with this function
|
||||||
/// ```
|
/// ```
|
||||||
/// instance->set_callback("foo", [](auto args) {
|
/// instance->set_callback("foo", [](auto args) {
|
||||||
/// std::cout << "foo(" << *args[0].to_string() << ", " << *args[1].to_number() << ")\n";
|
/// std::cout << "foo(" << *args[0].to_string() << ", " << *args[1].to_number() << ")\n";
|
||||||
|
@ -721,7 +721,8 @@ public:
|
||||||
/// Returns true if the property was correctly set. Returns false if the property
|
/// Returns true if the property was correctly set. Returns false if the property
|
||||||
/// could not be set because it either do not exist (was not declared in .60) or if
|
/// could not be set because it either do not exist (was not declared in .60) or if
|
||||||
/// the value is not of the proper type for the property's type.
|
/// the value is not of the proper type for the property's type.
|
||||||
bool set_global_property(std::string_view global, std::string_view prop_name, const Value &value) const
|
bool set_global_property(std::string_view global, std::string_view prop_name,
|
||||||
|
const Value &value) const
|
||||||
{
|
{
|
||||||
using namespace cbindgen_private;
|
using namespace cbindgen_private;
|
||||||
return sixtyfps_interpreter_component_instance_set_global_property(
|
return sixtyfps_interpreter_component_instance_set_global_property(
|
||||||
|
@ -729,7 +730,8 @@ public:
|
||||||
sixtyfps::private_api::string_to_slice(prop_name), &value.inner);
|
sixtyfps::private_api::string_to_slice(prop_name), &value.inner);
|
||||||
}
|
}
|
||||||
/// Returns the value behind a property in a exported global.
|
/// Returns the value behind a property in a exported global.
|
||||||
std::optional<Value> get_global_property(std::string_view global, std::string_view prop_name) const
|
std::optional<Value> get_global_property(std::string_view global,
|
||||||
|
std::string_view prop_name) const
|
||||||
{
|
{
|
||||||
using namespace cbindgen_private;
|
using namespace cbindgen_private;
|
||||||
ValueOpaque out;
|
ValueOpaque out;
|
||||||
|
@ -741,6 +743,37 @@ public:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like `set_callback()` but on a callback in the specified exported global
|
||||||
|
///
|
||||||
|
/// Example: imagine the .60 file contains the given global:
|
||||||
|
/// ```60
|
||||||
|
/// export global Logic := {
|
||||||
|
/// callback to_uppercase(string) -> string;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Then one can set the callback handler
|
||||||
|
/// ```cpp
|
||||||
|
/// instance->set_global_callback("Logic", "to_uppercase", [](auto args) {
|
||||||
|
/// std::string arg1(*args[0].to_string());
|
||||||
|
/// std::transform(arg1.begin(), arg1.end(), arg1.begin(), toupper);
|
||||||
|
/// return SharedString(arg1);
|
||||||
|
/// })
|
||||||
|
/// ```
|
||||||
|
template<typename F>
|
||||||
|
bool set_global_callback(std::string_view global, std::string_view name, F callback) const
|
||||||
|
{
|
||||||
|
using cbindgen_private::ValueOpaque;
|
||||||
|
auto actual_cb = [](void *data, Slice<ValueOpaque> arg, ValueOpaque *ret) {
|
||||||
|
Slice<Value> args_view { reinterpret_cast<Value *>(arg.ptr), arg.len };
|
||||||
|
Value r = (*reinterpret_cast<F *>(data))(args_view);
|
||||||
|
new (ret) Value(std::move(r));
|
||||||
|
};
|
||||||
|
return cbindgen_private::sixtyfps_interpreter_component_instance_set_global_callback(
|
||||||
|
inner(), sixtyfps::private_api::string_to_slice(global),
|
||||||
|
sixtyfps::private_api::string_to_slice(name), actual_cb, new F(std::move(callback)),
|
||||||
|
[](void *data) { delete reinterpret_cast<F *>(data); });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if !defined(DOXYGEN)
|
#if !defined(DOXYGEN)
|
||||||
|
|
|
@ -492,7 +492,6 @@ SCENARIO("Send key events")
|
||||||
REQUIRE(*instance->get_property("result")->to_string() == "Hello keys!");
|
REQUIRE(*instance->get_property("result")->to_string() == "Hello keys!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SCENARIO("Global properties")
|
SCENARIO("Global properties")
|
||||||
{
|
{
|
||||||
using namespace sixtyfps::interpreter;
|
using namespace sixtyfps::interpreter;
|
||||||
|
@ -504,9 +503,15 @@ SCENARIO("Global properties")
|
||||||
R"(
|
R"(
|
||||||
export global The-Global := {
|
export global The-Global := {
|
||||||
property <string> the-property: "€€€";
|
property <string> the-property: "€€€";
|
||||||
|
callback to_uppercase(string)->string;
|
||||||
}
|
}
|
||||||
export Dummy := Rectangle { }
|
export Dummy := Rectangle {
|
||||||
)", "");
|
property <string> result: The-Global.to_uppercase("abc");
|
||||||
|
}
|
||||||
|
)",
|
||||||
|
"");
|
||||||
|
for (auto &&x : compiler.diagnostics())
|
||||||
|
std::cerr << x.message << std::endl;
|
||||||
REQUIRE(result.has_value());
|
REQUIRE(result.has_value());
|
||||||
auto instance = result->create();
|
auto instance = result->create();
|
||||||
|
|
||||||
|
@ -536,4 +541,16 @@ SCENARIO("Global properties")
|
||||||
REQUIRE(value->to_string().has_value());
|
REQUIRE(value->to_string().has_value());
|
||||||
REQUIRE(value->to_string().value() == "§§§");
|
REQUIRE(value->to_string().value() == "§§§");
|
||||||
}
|
}
|
||||||
|
SECTION("set callback")
|
||||||
|
{
|
||||||
|
REQUIRE(instance->set_global_callback("The-Global", "to_uppercase", [](auto args) {
|
||||||
|
std::string arg1(*args[0].to_string());
|
||||||
|
std::transform(arg1.begin(), arg1.end(), arg1.begin(), toupper);
|
||||||
|
return SharedString(arg1);
|
||||||
|
}));
|
||||||
|
auto result = instance->get_property("result");
|
||||||
|
REQUIRE(result.has_value());
|
||||||
|
REQUIRE(result->to_string().has_value());
|
||||||
|
REQUIRE(result->to_string().value() == "ABC");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,6 +371,22 @@ pub unsafe extern "C" fn sixtyfps_interpreter_component_instance_invoke_callback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrap the user_data provided by the native code and call the drop function on Drop.
|
||||||
|
///
|
||||||
|
/// Safety: user_data must be a pointer that can be destroyed by the drop_user_data function.
|
||||||
|
struct CallbackUserData {
|
||||||
|
user_data: *mut c_void,
|
||||||
|
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CallbackUserData {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(x) = self.drop_user_data {
|
||||||
|
x(self.user_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a handler for the callback.
|
/// Set a handler for the callback.
|
||||||
/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
|
/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -381,20 +397,7 @@ pub unsafe extern "C" fn sixtyfps_interpreter_component_instance_set_callback(
|
||||||
user_data: *mut c_void,
|
user_data: *mut c_void,
|
||||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
struct UserData {
|
let ud = CallbackUserData { user_data, drop_user_data };
|
||||||
user_data: *mut c_void,
|
|
||||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for UserData {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(x) = self.drop_user_data {
|
|
||||||
x(self.user_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let ud = UserData { user_data, drop_user_data };
|
|
||||||
|
|
||||||
let callback = move |args: &[Value]| -> Value {
|
let callback = move |args: &[Value]| -> Value {
|
||||||
let args = std::mem::transmute::<&[Value], &[ValueOpaque]>(args);
|
let args = std::mem::transmute::<&[Value], &[ValueOpaque]>(args);
|
||||||
let mut ret = std::mem::MaybeUninit::<Value>::uninit();
|
let mut ret = std::mem::MaybeUninit::<Value>::uninit();
|
||||||
|
@ -462,6 +465,38 @@ pub extern "C" fn sixtyfps_interpreter_component_instance_set_global_property(
|
||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The `callback` function must initialize the `ret` (the `ret` passed to the callback is initialized and is assumed initialized after the function)
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_interpreter_component_instance_set_global_callback(
|
||||||
|
inst: &ErasedComponentBox,
|
||||||
|
global: Slice<u8>,
|
||||||
|
name: Slice<u8>,
|
||||||
|
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<ValueOpaque>, ret: *mut ValueOpaque),
|
||||||
|
user_data: *mut c_void,
|
||||||
|
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||||
|
) -> bool {
|
||||||
|
let ud = CallbackUserData { user_data, drop_user_data };
|
||||||
|
|
||||||
|
let callback = move |args: &[Value]| -> Value {
|
||||||
|
let args = std::mem::transmute::<&[Value], &[ValueOpaque]>(args);
|
||||||
|
let mut ret = std::mem::MaybeUninit::<Value>::uninit();
|
||||||
|
callback(ud.user_data, Slice::from_slice(args), ret.as_mut_ptr() as *mut ValueOpaque);
|
||||||
|
ret.assume_init()
|
||||||
|
};
|
||||||
|
|
||||||
|
generativity::make_guard!(guard);
|
||||||
|
let comp = inst.unerase(guard);
|
||||||
|
comp.description()
|
||||||
|
.get_global(comp.borrow(), &normalize_identifier(std::str::from_utf8(&global).unwrap()))
|
||||||
|
.and_then(|g| {
|
||||||
|
g.as_ref().set_callback_handler(
|
||||||
|
&normalize_identifier(std::str::from_utf8(&name).unwrap()),
|
||||||
|
Box::new(callback),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
/// Show or hide
|
/// Show or hide
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn sixtyfps_interpreter_component_instance_show(
|
pub extern "C" fn sixtyfps_interpreter_component_instance_show(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue