A narrated walkthrough that drives the lab through its own mutation API. More...
| Import Statement: | import Clayground.Lab |
A flow is an ordered list of FlowStep. Demonstration steps run the lab's own verbs (from the lab's flowActions() map) so the learner sees the lab being operated; task steps wait for a condition on lab state. Pacing is measured in sim seconds, so a flow traverses the same states live and headless, and SimClock.timeScale scales it.
While a flow runs the board is the flow's, and a task lends back exactly the one interaction it asks for - see control. That is the whole difference between a lesson and a demo somebody can walk over: the professor cannot explain the current that flows while a second click is reopening the switch.
Example usage:
import Clayground.Lab Flow { id: flow lab: root; flowId: "led-basics" FlowStep { key: "place" demo: [["let", "bat", "addPart", "battery", 6, 2]] } FlowStep { key: "flip" task: { "until": (n) => root.elemAt(n("sw")).on, "allow": ["sw"], // the one thing that is live "solve": [["flipSwitch", "sw"]] } } }
See also FlowStep, Narrator, and LabLang.
camera : var |
The lab's camera rig, for steps that carry a FlowStep::view.
An OrbitCamera3D (anything with goTo / focusOn / applyState will do). Left null, FlowStep.view is ignored - which is the whole of the compatibility story: a flow that never wires a camera behaves exactly as it did before the property existed.
control : string |
Who has the board right now: "learner", "flow" or "task".
A lesson alternates the way a game tutorial does. While no flow runs the board is the learner's and everything works. The moment one starts it is the flow's: it is building and explaining, and a touch on the board is refused rather than obeyed. A task step opens exactly what it named (task.allow) and nothing else - that is task - and the instant its until holds the board is the flow's again. Full control comes back by leaving the flow (stop()), not by touching something mid-lesson.
dwellTarget : real |
Sim seconds this step is estimated to need (0 while waiting).
flowId : string |
Stable id; prefixes narration keys and identifies the flow.
hintShown : bool |
index : int |
Active step, -1 while the flow is not running.
lab : var |
The sandbox root: source of flowActions() and of the checkpoints.
marks : var |
The active step's FlowStep::mark names; empty when idle.
One binding for the lab: hand it to whatever resolves and draws the marks (the professor kit's FlowGuide, or a MarkLayer directly). It empties itself when the flow stops, so a mark cannot outlive the lesson.
narration : string |
The active step's text in the current language.
pacing : string |
How a step ends: "ready" (default), "auto" or "manual".
ready uses the reading estimate as a ripening time, not as an advance: while it runs the Next control is quiet, and when it elapses Next becomes prominent and the learner confirms. Nobody is hurried and nobody waits - Next stays clickable throughout, so a learner who already knows this step can skip straight past it.
auto advances by itself once the estimate elapses (kiosk mode, recordings, and the headless verification run); manual offers no estimate at all.
A task step is the exception in every mode: it ends when its until holds (or solve is asked for), never on a Next - the Narrator offers none while waiting - so no later line can assume the learner did something they did not.
pending : bool |
Waiting on the learner or on the sim.
readyProgress : real |
0..1 progress through the estimate; 1 means "read it, go on".
readySince : real |
0..1 progress through the estimate; 1 means "read it, go on".
refusal : string |
ripe : bool |
The estimate has elapsed: Next is the obvious thing to do now.
running : bool |
step : FlowStep |
steps : list<FlowStep> |
The steps (default property).
title : string |
titleKey : string |
Dictionary key of the flow's title.
unresolvedVerbs : var |
Verb names a demo or a solve asked for and the lab does not have.
Cleared by start(). A verb that does not resolve fails SILENTLY - the action does nothing and the flow walks on teaching nothing - so a headless run has to be able to read the list rather than hope somebody was watching the log.
See also Lab::runFlow.
waiting : bool |
In a task: the learner must act.
finished() |
Note: The corresponding handler is onFinished.
narrated(string text, string lang, string key) |
Note: The corresponding handler is onNarrated.
void applyView(var v) |
Moves camera as a FlowStep::view asks; a no-op without one.
bool check() |
Re-tests the running task's until at once; advances if it holds.
The clock samples ten times a second, which is late enough for a second click to land on a task the first one already satisfied - the exact thing a locked board exists to stop. Whoever performed a granted interaction calls this the moment the gesture is over, so the board is the flow's again before the next press can arrive.
void goTo(int i) |
Jumps to a step: restores its checkpoint, then replays its demo.
Every step is entered from a stored lab state, so stepping back or scrubbing never has to replay the whole flow.
bool grants(var id) |
May the learner touch part id right now?
True whenever no flow runs. While one does, only during a task step and only for what that task's allow named - resolved through nameOf, so a task names the part the way its until and its solve do ("allow": ["sw"]). A task whose subject only exists once its own demo has run gives a function instead ("allow": (n) => root.logicInputs), evaluated per press. A task that names nothing keeps the whole board live, which is what every flow written before this existed still gets.
void next() |
void prev() |
void refuse() |
Says why the touch just refused did nothing, for a moment.
void replayStep() |
Re-enters the current step from its checkpoint.
void run(var actions) |
Executes an action list against the lab's verbs.
string sayOf(QtObject step) |
Narration for a step: dictionary entry for its key, else say.
void solve() |
Performs the current task for the learner ("show me").
void start() |
Starts at the first step, dropping earlier checkpoints.
void stop() |
Leaves the flow - and hands the whole board back, at once.