How a click becomes a document edit
This page explains the shape of the system from the outside in. It is the answer to a question the how-to guides skip: why does the code look the way it does?
The facade is the contract, and it keeps no state
import FreeCAD is the product surface. Everything else — the shell, a workbench
module, a test — is a client of it.
The Python package is a thin facade over a Rust core, and the facade deliberately owns nothing. Documents, objects, properties and undo all live in the core; the facade forwards. That is why you can close a document from one script and read it from another: there is no per-script model to fall out of sync.
The same property is what makes the workflow pages in this guide possible. They create documents, edit them and delete them without a window, because there is nothing window-shaped in the model.
The shell is a client, not a layer
The application shell does not link a workbench and does not reach into the
document model directly. It calls a small set of Python functions
(ferrocad_shell), each of which returns JSON:
| The shell asks for | The function | What comes back |
|---|---|---|
| the document tree | model_tree() | names, labels, type names |
| one object’s editors | properties(name) | rows for the property pane |
| what the viewport draws | stage_meshes() | tessellated shapes |
| the actions | commands() | ids, groups, enablement, check state |
| to run one | run_command(id) | a status, or a dialog and a token |
Every one of those is an ordinary call into FreeCAD or FreeCADGui. Nothing in
ferrocad_shell names a workbench. That is a rule, not a style preference: it is
what lets the same shell host the toy edition, a Part edition, or a future
workbench without a rebuild. See
docs/workbenches/actions-and-tools.md.
A click, step by step
- The viewport reports a pick. The shell turns it into a selection snapshot and
pushes it into
FreeCADGui.Selection. - A command’s
IsActiveis evaluated against that snapshot, so the button enables or disables without anyone telling it to. - Running the command returns one of a small number of things: a status, a
{"view": …}request, a{"tool": …}request, or a dialog description plus a token. - A dialog is data, not a callback. The shell renders the fields natively and
sends the typed values back to the token. The command’s
on_acceptthen opens the transaction.
That last point is the one people find surprising. Workbenches do not build Qt widgets in FerroCAD; they describe a form. The description is ordinary Python data, so it crosses the JSON boundary, and the window decides how it looks. This is also why a dialog can be tested with no window at all.
- The edit itself is one document transaction, so
undoreverses the click, not a half-applied intermediate state.
Several providers, one feature
Where two modules can supply the same capability — a grid, for instance — they
register as providers of a feature rather than competing silently. The
registry picks one (priority, then a saved choice), and only the active backend
draws. Mods_Settings switches them.
This is why the grid has more than one implementation and still one UI: the
grid feature owns the command and the appearance settings, and the provider
owns the drawing. docs/workbenches/mod-providers.md
specifies the contract.
The consequence is a rule for anything that adds a provider: the switch that
turns a feature on and off belongs to the feature, not to the provider. When
two stores disagree about “on”, a toggle moves one of them and the other keeps
drawing — the defect recorded in
docs/tasks/grid-visibility.md.
What this buys
- Workflows are testable without a GPU or a display. The model path is the same one a window uses; only the pixels differ.
- The window is replaceable. A different shell can drive the same facade.
- Documentation can be executed. The pages in this guide are generated by running the code they show, which is only possible because the model is reachable without the UI.
Where to read further
docs/architecture.md— the crate graph and the call paths.docs/interaction/axis-origin-grid.md— the spatial model the grid and the navigation cube share.../how-to/toy-workbench.md— the contract above, executed.