Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified outputs/images/calc-cpp-buttons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added outputs/images/calc-cpp-event.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified outputs/images/calc-cpp-result.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions outputs/logos-calc-ui-cpp/src/calc_ui_cpp.rep
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
class CalcUiCpp
{
// ── SLOTs — call-and-return; each reply reaches QML via logos.watch() ──
SLOT(int add(int a, int b))
SLOT(int multiply(int a, int b))
SLOT(int factorial(int n))
SLOT(int fibonacci(int n))
SLOT(QString libVersion())

// Void slot — fire-and-forget. Asks calc_module to (re-)announce
// its version as a `versionReady` event; there's no return value
// to await, the answer comes back through the PROP below.
SLOT(void announceVersion())

// ── PROPs — auto-synced backend → every QML replica, no polling ──
// QString, event-fed: the typed `versionReady` subscription the
// backend arms in onContextReady() writes it. Starts empty.
PROP(QString versionEvent="" READONLY)

// int, slot-driven: the backend bumps it after each calculation,
// so the view shows a live tally without ever polling.
PROP(int computeCount=0 READONLY)

// int, READWRITE: a memory register the QML view both *reads* and
// *writes* (Store / Clear buttons), and the backend may set too.
// A write round-trips QML → replica → source → back to every replica.
PROP(int memory=0 READWRITE)

// ── SIGNAL — backend → view push, distinct from a return value ──
// Emitted after each calculation. QML catches it with a
// Connections block (not logos.watch(), not a PROP read).
SIGNAL(computed(QString op, int result))
}
51 changes: 47 additions & 4 deletions outputs/logos-calc-ui-cpp/src/calc_ui_cpp_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,30 @@

int CalcUiCppBackend::add(int a, int b)
{
return modules().calc_module.add(a, b);
int result = modules().calc_module.add(a, b);
record("add", result);
return result;
}

int CalcUiCppBackend::multiply(int a, int b)
{
return modules().calc_module.multiply(a, b);
int result = modules().calc_module.multiply(a, b);
record("multiply", result);
return result;
}

int CalcUiCppBackend::factorial(int n)
{
return modules().calc_module.factorial(n);
int result = modules().calc_module.factorial(n);
record("factorial", result);
return result;
}

int CalcUiCppBackend::fibonacci(int n)
{
return modules().calc_module.fibonacci(n);
int result = modules().calc_module.fibonacci(n);
record("fibonacci", result);
return result;
}

QString CalcUiCppBackend::libVersion()
Expand All @@ -30,3 +38,38 @@ QString CalcUiCppBackend::libVersion()
// (api-style qt), matching the .rep slot — no conversion needed.
return modules().calc_module.libVersion();
}

void CalcUiCppBackend::record(const QString& op, int result)
{
// PROP: bump the slot-driven counter. setComputeCount() is the
// generated setter; Qt Remote Objects syncs the new value to every
// replica, so the view's "Computations" label updates with no polling.
setComputeCount(computeCount() + 1);

// SIGNAL: a backend → view push, distinct from the return value the
// QML side gets via logos.watch(). `computed` is declared on the
// generated SimpleSource, so we just emit it; the typed replica
// re-emits it and the view's Connections block catches it.
emit computed(op, result);
}

void CalcUiCppBackend::announceVersion()
{
// Fire-and-forget call into calc_module: it looks up the library
// version and emits it as a `versionReady` event. We don't read a
// return value here — the event comes back through the subscription
// armed in onContextReady() below.
modules().calc_module.libVersionNotify();
}

void CalcUiCppBackend::onContextReady()
{
// Typed module-event subscription. `versionReady` is calc_module's
// event (Part 1's `logos_events:` block); the generated wrapper
// exposes it as on<Event> + a Qt-typed callback (QString, because a
// UI plugin is api-style qt). Push each payload into the versionEvent
// PROP — Qt Remote Objects then auto-syncs it to the QML replica.
modules().calc_module.onVersionReady([this](const QString& version) {
setVersionEvent(version);
});
}
15 changes: 15 additions & 0 deletions outputs/logos-calc-ui-cpp/src/calc_ui_cpp_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,19 @@ class CalcUiCppBackend : public CalcUiCppSimpleSource,
int factorial(int n) override;
int fibonacci(int n) override;
QString libVersion() override;

// Tells calc_module to emit its `versionReady` event.
void announceVersion() override;

// Fires once when ui-host hands the plugin its LogosAPI — the
// typed dependency surface is live, so we arm the event
// subscription here (before the view's first call).
void onContextReady() override;

private:
// Feeds the non-slot surfaces of the .rep after each calculation:
// bumps the computeCount PROP (setComputeCount, generated) and
// emits the `computed` SIGNAL. The READWRITE `memory` PROP is
// driven from QML, so the backend doesn't have to touch it.
void record(const QString& op, int result);
};
78 changes: 78 additions & 0 deletions outputs/logos-calc-ui-cpp/src/qml/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Item {
property string result: ""
property string errorText: ""

// Last payload from the backend's `computed` SIGNAL (see Connections below).
property string lastSignal: "(none)"

// Typed replica of the backend running in ui-host (generated from calc_ui_cpp.rep).
readonly property var backend: logos.module("calc_ui_cpp")

Expand All @@ -29,6 +32,17 @@ Item {
root.ready = root.backend !== null && logos.isViewModuleReady("calc_ui_cpp")
}

// SIGNAL from the .rep: the backend emits `computed(op, result)` after
// each calculation. The typed replica re-emits it, so we catch it with
// a Connections block — no logos.watch(), no property read. This is the
// backend → view push path, distinct from the slot return value above.
Connections {
target: root.backend
function onComputed(op, result) {
root.lastSignal = op + " = " + result
}
}

// logos.watch() delivers the result of a replica slot call via callbacks.
// No QtRemoteObjects import needed — the bridge handles it.
function callCalc(method, args) {
Expand Down Expand Up @@ -123,6 +137,15 @@ Item {
enabled: root.ready
onClicked: root.callCalc("libVersion", [])
}

Button {
// Fires the event path: asks calc_module to emit
// versionReady. No logos.watch() — the result comes
// back through the versionEvent PROP, not a return value.
text: "Announce version (event)"
enabled: root.ready
onClicked: root.backend.announceVersion()
}
}

Rectangle {
Expand All @@ -140,6 +163,61 @@ Item {
}
}

// Slot-driven PROP: bumped by the backend's record() after each
// calculation. A plain property read — auto-syncs, no polling.
Text {
text: "Computations: " + ((root.ready && root.backend) ? root.backend.computeCount : 0)
color: "#cdd6f4"
font.pixelSize: 14
Layout.alignment: Qt.AlignHCenter
}

// SIGNAL payload, captured by the Connections block above.
Text {
text: "Last op (signal): " + root.lastSignal
color: "#94e2d5"
font.pixelSize: 14
Layout.alignment: Qt.AlignHCenter
}

// READWRITE PROP: the memory register. The label *reads*
// backend.memory; the buttons *write* it. A write round-trips
// QML → replica → backend source → back to every replica, so the
// label updates once the new value syncs home.
RowLayout {
spacing: 12
Layout.alignment: Qt.AlignHCenter

Text {
text: "Memory: " + ((root.ready && root.backend) ? root.backend.memory : 0)
color: "#cdd6f4"
font.pixelSize: 14
}

Button {
text: "Store (MS)"
enabled: root.ready
onClicked: root.backend.memory = parseInt(root.result) || 0
}

Button {
text: "Clear (MC)"
enabled: root.ready
onClicked: root.backend.memory = 0
}
}

// Event-fed label: the versionEvent PROP auto-syncs from the
// backend's typed versionReady subscription. No polling — it
// updates the moment calc_module emits.
Text {
readonly property string ev: (root.ready && root.backend) ? root.backend.versionEvent : ""
text: "Version event: " + (ev.length > 0 ? ev : "(none yet)")
color: "#f9e2af"
font.pixelSize: 15
Layout.alignment: Qt.AlignHCenter
}

Item { Layout.fillHeight: true }
}
}
Loading
Loading