Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose track info to JS controllers #14175

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

christophehenry
Copy link
Contributor

Following #13935, my proposed solution to expose track infos to JS mappings based on @Swiftb0y's idea.

src/controllers/scripting/controllerscriptenginebase.cpp Outdated Show resolved Hide resolved
Comment on lines 35 to 38
declare interface PlayerManager {
isLoaded(): boolean;
getTrack(): string;
getTitle(): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these are accessible via JS because those functions are not Q_INVOKABLE. You should however be able to directly access the declared Q_PROPERTYs. So just console.log(player.title) for example.

Also slight misnaming, this is not the playermanager:

Suggested change
declare interface PlayerManager {
isLoaded(): boolean;
getTrack(): string;
getTitle(): string;
declare interface PlayerInfo {
isLoaded(): boolean;
getTrack(): string;
getTitle(): string;

Copy link
Member

@JoergAtGithub JoergAtGithub Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the proposed getter is a much safer solution, than direct access to properties.
Please have a look on the other engine functions, which have a Proxy class, that exposes such functions as Q_INVOKABLE.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No!
afaict this would remove the .connect(handler)! That is a deal breaker. Moreover, we're now unnecessarily duplicating APIs again!

Copy link
Member

@JoergAtGithub JoergAtGithub Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need proper abstraction of the stable API from the experimental QML code. How to ensure this without a proper wrapper?
The other issue is that we need to prevent writing to internal strings in Mixxx.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How to ensure this without a proper wrapper?

QmlPlayerProxy is the wrapper.

The other issue is that we need to prevent writing to internal strings in Mixxx.

Again, what is "internal"? Everything exposed via QmlPlayerProxy should be freely readable and writable. There is no need for a second QmlPlayerProxy copy IMO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need proper abstraction of the stable API from the experimental QML code.

Well, the code has been around long enough. Lets make it stable then... The QML controller screen feature already assumes its stable.

@github-actions github-actions bot added the qml label Jan 15, 2025
@@ -31,7 +31,9 @@ constexpr double kBrakeRampToRate = 0.01;
ControllerScriptInterfaceLegacy::ControllerScriptInterfaceLegacy(
ControllerScriptEngineLegacy* m_pEngine, const RuntimeLoggingCategory& logger)
: m_pScriptEngineLegacy(m_pEngine),
m_logger(logger) {
m_logger(logger),
m_pPlayerManagerProxy(mixxx::qml::QmlPlayerManagerProxy::create(m_pEngine->jsEngine().get(), m_pEngine))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still struggling with C++'s pointers. Not sure about m_pEngine->jsEngine().get() here. I feel like ideally I should be using .get() which seem to defeat std::shared_ptr. I though I could use C++ smart pointers (shared_ptr and unique_ptr) as if they were raw pointers but no.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I see how thats confusing. In this case its fine though because create does not take the pointer for storing (in fact, its unused, we should double check that). So reaching into the shared_ptr and taking the raw one is completely fine in this case...

@@ -60,7 +60,7 @@ void QmlPlayerManagerProxy::loadLocationToPlayer(
}

// static
QmlPlayerManagerProxy* QmlPlayerManagerProxy::create(QQmlEngine* pQmlEngine, QJSEngine* pJsEngine) {
QmlPlayerManagerProxy* QmlPlayerManagerProxy::create(QJSEngine* pJsEngine, QObject* parent) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the order of arguments here to closer match the signature of QmlPlayerManagerProxy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure just make sure to double check all invocation sites. The compiler may not catch calls with the arguments mistakenly swapped due to the class hierarchy (since QQmlEngine converts implicitly to QJSEngine which also implicitly converts to QObject).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that the ordering was likely chosen deliberately because it matches the Qt example here: https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm… Wait, it's supposed to be a singleton instance but it doesn't implement the singleton pattern. The create static method doesn't check the existance of a static field like it's done on https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON.

@christophehenry christophehenry force-pushed the expose-track-info-to-mappings branch from dcae1c3 to 330cdc8 Compare January 15, 2025 17:51
Comment on lines +36 to +49
isLoaded(): boolean;
track(): string;
title(): string;
artist(): string;
album(): string;
albumArtist(): string;
genre(): string;
composer(): string;
grouping(): string;
year(): string;
trackNumber(): string;
trackTotal(): string;
comment(): string;
keyText(): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit tests that:
-Test each function
-Ensure that the test case fails if somebody add a field to PlayerInfo. This is to ensure that nobody expose unintentional objects without implementing test and documentation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats quite a lot of work and repeated code for not a lot of gain IMO...

/** QmlPlayerProxy */
declare interface PlayerInfo {
isLoaded(): boolean;
track(): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the string contain returened here? track() is too common to be self explaining.

grouping(): string;
year(): string;
trackNumber(): string;
trackTotal(): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trackTotal() is also too common. Is it number of Tracks in the Album?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants