Skip to content

packages/core/src/lib/render-layer/define-render-layer.ts

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

1 import { definePrimitive } from "../define-primitive.js";
2 import { GLOBAL_STATE } from "../global-state.js";
3 import { createLayerSurface } from "../surface/resolve-surface.js";
4 import {
5   resolveRequiredLength,
6   stripLayoutParticipation,
7 } from "../style/resolve-built-in.js";
8 import type {
9   PibblComponent,
10   PrimitiveCapabilities,
11   RenderingContext2D,
12 } from "../types.js";
13 import { PIBBL_EVENT_DEFINITIONS } from "../events/event-definitions.js";
14 import { wireStructuralEvents } from "../components/structural-events.js";
15 import { RenderLayerController } from "./controller.js";
16 import type {
17   PibblRenderLayerDefinition,
18   PibblRenderLayerProps,
19   PibblRenderLayerSize,
20   PibblRenderLayerStyle,
21 } from "./types.js";
22 
23 type ResolvedRenderLayerStyle = PibblRenderLayerStyle & {
24   readonly width: number;
25   readonly height: number;
26 };
27 
28 /**
29  * Defines a Pibbl component backed by a persistent external-renderer surface and resource lifetime.
30  * Pibbl schedules frames and commits the bitmap and interaction snapshot together; the definition
31  * owns application-resource cleanup.
32  *
33  * @param definition - Synchronous resource lifecycle, rendering, and interaction implementation.
34  * See {@link PibblRenderLayerDefinition} .
35  * @returns A Pibbl component that owns one external-renderer boundary per mounted identity. See
36  * {@link PibblComponent} , {@link PibblRenderLayerProps} .
37  *
38  * @see {@link PibblRenderLayerDefinition}
39  * @see {@link PibblComponent}
40  * @see {@link PibblRenderLayerProps}
41  */
42 export function defineRenderLayer<
43   Props,
44   Resources,
45   Target extends object = object,
46 >(
47   definition: PibblRenderLayerDefinition<Props, Resources, Target>,
48 ): PibblComponent<PibblRenderLayerProps<Props>> {
49   const immutableDefinition = snapshotDefinition(definition);
50   const primitive = (definePrimitive as any)(
51     function renderDefinedRenderLayer(
52       props: Readonly<PibblRenderLayerProps<Props>>,
53       style: Readonly<ResolvedRenderLayerStyle>,
54       ctx: RenderingContext2D,
55     ) {
56       wireStructuralEvents(props);
57       const refs = GLOBAL_STATE.componentRefs!;
58       let controller = refs.refs[0] as
59         RenderLayerController<Props, Resources, Target> | undefined;
60       if (!controller) {
61         controller = new RenderLayerController(immutableDefinition, refs);
62         refs.refs[0] = controller;
63       }
64       const surface = GLOBAL_STATE.currentPibblInstance!.surface.snapshot!;
65       const layerSurface = createLayerSurface(
66         style.width,
67         style.height,
68         surface.backingPixelsPerLogicalUnitX,
69         surface.backingPixelsPerLogicalUnitY,
70       );
71       const size: PibblRenderLayerSize = Object.freeze({
72         width: style.width,
73         height: style.height,
74         backingWidth: layerSurface.backingWidth,
75         backingHeight: layerSurface.backingHeight,
76         pixelRatioX: layerSurface.backingPixelsPerLogicalUnitX,
77         pixelRatioY: layerSurface.backingPixelsPerLogicalUnitY,
78       });
79       controller.render(customProps(props) as Props, size, ctx, {
80         missBehavior: props.missBehavior ?? "pass-through",
81         pointerEvents: props.pointerEvents,
82         keyboardFocusable: props.keyboardFocusable,
83         keyboardNavigationBounds: props.keyboardNavigationBounds,
84       });
85     },
86     { resolveStyle },
87   );
88   return primitive as PibblComponent<PibblRenderLayerProps<Props>>;
89 }
90 
91 const resolveStyle: NonNullable<
92   PrimitiveCapabilities<
93     PibblRenderLayerProps<any>,
94     PibblRenderLayerStyle,
95     PibblRenderLayerStyle,
96     ResolvedRenderLayerStyle
97   >["resolveStyle"]
98 > = (style, context) =>
99   ({
100     ...stripLayoutParticipation(style),
101     width: resolveRequiredLength(
102       style.width ?? context.allocation.width,
103       context.percentageBasis.width,
104       "width",
105       context,
106     ),
107     height: resolveRequiredLength(
108       style.height ?? context.allocation.height,
109       context.percentageBasis.height,
110       "height",
111       context,
112     ),
113   }) as ResolvedRenderLayerStyle;
114 
115 function customProps<Props>(
116   props: Readonly<PibblRenderLayerProps<Props>>,
117 ): Props {
118   const custom = { ...props } as Record<PropertyKey, unknown>;
119   delete custom.missBehavior;
120   delete custom.pointerEvents;
121   delete custom.keyboardFocusable;
122   delete custom.keyboardNavigationBounds;
123   for (const definition of PIBBL_EVENT_DEFINITIONS) {
124     delete custom[definition.prop];
125     delete custom[definition.captureProp];
126   }
127   return custom as Props;
128 }
129 
130 function snapshotDefinition<Props, Resources, Target extends object>(
131   definition: PibblRenderLayerDefinition<Props, Resources, Target>,
132 ): PibblRenderLayerDefinition<Props, Resources, Target> {
133   return Object.freeze({
134     create: definition.create,
135     update: definition.update,
136     resize: definition.resize,
137     render: definition.render,
138     dispose: definition.dispose,
139     interaction: definition.interaction
140       ? Object.freeze({
141           settle: definition.interaction.settle,
142         })
143       : undefined,
144   });
145 }
146 

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.