This week in SixtyFPS

25th of October 2021 to 31st of October 2021

Posted on November 1, 2021

SixtyFPS is a toolkit to efficiently develop fluid graphical user interfaces for any display: embedded devices and desktop applications. We support multiple programming languages, such as Rust, C++, and JavaScript. Find more information at https://sixtyfps.io/ or go straight to github at https://github.com/sixtyfpsui/sixtyfps

SixtyFPS UI Library

This week we worked primarily on a feature internal to the compiler we call "de-inlining".

The SixtyFPS compiler takes components in .60 files and generates C++ and Rust code. The following .60 code maps to this pseudo Rust code:

CountingButton := TouchArea {
    property <int> counter;
    Text {
        text: "pressed \{root.counter} times!";
    }
    clicked => {
        counter = counter + 1;
    }
}

App := Window {
    btn1 := CountingButton {
    }
    btn2 := CountingButton {
        y: 50px;
    }
}
    
// (note that this is all pseudo-code, for readability)
struct App {
    btn1_counter: Property<int>,
    btn1_touch_area: BuiltinTouchArea,
    btn1_label: BuiltinTextItem,
    btn2_counter: Property<int>,
    btn2_touch_area: BuiltinTouchArea,
    btn2_label: BuiltinTextITem
}

impl App {
    fn new() -> Self {        
        self.btn1_label.text.set_binding(|| {
            format!("pressed {} times", self.btn1_counter.get())
        });
        self.btn1_touch_area.set_handler|| {
            self.btn1_counter.set(self.btn1_counter.get() + 1);
        });
        self.btn2_label.text.set_binding(|| {
            format!("pressed {} times", self.btn2_counter.get())
        });
        self.btn2_touch_area.set_handler|| {
            self.btn2_counter.set(self.btn2_counter.get() + 1);
        });
        self.btn2_touch_area.y.set(50);
    }
}

This generated code practically embeds all of CountingButton fields and binding code in the App struct. We call this inlining and it allows for nice optimizations and keeps the compiler simple. However a consequence of this inlining is that the resulting generated code becomes big and some parts of it are duplicated. A while ago we started the project of not inlining and this week we've merged the bulk of that into the mainline branch in order to finish it soon. The objective is to provide a more direct mapping between the components declared in .60 code and the generated Rust and C++ code, resulting in faster build times and smaller binaries. This is how the pseudo code should look like when not inlining:

struct CountingButton {
    counter: Property<int>,
    touch_area: BuiltinTouchArea,
    label: BuiltinTextItem
}

impl CountingButton {
    fn init(&self) {
        self.label.text.set_binding(|| {
            format!("pressed {} times", self.counter.get())
        });
        self.touch_area.set_handler|| {
            self.counter.set(self.counter.get() + 1);
        });
    }
}

struct App {
    btn1: CountingButton,
    btn2: CountingButton,
}

impl App {
    fn new() -> Self {
        self.btn1.init();
        self.btn2.init();
        self.btn2.touch_area.y.set(50);
    }
}

Almost all commits this week are related to this feature and the summary of individual commits did not make much sense. So this week's summary is a bit different than other weeks. Thanks for reading.

Statistics

61 patches were committed by 3 authors.