36 lines
6.6 KiB
Markdown
36 lines
6.6 KiB
Markdown
# Custom ViewModels and Computed bindings
|
|
|
|
The shared **Bindings → Discover targets** panel now identifies the declared Model, selected ViewModel and Controller, lists custom properties, and shows public zero-argument application methods as Computed sources. **Find Converters / Validate** and the automatic preview gate validate existing Direct and Computed declarations in both hosts. This slice validates Computed bindings edited in Source; it does not add a Computed-binding authoring form.
|
|
|
|
## Runtime selection and property precedence
|
|
|
|
Discovery reads NeitzelLib's `@ViewModelClass` on the FXML Controller. For Component `fx:root` documents it uses the captured resource path (`package/ClassName.fxml`) to identify the Component class, which is also the Controller. The worker receives this identity from the same unambiguous source-root mapping used for draft overlays. The root's actual Component class supplies its target properties, including additions beyond its FXML base type. An unavailable Component class/context is reported as unresolved; a conflicting `fx:controller` on Component FXML is an error.
|
|
|
|
With no explicit selection, discovery uses generic ViewModel capabilities, independent of incidental runtime reuse from another View. A subclass adds public no-argument accessors returning JavaFX `Property` to the inherited Model Attributes. Runtime precedence is preserved: reserved `latestMessage` wins, then Model Attributes, then additional property accessors. Generic nested Model paths also expose their message property. A replacement exposes only its own named JavaFX property API; it does not inherit Model Attributes, built-in message properties or dotted traversal through plain objects.
|
|
|
|
The selected class must be concrete and have a constructor accepting the declared Model. The most-specific compatible constructor is checked for access from NeitzelLib's module; incomparable overloads produce an ambiguity warning. No constructor or accessor runs during discovery. An overridden `ViewModel.property(String)` is dynamic: signature-derived results remain unverified and produce warnings. Raw/generic property signatures that cannot establish a value type stay unresolved. Read-only ObservableValue accessors are not advertised as bindable ViewModel properties, matching the runtime's requirement for `Property`.
|
|
|
|
## Computed validation
|
|
|
|
Discovery excludes Object methods and inherited JDK/JavaFX framework methods from the application-method inventory. Void and parameterized methods are unavailable as sources. Public inherited application methods are included. Method bodies never run during discovery.
|
|
|
|
Validation checks the declared Controller, public zero-argument method, reflective accessibility to NeitzelLib, target fx:id, writable JavaFX property, duplicate Direct/Computed targets and boxed return-type assignability. Private `@FXML` methods are not sufficient for NeitzelLib Computed bindings. Object or unresolved value types yield warnings; missing methods, void results and known incompatible types are errors. Existing unprefixed method names remain accepted because the runtime also accepts them; the inventory displays `#method`.
|
|
|
|
Computed validation still runs when a Model declaration is missing: that limits Model discovery but does not hide a known Controller-method error. Errors block new previews while saving invalid drafts and normal host Undo/Redo remain available. Results retain build/draft freshness checks. Pending target discovery can also satisfy an automatic preview-validation request, avoiding replacement of the user's discovery operation by a canvas refresh.
|
|
|
|
## Boundaries
|
|
|
|
Application classes stay in the target worker and are loaded without initialization. The `bindings-v3` capability and metadata format 3 add Controller/ViewModel identity and Computed signatures, plus the captured resource on requests. Old metadata capabilities are rejected rather than silently decoded. Existing protocol identities, payload limits, path/target limits and worker cancellation apply; the method inventory is bounded to 256 entries.
|
|
|
|
Generated metadata, custom BeanInfo execution, arbitrary dynamic property resolvers, full generic inference, cross-View first-use conflicts, nested ViewModel selection caused by other Views/Components, collection authoring and dependency-injection viability remain outside this slice. Method bodies, null results, constructor side effects and Computed refresh behavior are runtime concerns; compatible signatures do not prove that application code will succeed. In particular, discovery does not change which additional properties NeitzelLib observes for Computed refresh. Complete IDE Problems integration and a Computed authoring form remain later work.
|
|
|
|
## Automated qualification
|
|
|
|
Unit checks cover subclass/replacement selection, inherited properties and methods, name precedence, built-in messages, constructor mismatch, dynamic resolvers, Component resource identity/root properties, method accessibility and return types, missing Model declarations and non-initialization.
|
|
|
|
Real classpath and named-module fixtures check custom, replacement and Component metadata without initializing application classes. Separate fresh target processes load subclass/replacement Views through NeitzelLib, exercise custom-property write-back, confirm Model-name precedence and execute Computed methods. Shared host contracts verify resource identity through the worker transport. Live standalone and registered IntelliJ checks inspect custom properties and methods, save a type-invalid Computed binding, verify preview blocking and exact Undo/Redo, and discover replacement properties without mutating FXML. Existing rendering, Converter, input and lifecycle checks remain enabled. No manual test is required; previously skipped human checks remain skipped.
|
|
|
|
Qualification completed on 2026-11-13 on the pinned macOS baseline: `check-bootstrap.py` passed with **91 unit/adapter tests** (84 Maven, 7 IntelliJ), **36 fresh target processes / 484 fixture assertions**, both native standalone launch modes, both connected host adapters and **48 worker lifecycle cycles**. `check-ide-host.py` passed the registered IntelliJ editor scenarios for both classpath and module-path applications, including exclusive JavaFX 26 native-library verification and worker shutdown. No manual testing was used.
|
|
|
|
Local run evidence: `/tmp/viewmodel-bootstrap.log`, `/tmp/viewmodel-registered-ide.log`, `target/connected-workflow/` and `target/ide-workflow/{classpath,modulepath}.json`. Malformed-XML diagnostics are intentional recovery-test inputs. No uncaught application exception or native crash was found in these qualification runs. This is evidence for the pinned baseline, not a broader platform certification.
|