Node: Add setHandler features on signals

This commit is contained in:
Olivier Goffart 2020-10-21 12:20:59 +02:00
parent c50b9d73d3
commit ac6d4007ab
4 changed files with 73 additions and 23 deletions

View file

@ -70,11 +70,12 @@ console.log(component.counter);
### Signals ### Signals
The signals are also exposed as property that can be called The signals are also exposed as property that have a setHandler function, and that can can be called.
```js ```js
// connect to a signal // connect to a signal
component.clicked = function() { console.log("hello"); } component.clicked.setHandler(function() { console.log("hello"); })
// emit a signal // emit a signal
component.clicked(); component.clicked();
``` ```

View file

@ -49,6 +49,14 @@ class Component {
} }
} }
/**
* @hidden
*/
interface Signal {
(): any;
setHandler(cb: any): void;
}
require.extensions['.60'] = require.extensions['.60'] =
function (module, filename) { function (module, filename) {
var c = native.load(filename); var c = native.load(filename);
@ -64,7 +72,11 @@ require.extensions['.60'] =
}); });
c.signals().forEach((x: string) => { c.signals().forEach((x: string) => {
Object.defineProperty(ret, x, { Object.defineProperty(ret, x, {
get() { return function () { comp.emit_signal(x, [...arguments]); } }, get() {
let signal = function () { comp.emit_signal(x, [...arguments]); } as Signal;
signal.setHandler = function (callback) { comp.connect_signal(x, callback) };
return signal;
},
enumerable: true, enumerable: true,
}) })
}); });
@ -187,8 +199,8 @@ class ArrayModel<T> implements Model<T> {
* the model, starting at the specified index. This is equivalent to calling * the model, starting at the specified index. This is equivalent to calling
* Array.slice() on the array and notifying the run-time about the removed * Array.slice() on the array and notifying the run-time about the removed
* rows. * rows.
* @param index * @param index
* @param size * @param size
*/ */
remove(index: number, size: number) { remove(index: number, size: number) {
let r = this.a.splice(index, size); let r = this.a.splice(index, size);

View file

@ -83,6 +83,28 @@ fn load(mut cx: FunctionContext) -> JsResult<JsValue> {
Ok(obj.as_value(&mut cx)) Ok(obj.as_value(&mut cx))
} }
fn make_signal_handler<'cx>(
cx: &mut impl Context<'cx>,
persistent_context: &persistent_context::PersistentContext<'cx>,
fun: Handle<'cx, JsFunction>,
) -> Box<dyn Fn(&[sixtyfps_interpreter::Value])> {
let fun_value = fun.as_value(cx);
let fun_idx = persistent_context.allocate(cx, fun_value);
Box::new(move |args| {
let args = args.iter().cloned().collect::<Vec<_>>();
run_with_global_contect(&move |cx, persistent_context| {
let args = args.iter().map(|a| to_js_value(a.clone(), cx).unwrap()).collect::<Vec<_>>();
persistent_context
.get(cx, fun_idx)
.unwrap()
.downcast::<JsFunction>()
.unwrap()
.call::<_, _, JsValue, _>(cx, JsUndefined::new(), args)
.unwrap();
})
})
}
fn create<'cx>( fn create<'cx>(
cx: &mut CallContext<'cx, impl neon::object::This>, cx: &mut CallContext<'cx, impl neon::object::This>,
component_type: Rc<sixtyfps_interpreter::ComponentDescription>, component_type: Rc<sixtyfps_interpreter::ComponentDescription>,
@ -103,28 +125,12 @@ fn create<'cx>(
})? })?
.clone(); .clone();
if let Type::Signal { .. } = ty { if let Type::Signal { .. } = ty {
let _fun = value.downcast_or_throw::<JsFunction, _>(cx)?; let fun = value.downcast_or_throw::<JsFunction, _>(cx)?;
let fun_idx = persistent_context.allocate(cx, value);
component_type component_type
.set_signal_handler( .set_signal_handler(
component.borrow(), component.borrow(),
prop_name.as_str(), prop_name.as_str(),
Box::new(move |args| { make_signal_handler(cx, &persistent_context, fun),
let args = args.iter().cloned().collect::<Vec<_>>();
run_with_global_contect(&move |cx, persistent_context| {
let args = args
.iter()
.map(|a| to_js_value(a.clone(), cx).unwrap())
.collect::<Vec<_>>();
persistent_context
.get(cx, fun_idx)
.unwrap()
.downcast::<JsFunction>()
.unwrap()
.call::<_, _, JsValue, _>(cx, JsUndefined::new(), args)
.unwrap();
})
}),
) )
.or_else(|_| cx.throw_error(format!("Cannot set signal")))?; .or_else(|_| cx.throw_error(format!("Cannot set signal")))?;
} else { } else {
@ -412,6 +418,23 @@ declare_types! {
Ok(JsUndefined::new().as_value(&mut cx)) Ok(JsUndefined::new().as_value(&mut cx))
} }
method connect_signal(mut cx) {
let signal_name = cx.argument::<JsString>(0)?.value();
let handler = cx.argument::<JsFunction>(1)?;
let this = cx.this();
let persistent_context =
persistent_context::PersistentContext::from_object(&mut cx, this.downcast().unwrap())?;
let lock = cx.lock();
let x = this.borrow(&lock).0.clone();
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
component.description().set_signal_handler(
component.borrow(),
signal_name.as_str(),
make_signal_handler(&mut cx, &persistent_context, handler)
).or_else(|_| cx.throw_error(format!("Cannot set signal")))?;
Ok(JsUndefined::new().as_value(&mut cx))
}
method send_mouse_click(mut cx) { method send_mouse_click(mut cx) {
let x = cx.argument::<JsNumber>(0)?.value() as f32; let x = cx.argument::<JsNumber>(0)?.value() as f32;
let y = cx.argument::<JsNumber>(1)?.value() as f32; let y = cx.argument::<JsNumber>(1)?.value() as f32;

View file

@ -88,7 +88,21 @@ try {
assert.equal(e.toString(), "Error: test_signal expect 1 arguments, but 0 where provided"); assert.equal(e.toString(), "Error: test_signal expect 1 arguments, but 0 where provided");
} }
assert.equal(instance.signal_emission_count, 0); assert.equal(instance.signal_emission_count, 0);
/// also test setHandler
instance.test_signal2.setHandler(function(a) {
signal_3_emited += 100;
signal_3_string_value = a;
});
instance.test_signal2("salùt")
assert.equal(signal_3_emited, 101);
assert.equal(signal_3_string_value, "salùt");
assert.equal(signal_3_int_value, 55); // same as before
``` ```
*/ */