Local and remote views in Phlow using a single view class

A Phlow view can have one definition that works for both local and remote inspection. A <gtView> method configures a GtRemotePhlowLocalView GtRemotePhlowView << #GtRemotePhlowLocalView slots: { #localOriginalView . #localBuildContext . #localDefiningMethodProvider }; package: 'GToolkit-RemotePhlow-PhlowViews' subclass. The view retains the domain-facing API and computations, while the declarative pipeline transports a serializable description and concrete rendering data to the inspector that displays it.

There are two supported forms. The generic concrete-data form is the default: define a view, a serializable data value, and rendering stencil(s), then reuse the generic specification and data source. The custom pipeline form is for views that need specialized data retrieval, state, or refresh behavior and therefore define their own specification and data source.

Every local/remote view has the same responsibilities, whether they are implemented by generic framework classes or custom classes:

- The view is configured by a <gtView> method. It owns the public builder API and computation blocks.

- The specification is the serializable view declaration. It carries view metadata such as title, priority, actions, transport mode, and included data, and creates the local explicit view.

- The data source evaluates computations near the inspected object and answers serializable concrete data or error data.

- The view data contains only concrete transportable values used for rendering.

- The declarative stencil turns concrete values into the local UI element. A named stencil allows callers to select a renderer; a GtPhlowStencil GtPhlowDeclarativeSpecification << #GtPhlowStencil slots: {}; package: 'GToolkit-RemotePhlow-Stencils' is a directly serializable fixed renderer.

The specification uses GtPhlowExplicitView>>#declarativeStencil: declarativeStencil: aStencilBuilder stencilBuilder := aStencilBuilder asPhlowDeclarativeStencilBuilder so the local rendering also participates in the remote declarative pipeline. The boundary is important: computation blocks stay with the view/data source; specifications, view data, and declarative stencils must be serializable.

GtRemotePhlowPictureView GtRemotePhlowLocalView << #GtRemotePhlowPictureView slots: { #pictureComputation . #widthComputation . #heightComputation . #mediaTypeComputation . #renderingStencilName . #shouldLoadContentLazily }; package: 'GToolkit-RemotePhlow-PhlowViews' is the preferred model for a new view that computes one cohesive custom data object. The application defines:

1. a GtRemotePhlowLocalView GtRemotePhlowView << #GtRemotePhlowLocalView slots: { #localOriginalView . #localBuildContext . #localDefiningMethodProvider }; package: 'GToolkit-RemotePhlow-PhlowViews' subclass with its builder API and GtRemotePhlowPictureView>>#computeViewData computeViewData ^ GtRemotePhlowPictureViewData new content: self pictureComputation value; mediaType: self mediaTypeComputation value; width: self widthComputation value; height: self heightComputation value ;

2. a GtRemotePhlowBasicViewData GtPhlowDeclarativeSpecification << #GtRemotePhlowBasicViewData slots: {}; package: 'GToolkit-RemotePhlow-DeclarativeViews' subclass, here GtRemotePhlowPictureViewData GtRemotePhlowViewData << #GtRemotePhlowPictureViewData slots: { #content . #mediaType . #width . #height }; package: 'GToolkit-RemotePhlow-DeclarativeViews' ;

3. one or more rendering stencils.

It does not define its own specification or data source. GtRemotePhlowPictureView>>#asGtDeclarativeView asGtDeclarativeView | viewSpecification | viewSpecification := (GtRemotePhlowConcreteDataViewSpecification new) phlowDataSource: (GtRemotePhlowConcreteDataViewDataSource forPhlowView: self); renderingStencilName: self renderingStencilName; dataTransport: self currentDataTransport. self configureGenericViewSpecificationOn: viewSpecification. ^viewSpecification uses GtRemotePhlowConcreteDataViewSpecification GtPhlowViewSpecification << #GtRemotePhlowConcreteDataViewSpecification slots: { #renderingStencilName . #viewData }; package: 'GToolkit-RemotePhlow-DeclarativeViews' and GtRemotePhlowConcreteDataViewDataSource GtRemotePhlowDeclarativeViewDataSource << #GtRemotePhlowConcreteDataViewDataSource slots: {}; package: 'GToolkit-RemotePhlow-DeclarativeViews' . The generic datasource calls GtRemotePhlowPictureView>>#computeViewData computeViewData ^ GtRemotePhlowPictureViewData new content: self pictureComputation value; mediaType: self mediaTypeComputation value; width: self widthComputation value; height: self heightComputation value , captures errors, and the generic specification handles lazy/included transport, reconstruction, refresh, and rendering.

The picture data consists of bytes plus picture metadata. The generic specification creates a GtPhlowNamedStencil GtPhlowForwardStencil << #GtPhlowNamedStencil slots: { #stencilClassName . #stencilDataWrapper }; package: 'GToolkit-RemotePhlow-Stencils' using the configured rendering-stencil name. This is useful when callers may choose the renderer. GtRemotePhlowPictureViewTestObject>>#gtViewPictureScaledWithExtentFor: gtViewPictureScaledWithExtentFor: aView <gtView> ^(aView picture) title: 'Picture (full size)'; priority: 40; width: 1164; height: 560; renderingStencilName: #GtLocalPhlowPictureFullSizeViewStencil; content: [self class pictureBytes] selects a full-size local-only stencil through GtRemotePhlowPictureView>>#renderingStencilName: renderingStencilName: aSymbol renderingStencilName := aSymbol .

Choose this form first: it minimizes application-specific infrastructure while retaining local and remote behavior.

GtRemotePhlowWebBrowserView GtRemotePhlowLocalView << #GtRemotePhlowWebBrowserView slots: { #contentComputation . #staticHeaders . #dynamicHeadersComputation . #shouldLoadContentLazily }; package: 'GToolkit-RemotePhlow-PhlowViews' demonstrates when the generic concrete-data pipeline is too narrow. It has independently retrieved browser content and dynamic headers, static headers, merging rules, custom cache clearing, and specialized Update behavior. These responsibilities require custom collaborators:

- GtRemotePhlowWebBrowserViewSpecification GtPhlowViewSpecification << #GtRemotePhlowWebBrowserViewSpecification slots: { #staticHeaders . #hasDynamicHeaders . #browserContent . #dynamicHeaders }; package: 'GToolkit-RemotePhlow-DeclarativeViews' coordinates retrieval, included versus lazy data, header merging, explicit rendering, and cache flushing.

- GtRemotePhlowDeclarativeWebBrowserViewDataSource GtRemotePhlowDeclarativeViewDataSource << #GtRemotePhlowDeclarativeWebBrowserViewDataSource slots: {}; package: 'GToolkit-RemotePhlow-DeclarativeViews' evaluates content and dynamic-header computations and captures failures.

- GtRemotePhlowWebBrowserStencil GtPhlowStencil << #GtRemotePhlowWebBrowserStencil slots: { #browserContent . #contentHeaders }; package: 'GToolkit-RemotePhlow-Stencils' renders the final concrete content and headers as a web element.

The specification's GtRemotePhlowWebBrowserViewSpecification>>#viewFor: viewFor: aView | browserView | browserView := aView explicit originalView: aView; title: title; priority: priority; phlowViewSpecification: self; declarativeStencil: [ self createDeclarativeContentStencil ]. self configureViewActionsFor: browserView. ^ browserView follows the shared rule: it creates an explicit view, sets phlowViewSpecification: self, and uses GtPhlowExplicitView>>#declarativeStencil: declarativeStencil: aStencilBuilder stencilBuilder := aStencilBuilder asPhlowDeclarativeStencilBuilder . GtRemotePhlowWebBrowserViewSpecification>>#createDeclarativeContentStencil createDeclarativeContentStencil | content currentDynamicHeaders contentHeaders | content := self browserContent. content isPhlowErrorData ifTrue: [ ^ GtPhlowErrorStencil new stencilData: content ]. currentDynamicHeaders := self dynamicHeaders. currentDynamicHeaders isPhlowErrorData ifTrue: [ ^ GtPhlowErrorStencil new stencilData: currentDynamicHeaders ]. contentHeaders := self staticHeaders copy. currentDynamicHeaders do: [ :name :value | contentHeaders headerAt: name put: value ]. ^ GtRemotePhlowWebBrowserStencil new browserContent: content; contentHeaders: contentHeaders resolves the needed values and answers either an error stencil or the dedicated browser stencil.

The browser stencil subclasses GtPhlowStencil GtPhlowDeclarativeSpecification << #GtPhlowStencil slots: {}; package: 'GToolkit-RemotePhlow-Stencils' . Unlike the view, it receives values rather than blocks, and it can be used on its own in an explicit declarative view, as in GtRemotePhlowWebBrowserStencilTestObject>>#gtWebBrowserHeadersFor: gtWebBrowserHeadersFor: aView <gtView> ^ aView explicit title: 'Browser headers'; priority: 23; declarativeStencil: [ GtRemotePhlowWebBrowserStencil new url: 'https://example.com'; headerAt: 'Accept' put: 'text/html'; headerAt: 'X-Static' put: 'static value'; headerAt: 'Authorization' put: 'Bearer dynamic-token'; headerAt: 'X-Dynamic' put: 'dynamic value' ] .

Use this form only when the view needs control beyond a single generic concrete-data computation.

Choose the picture-style generic concrete-data form when the view can compute one serializable result and render it using a named stencil. It is the normal way to build a new local/remote view.

Choose the browser-style custom pipeline when data has separate retrieval paths, needs custom composition before rendering, has specialized caching or refreshing behavior, or otherwise cannot fit the generic data-source/specification contract.

In both cases, validate the view through local and remote construction examples and inspector interaction examples. For GemStone, deploy the implementation before running its GemStone examples.