mixxxdj / mixxx

Mixxx is Free DJ software that gives you everything you need to perform live mixes.
http://mixxx.org
Other
4.28k stars 1.24k forks source link

Create a reactive programming API for controllers #13440

Open christophehenry opened 2 weeks ago

christophehenry commented 2 weeks ago

Feature Description

This is an issue that I've already risen in the past proposing to add ability to pass arbitrary keys engine.setValue and engine.setParameter but this seem to have met some opposition.

So I'd like to start a broader discussion about adding an API to do reactive programming in controller especially now that I started working on ES6+ components JS.

What I propose is to reflect on a standard API for Mixxx, setting aside the implementation. This API could mimic ES Event API with methods like dispatchEvent, addEventListener and removeEventListener a be closer to Qt's signals/slots API (also I'm not sure what this would look like…).

This API could be standard to Component and ComponentContainer.

Regarding the implementation, I propose a first implementation in pure JS using EventTarget, CustomEvent and Proxy (this seem supported by Mixxx' JS engine, also I need to formally check) as follows:

class Component {
        __signals = new (class extends EventTarget {
            constructor() {
                super();
                return new Proxy(this, {
                    set: (target, property, value) => {
                        const previousValue = target[property];
                        target[property] = value;
                        this.dispatchEvent(new CustomEvent(property, { detail: {value, previousValue} }));
                        return true;
                    },
                });
            }
        })();

        connect(name, callback) {
            this.__signals.addEventListener(name, (evt) => {
                callback(name, evt.detail.value, evt.detail.previousValue)
            })
        }
}

that could be transparantly replaced by a C++ implem in the engine namespace thanks to the standard API.

Swiftb0y commented 2 weeks ago

I'm slightly confused, is this about a reactive programming paradigm or sharing data between QJSEngine instances?

IMO the long term plan for enabling reactive programming in Mixxx would be to programm mappings in a QQmlEngine and use native QML reactive bindings. Have you looked into that?

christophehenry commented 2 weeks ago

No I'm talking specifically about cross-component communication between components inside the same controller for complex use cases (like the one I exposed in #12766).

Correct me if I'm wrong but a QQmlEngine implementation is not comming any time soon anyway?

Swiftb0y commented 2 weeks ago

Well, actually #11407 did a good job at laying the foundations. Its currently controller screen focused, but I'm sure we can bend it to work for plain controllers as well.

christophehenry commented 1 week ago

Ok, so EventTarget is not declared by QJSEngine so my first proposition of a pure JS implementation is off the table 😔

Swiftb0y commented 1 week ago

Yeah... EventTarget is part of the DOM Spec...

Wdyt about the QML experiment?

christophehenry commented 1 week ago

I don't really know where to start. I know very little Qt, let alone QML. I don't really understand the code you pointed me. I would need some guidance. Just to be clear: if I start working on QML, it's better to drop the ES6 implementation of ComponentsJS in favor of a QML impl?

Swiftb0y commented 1 week ago

if I start working on QML, it's better to drop the ES6 implementation of ComponentsJS in favor of a QML impl?

Depends. I think we can still modernize componentsJS but rather just as a refactoring without signficant API changes (so more like #4550 instead of #13437). I would treat the QML Components separate from JS components for now.

I know very little Qt, let alone QML.

Then I'd recommend you spend a little time getting to know both. The relevant parts from Qt are really "just" signals and slots and the property system and for QML there is the QML reference (though you'll need to watch out on what belongs to just QML and what belongs to QtQuick (the GUI framework built on top of QML)).

After you have familiarized yourself with those topics, you can either apply some of the new knowledge in the form of the experimental QML skin (start a build from main with --qml) or you can start right away with disentangling some of code I linked earlier.

christophehenry commented 1 week ago

Honestly, if #13437 risks to be controversial because of API breaking changes, I'd rather start working on a QML API right away.

Swiftb0y commented 1 week ago

Sounds good to me.

Swiftb0y commented 1 week ago

Mind that before diving into an API, you'll probably need to do a little bit of refactoring to decouple the current QQmlEngine from the the controller screen usecase it was designed for.