mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 21:04:47 +00:00
runtime: Add a Signal class
Not very functional yet
This commit is contained in:
parent
4f22e2c341
commit
83d206ea39
5 changed files with 158 additions and 2 deletions
33
api/sixtyfps-cpp/include/sixtyfps_signals.h
Normal file
33
api/sixtyfps-cpp/include/sixtyfps_signals.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string_view>
|
||||||
|
#include "sixtyfps_signals_internal.h"
|
||||||
|
|
||||||
|
namespace sixtyfps {
|
||||||
|
|
||||||
|
// template<typename... Args>
|
||||||
|
struct Signal
|
||||||
|
{
|
||||||
|
Signal() { internal::sixtyfps_signal_init(&inner); }
|
||||||
|
~Signal() { internal::sixtyfps_signal_drop(&inner); }
|
||||||
|
Signal(const Signal &) = delete;
|
||||||
|
Signal(Signal &&) = delete;
|
||||||
|
Signal &operator=(const Signal &) = delete;
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void set_handler(F binding)
|
||||||
|
{
|
||||||
|
internal::sixtyfps_signal_set_handler(
|
||||||
|
&inner,
|
||||||
|
[](const void *user_data, const void *value) {
|
||||||
|
(*reinterpret_cast<F *>(user_data))(value);
|
||||||
|
},
|
||||||
|
new F(binding),
|
||||||
|
[](const void *user_data) { delete reinterpret_cast<F *>(user_data); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(void *data) const { internal::sixtyfps_signal_emit(&inner, data); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
internal::SignalOpaque inner;
|
||||||
|
};
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ When adding an item or a property, it needs to be kept in sync with different pl
|
||||||
use super::datastructures::{
|
use super::datastructures::{
|
||||||
CachedRenderingData, Item, ItemConsts, ItemVTable, LayoutInfo, Rect, RenderingInfo,
|
CachedRenderingData, Item, ItemConsts, ItemVTable, LayoutInfo, Rect, RenderingInfo,
|
||||||
};
|
};
|
||||||
use crate::{Property, SharedString};
|
use crate::{Property, SharedString, Signal};
|
||||||
use vtable::HasStaticVTable;
|
use vtable::HasStaticVTable;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -143,6 +143,7 @@ pub struct TouchArea {
|
||||||
pub height: Property<f32>,
|
pub height: Property<f32>,
|
||||||
/// FIXME: We should anotate this as an "output" property
|
/// FIXME: We should anotate this as an "output" property
|
||||||
pub pressed: Property<bool>,
|
pub pressed: Property<bool>,
|
||||||
|
pub clicked: Signal<()>,
|
||||||
/// FIXME: remove this
|
/// FIXME: remove this
|
||||||
pub cached_rendering_data: CachedRenderingData,
|
pub cached_rendering_data: CachedRenderingData,
|
||||||
}
|
}
|
||||||
|
|
111
sixtyfps_runtime/corelib/abi/signals.rs
Normal file
111
sixtyfps_runtime/corelib/abi/signals.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*!
|
||||||
|
Signal that can be connected to one sigle handler.
|
||||||
|
|
||||||
|
TODO: reconsider if we should rename that to `Event`
|
||||||
|
but then it should also be renamed everywhere, including in the language grammar
|
||||||
|
*/
|
||||||
|
|
||||||
|
use core::any::Any;
|
||||||
|
|
||||||
|
/// A Signal that can be connected to a handler.
|
||||||
|
///
|
||||||
|
/// The Arg represents the argument. It should always be a tuple
|
||||||
|
///
|
||||||
|
#[derive(Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Signal<Arg> {
|
||||||
|
/// FIXME: Box<dyn> is a fat object and we probaly want to put an erased type in there
|
||||||
|
handler: Option<Box<dyn Fn(&dyn Any, Arg)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Arg> Signal<Arg> {
|
||||||
|
pub fn emit(&self, context: &dyn Any, a: Arg) {
|
||||||
|
if let Some(h) = &self.handler {
|
||||||
|
h(context, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_handler(&mut self, f: impl Fn(&dyn Any, Arg) + 'static) {
|
||||||
|
self.handler = Some(Box::new(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn signal_simple_test() {
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Component {
|
||||||
|
pressed: core::cell::Cell<bool>,
|
||||||
|
clicked: Signal<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut c = Component::default();
|
||||||
|
c.clicked.set_handler(|c, ()| {
|
||||||
|
c.downcast_ref::<Component>().unwrap().pressed.set(true);
|
||||||
|
});
|
||||||
|
c.clicked.emit(&c, ());
|
||||||
|
assert_eq!(c.pressed.get(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type c_void = ();
|
||||||
|
#[repr(C)]
|
||||||
|
/// Has the same layout as Signal<()>
|
||||||
|
pub struct SignalOpaque(*const c_void, *const c_void);
|
||||||
|
|
||||||
|
/// Initialize the signal.
|
||||||
|
/// sixtyfps_signal_drop must be called.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_signal_init(out: *mut SignalOpaque) {
|
||||||
|
assert_eq!(core::mem::size_of::<SignalOpaque>(), core::mem::size_of::<Signal<()>>());
|
||||||
|
core::ptr::write(out as *mut Signal<()>, Default::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit the signal
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_signal_emit(sig: *const SignalOpaque, component: *const c_void) {
|
||||||
|
let sig = &*(sig as *const Signal<()>);
|
||||||
|
let context = &*component;
|
||||||
|
sig.emit(context, ());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set signal handler.
|
||||||
|
///
|
||||||
|
/// The binding has signature fn(user_data, component_data)
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_signal_set_handler(
|
||||||
|
sig: *mut SignalOpaque,
|
||||||
|
binding: extern "C" fn(*mut c_void, *const c_void),
|
||||||
|
user_data: *mut c_void,
|
||||||
|
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||||
|
) {
|
||||||
|
let sig = &mut *(sig as *mut Signal<()>);
|
||||||
|
|
||||||
|
struct UserData {
|
||||||
|
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 };
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct TraitObject(*const c_void, *const c_void);
|
||||||
|
|
||||||
|
let real_binding = move |compo: &dyn Any, ()| {
|
||||||
|
let to = core::mem::transmute::<&dyn Any, TraitObject>(compo);
|
||||||
|
binding(ud.user_data, to.0);
|
||||||
|
};
|
||||||
|
sig.set_handler(real_binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroy signal
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sixtyfps_signal_drop(handle: *mut SignalOpaque) {
|
||||||
|
core::ptr::read(handle as *mut Signal<()>);
|
||||||
|
}
|
|
@ -36,11 +36,17 @@ fn main() {
|
||||||
cbindgen::Builder::new()
|
cbindgen::Builder::new()
|
||||||
.with_config(config.clone())
|
.with_config(config.clone())
|
||||||
.with_src(format!("{}/abi/properties.rs", crate_dir))
|
.with_src(format!("{}/abi/properties.rs", crate_dir))
|
||||||
// .with_after_include("namespace sixtyfps { struct SharedString; }")
|
|
||||||
.generate()
|
.generate()
|
||||||
.expect("Unable to generate bindings")
|
.expect("Unable to generate bindings")
|
||||||
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_properties_internal.h");
|
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_properties_internal.h");
|
||||||
|
|
||||||
|
cbindgen::Builder::new()
|
||||||
|
.with_config(config.clone())
|
||||||
|
.with_src(format!("{}/abi/signals.rs", crate_dir))
|
||||||
|
.generate()
|
||||||
|
.expect("Unable to generate bindings")
|
||||||
|
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_signals_internal.h");
|
||||||
|
|
||||||
cbindgen::Builder::new()
|
cbindgen::Builder::new()
|
||||||
.with_config(config)
|
.with_config(config)
|
||||||
.with_src(format!("{}/abi/datastructures.rs", crate_dir))
|
.with_src(format!("{}/abi/datastructures.rs", crate_dir))
|
||||||
|
@ -49,6 +55,7 @@ fn main() {
|
||||||
.with_include("vtable.h")
|
.with_include("vtable.h")
|
||||||
.with_include("sixtyfps_string.h")
|
.with_include("sixtyfps_string.h")
|
||||||
.with_include("sixtyfps_properties.h")
|
.with_include("sixtyfps_properties.h")
|
||||||
|
.with_include("sixtyfps_signals.h")
|
||||||
.generate()
|
.generate()
|
||||||
.expect("Unable to generate bindings")
|
.expect("Unable to generate bindings")
|
||||||
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_internal.h");
|
.write_to_file(env::var("OUT_DIR").unwrap() + "/sixtyfps_internal.h");
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub mod abi {
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod primitives;
|
pub mod primitives;
|
||||||
pub mod properties;
|
pub mod properties;
|
||||||
|
pub mod signals;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +17,9 @@ pub use abi::string::SharedString;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use abi::properties::Property;
|
pub use abi::properties::Property;
|
||||||
|
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use abi::signals::Signal;
|
||||||
|
|
||||||
mod item_rendering;
|
mod item_rendering;
|
||||||
|
|
||||||
pub struct MainWindow<GraphicsBackend>
|
pub struct MainWindow<GraphicsBackend>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue