Skip to content

Commit

Permalink
Support aliases to callbacks in globals
Browse files Browse the repository at this point in the history
  • Loading branch information
ogoffart committed Aug 24, 2021
1 parent d5f4a79 commit b07d52c
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 7 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ All notable changes to this project will be documented in this file.

### Added

- One can now set an alias from the root to a global callback

### Fixed

## [0.1.1] - 2021-08-19
Expand All @@ -25,7 +27,7 @@ All notable changes to this project will be documented in this file.
preserved. For fields in structures they are normalized.
- Show a compilation error when there are duplicated element ids.
- The `clip` property can now be any expression.

### Added

- `ComboBox` now has a `selected` callback.
Expand Down
25 changes: 25 additions & 0 deletions docs/langref.md
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,31 @@ Example := Rectangle {
}
```

It is possible to re-expose a callback or properties from a global using the two way binding syntax.

```60
global Logic := {
property <int> the-value;
callback magic-operation(int) -> int;
}
SomeComponent := Text {
// use the global in any component
text: "The magic value is:" + Logic.magic-operation(42);
}
MainWindow := Window {
// re-expose the global properties such that the native code
// can access or modify them
property the-value <=> Logic.the-value;
callback magic-operation <=> Logic.magic-operation;
SomeComponent {}
}
```

A global can be declared in another module file, and imported from many files.

## Modules

Components declared in a .60 file can be shared with components in other .60 files, by means of exporting and importing them.
Expand Down
3 changes: 3 additions & 0 deletions sixtyfps_compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,9 @@ fn handle_property_binding(
};
let prop_type = item.lookup_property(prop_name).property_type;
if let Type::Callback { args, .. } = &prop_type {
if matches!(binding_expression.expression, Expression::Invalid) {
return;
}
let mut params = args.iter().enumerate().map(|(i, ty)| {
format!("[[maybe_unused]] {} arg_{}", ty.cpp_type().unwrap_or_default(), i)
});
Expand Down
3 changes: 3 additions & 0 deletions sixtyfps_compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ fn handle_property_binding(
};

if matches!(prop_type, Type::Callback { .. }) {
if matches!(binding_expression.expression, Expression::Invalid) {
return;
}
let tokens_for_expression = compile_expression(binding_expression, component);
init.push(quote!({
sixtyfps::internal::set_callback_handler(#rust_property, &self_rc, {
Expand Down
7 changes: 4 additions & 3 deletions sixtyfps_compiler/passes/remove_aliases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ impl PropertySets {
if !std::rc::Weak::ptr_eq(
&p1.element().borrow().enclosing_component,
&p2.element().borrow().enclosing_component,
) {
// We can only merge aliases if they are in the same Component.
) && (p1.element().borrow().enclosing_component.upgrade().unwrap().is_global()
== p2.element().borrow().enclosing_component.upgrade().unwrap().is_global())
{
// We can only merge aliases if they are in the same Component. (unless one of them is global)
// TODO: actually we could still merge two alias in a component pointing to the same
// property in a parent component
return;
}

if let Some(s1) = self.map.get(&p1).cloned() {
if let Some(s2) = self.map.get(&p2).cloned() {
if Rc::ptr_eq(&s1, &s2) {
Expand Down
15 changes: 12 additions & 3 deletions sixtyfps_runtime/interpreter/global_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ pub enum CompiledGlobal {
}

pub trait GlobalComponent {
fn invoke_callback(self: Pin<&Self>, _callback_name: &str, _args: &[Value]) -> Value {
todo!("call callback")
}
fn invoke_callback(self: Pin<&Self>, callback_name: &str, args: &[Value]) -> Value;

fn set_property(self: Pin<&Self>, prop_name: &str, value: Value);
fn get_property(self: Pin<&Self>, prop_name: &str) -> Value;
Expand Down Expand Up @@ -101,6 +99,12 @@ impl GlobalComponent for GlobalComponentInstance {
comp.borrow_instance(),
)
}

fn invoke_callback(self: Pin<&Self>, callback_name: &str, args: &[Value]) -> Value {
generativity::make_guard!(guard);
let comp = self.0.unerase(guard);
comp.description().invoke_callback(comp.borrow(), callback_name, args).unwrap()
}
}

impl<T: rtti::BuiltinItem + 'static> GlobalComponent for T {
Expand All @@ -119,6 +123,11 @@ impl<T: rtti::BuiltinItem + 'static> GlobalComponent for T {
Self::properties().into_iter().find(|(k, _)| *k == prop_name).unwrap().1;
unsafe { (self.get_ref() as *const Self as *const u8).add(prop.offset()) as *const () }
}

fn invoke_callback(self: Pin<&Self>, callback_name: &str, args: &[Value]) -> Value {
let cb = Self::callbacks().into_iter().find(|(k, _)| *k == callback_name).unwrap().1;
cb.call(self, args).unwrap()
}
}

pub(crate) fn generate(component: &Rc<Component>) -> CompiledGlobal {
Expand Down
63 changes: 63 additions & 0 deletions tests/cases/globals/global_callback.60
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* LICENSE BEGIN
This file is part of the SixtyFPS Project -- https://sixtyfps.io
Copyright (c) 2021 Olivier Goffart <olivier.goffart@sixtyfps.io>
Copyright (c) 2021 Simon Hausmann <simon.hausmann@sixtyfps.io>

SPDX-License-Identifier: GPL-3.0-only
This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information.
LICENSE END */

global Glo := {
property <int> hello: 42;
callback sum(int, int) -> int;
callback mul(int, int) -> int;
}

ExtraComp := Rectangle {
property<int> five: Glo.sum(3, 2);
property<int> six: Glo.mul(3, 2);
}


TestCase := Window {
callback sum <=> Glo.sum;
callback mul <=> Glo.mul;

x := ExtraComp {}
property<int> five: x.five;
property<int> six: x.six;

//mul(α, β) => { return α * β; }
property<bool> test: five == 0; // because the callback is not set

}

/*
```rust
let instance = TestCase::new();
instance.on_sum(|a, b| a + b);
instance.on_mul(|a, b| a * b);
assert_eq!(instance.get_five(), 5);
assert_eq!(instance.get_six(), 6);
```

```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;
instance.on_sum([](int a, int b) { return a + b; });
instance.on_mul([](int a, int b) { return a * b; });
assert_eq(instance.get_five(), 5);
assert_eq(instance.get_six(), 6);
```

```js
let instance = new sixtyfps.TestCase({
sum: function(a, b) { return a + b; },
mul: function(a, b) { return a * b; }
});
assert.equal(instance.five, 5);
assert.equal(instance.six, 6);
```

*/

0 comments on commit b07d52c

Please sign in to comment.