mirror of
https://github.com/logos-co/logos-view-module-runtime.git
synced 2026-08-31 04:51:07 +00:00
149 lines
6.6 KiB
C++
149 lines
6.6 KiB
C++
#pragma once
|
|
|
|
#include <QObject>
|
|
#include <QString>
|
|
#include <QVariant>
|
|
#include <QVariantList>
|
|
#include <QJSValue>
|
|
#include <QMap>
|
|
|
|
class LogosAPI;
|
|
class QRemoteObjectNode;
|
|
class QAbstractItemModelReplica;
|
|
class LogosViewReplicaFactory;
|
|
class QPluginLoader;
|
|
|
|
// ── LogosQmlBridge ───────────────────────────────────────────────────────────
|
|
//
|
|
// Single entry point for QML to talk to modules.
|
|
//
|
|
// Non-view (backend) modules — go through LogosAPI IPC:
|
|
// logos.callModule(name, method, [args]) // sync, returns JSON
|
|
// logos.callModuleAsync(name, method, [args], cb, ms) // async, JSON payload
|
|
//
|
|
// View modules — each view module ships a typed replica factory plugin
|
|
// (generated by logos_module() from a .rep file). The host loads it and
|
|
// exposes the typed replica directly to QML, so this API is dead simple:
|
|
//
|
|
// property var backend: logos.module("my_view_module")
|
|
// Text { text: backend.someProperty }
|
|
// Button { onClicked: backend.someSlot() }
|
|
// Connections { target: backend; function onSomeSignal() { ... } }
|
|
//
|
|
// // Enums declared in the .rep are available under the QML URI the
|
|
// // factory registered (typically "Logos.<ModuleName>"):
|
|
// import Logos.MyViewModule 1.0
|
|
// Text { text: backend.status === MyViewModule.Active ? "on" : "off" }
|
|
//
|
|
// // QAbstractItemModel* Q_PROPERTYs are still remoted separately:
|
|
// ListView { model: logos.model("my_view_module", "items") }
|
|
//
|
|
class LogosQmlBridge : public QObject {
|
|
Q_OBJECT
|
|
public:
|
|
explicit LogosQmlBridge(LogosAPI* api, QObject* parent = nullptr);
|
|
|
|
// ── Backend (non-view) module calls via LogosAPI IPC ────────────────
|
|
Q_INVOKABLE QString callModule(const QString& module,
|
|
const QString& method,
|
|
const QVariantList& args = QVariantList());
|
|
|
|
// timeoutMs: if > 0, the callback is invoked with an error payload when
|
|
// no reply arrives within that many milliseconds. Default 30000 (30s);
|
|
// pass 0 to disable.
|
|
Q_INVOKABLE void callModuleAsync(const QString& module,
|
|
const QString& method,
|
|
const QVariantList& args,
|
|
QJSValue callback,
|
|
int timeoutMs = 30000);
|
|
|
|
// ── View module API ─────────────────────────────────────────────────
|
|
|
|
// Returns the typed replica for a view module. Safe to call before the
|
|
// replica is Valid — QML will see default-constructed values until then
|
|
// and property NOTIFYs fire once the source meta arrives.
|
|
Q_INVOKABLE QObject* module(const QString& moduleName);
|
|
|
|
// Returns a QAbstractItemModelReplica* for a QAbstractItemModel*
|
|
// Q_PROPERTY on the view module. ui-host auto-remotes every such
|
|
// property as "<module>/<propertyName>".
|
|
Q_INVOKABLE QObject* model(const QString& moduleName,
|
|
const QString& modelName);
|
|
|
|
// True once the view module's replica is Valid (source meta received).
|
|
Q_INVOKABLE bool isViewModuleReady(const QString& moduleName) const;
|
|
|
|
// Watch a QRemoteObjectPendingCall returned by a replica slot call and
|
|
// invoke callbacks with the result. Replaces QtRemoteObjects.watch() so
|
|
// QML plugins don't need to import QtRemoteObjects (keeping the sandbox
|
|
// locked down).
|
|
//
|
|
// Usage:
|
|
// logos.watch(backend.add(1, 2),
|
|
// function(value) { display.text = value },
|
|
// function(error) { console.log(error) })
|
|
Q_INVOKABLE void watch(const QVariant& pendingCall,
|
|
QJSValue onSuccess,
|
|
QJSValue onError = QJSValue());
|
|
|
|
// Called by the host application during setup.
|
|
void setViewModuleSocket(const QString& moduleName, const QString& socketName);
|
|
void setViewReplicaPlugin(const QString& moduleName, const QString& pluginPath);
|
|
|
|
// Called when the view module's child process exits unexpectedly.
|
|
void notifyViewModuleCrashed(const QString& moduleName);
|
|
|
|
// ── Test-friendly helpers ───────────────────────────────────────────
|
|
//
|
|
// Stateless serializer for a QVariant result into the JSON string
|
|
// shape QML consumers expect. Exposed as a public static method so
|
|
// unit tests (and callers who want the same format without going
|
|
// through callModule()) can exercise it directly.
|
|
static QString serializeResultForTesting(const QVariant& result);
|
|
|
|
// Introspection helpers used by unit tests to assert routing state
|
|
// without needing a real QRemoteObjectNode.
|
|
bool hasViewModuleSocket(const QString& moduleName) const;
|
|
QString viewModuleSocket(const QString& moduleName) const;
|
|
QString viewReplicaPluginPath(const QString& moduleName) const;
|
|
|
|
// Subscribe to events emitted by core (non-view) modules. When the
|
|
// module emits an event, the moduleEventReceived signal fires.
|
|
//
|
|
// Usage:
|
|
// Component.onCompleted: logos.onModuleEvent("calc_module", "resultReady")
|
|
// Connections {
|
|
// target: logos
|
|
// function onModuleEventReceived(moduleName, eventName, data) {
|
|
// if (eventName === "resultReady")
|
|
// console.log("Got result:", JSON.stringify(data))
|
|
// }
|
|
// }
|
|
//
|
|
// Returns true if the subscription succeeded.
|
|
Q_INVOKABLE bool onModuleEvent(const QString& moduleName, const QString& eventName);
|
|
|
|
signals:
|
|
void viewModuleReadyChanged(const QString& moduleName, bool ready);
|
|
void viewModuleCrashed(const QString& moduleName);
|
|
void moduleEventReceived(const QString& moduleName,
|
|
const QString& eventName,
|
|
const QVariantList& data);
|
|
|
|
private:
|
|
void dropViewModuleCaches(const QString& moduleName);
|
|
QRemoteObjectNode* getOrCreateNode(const QString& moduleName);
|
|
LogosViewReplicaFactory* loadFactory(const QString& moduleName);
|
|
|
|
LogosAPI* m_logosAPI;
|
|
|
|
// Per-view-module state
|
|
QMap<QString, QString> m_viewModuleSockets;
|
|
QMap<QString, QString> m_replicaPluginPaths;
|
|
QMap<QString, QRemoteObjectNode*> m_replicaNodes;
|
|
QMap<QString, QPluginLoader*> m_factoryLoaders;
|
|
QMap<QString, LogosViewReplicaFactory*> m_factories;
|
|
QMap<QString, QObject*> m_replicas;
|
|
QMap<QString, QAbstractItemModelReplica*> m_modelReplicas;
|
|
};
|