ranger-compiler 3.5.1 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1064 -20
- package/LICENSE +3 -1
- package/README.md +123 -28
- package/dist/Lang.rgr +1188 -290
- package/dist/api.d.ts +2343 -879
- package/dist/api.js +79923 -56921
- package/dist/lib/JSON.rgr +102 -91
- package/dist/lib/Shell.rgr +4 -4
- package/dist/lib/apple/AppleToolchain.rgr +4 -4
- package/dist/lib/apple/README.md +5 -4
- package/dist/lib/apple/apple_test.rgr +1 -1
- package/dist/lib/core/README.md +1 -1
- package/dist/lib/evg/EVG.rgr +12 -0
- package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
- package/dist/lib/evg/EVGA11yTree.rgr +894 -0
- package/dist/lib/evg/EVGBox.rgr +267 -0
- package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
- package/dist/lib/evg/EVGCodepoint.rgr +316 -0
- package/dist/lib/evg/EVGColor.rgr +700 -0
- package/dist/lib/evg/EVGCommands.rgr +177 -0
- package/dist/lib/evg/EVGComponent.rgr +331 -0
- package/dist/lib/evg/EVGComponentTest.rgr +387 -0
- package/dist/lib/evg/EVGConnector.rgr +541 -0
- package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
- package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
- package/dist/lib/evg/EVGEasing.rgr +370 -0
- package/dist/lib/evg/EVGEffectTest.rgr +253 -0
- package/dist/lib/evg/EVGElement.rgr +4914 -0
- package/dist/lib/evg/EVGFixedTest.rgr +289 -0
- package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
- package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
- package/dist/lib/evg/EVGFling.rgr +244 -0
- package/dist/lib/evg/EVGFocus.rgr +400 -0
- package/dist/lib/evg/EVGFocusTest.rgr +399 -0
- package/dist/lib/evg/EVGGradient.rgr +322 -0
- package/dist/lib/evg/EVGGrapheme.rgr +190 -0
- package/dist/lib/evg/EVGGrid.rgr +975 -0
- package/dist/lib/evg/EVGHitTest.rgr +208 -0
- package/dist/lib/evg/EVGHoles.rgr +294 -0
- package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
- package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
- package/dist/lib/evg/EVGHostTree.rgr +834 -0
- package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
- package/dist/lib/evg/EVGImageDecode.rgr +117 -0
- package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
- package/dist/lib/evg/EVGInspect.rgr +883 -0
- package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
- package/dist/lib/evg/EVGJsonTest.rgr +535 -0
- package/dist/lib/evg/EVGLayout.rgr +4220 -0
- package/dist/lib/evg/EVGMeasure.rgr +1267 -0
- package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
- package/dist/lib/evg/EVGPatch.rgr +1721 -0
- package/dist/lib/evg/EVGPatchTest.rgr +692 -0
- package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
- package/dist/lib/evg/EVGReconcile.rgr +226 -0
- package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
- package/dist/lib/evg/EVGReject.rgr +126 -0
- package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
- package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
- package/dist/lib/evg/EVGRuler.rgr +232 -0
- package/dist/lib/evg/EVGRulerTest.rgr +172 -0
- package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
- package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
- package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
- package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
- package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
- package/dist/lib/evg/EVGText.rgr +105 -0
- package/dist/lib/evg/EVGTextEngine.rgr +536 -0
- package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
- package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
- package/dist/lib/evg/EVGToolbar.rgr +1369 -0
- package/dist/lib/evg/EVGTransition.rgr +728 -0
- package/dist/lib/evg/EVGTreeJson.rgr +721 -0
- package/dist/lib/evg/EVGUnit.rgr +554 -0
- package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
- package/dist/lib/evg/EvgApp.rgr +189 -0
- package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
- package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
- package/dist/lib/evg/EvgHost.rgr +451 -0
- package/dist/lib/evg/EvgTest.rgr +91 -0
- package/dist/lib/evg/EvgTraceColor.rgr +236 -0
- package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
- package/dist/lib/evg/EvgTraceFit.rgr +767 -0
- package/dist/lib/evg/EvgTracePath.rgr +435 -0
- package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
- package/dist/lib/evg/EvgViewport.rgr +313 -0
- package/dist/lib/evg/FxDemoDoc.rgr +185 -0
- package/dist/lib/evg/HOSTS.md +227 -0
- package/dist/lib/evg/ISSUES.md +835 -0
- package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
- package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
- package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
- package/dist/lib/evg/PLAN_EVG.md +518 -0
- package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
- package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
- package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
- package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
- package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
- package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
- package/dist/lib/evg/PathBuilder.rgr +333 -0
- package/dist/lib/evg/README.md +1236 -0
- package/dist/lib/evg/SPEC.md +1252 -0
- package/dist/lib/evg/SVGPathParser.rgr +1199 -0
- package/dist/lib/evg/SvgParser.rgr +1736 -0
- package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
- package/dist/lib/evg/VectorShapes.rgr +331 -0
- package/dist/lib/evg/VectorStroke.rgr +107 -0
- package/dist/lib/evg/VectorViewBox.rgr +380 -0
- package/dist/lib/evg/agent/README.md +290 -0
- package/dist/lib/evg/agent/evg_agent.rgr +899 -0
- package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
- package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
- package/dist/lib/evg/agent/fixtures/connector.css +63 -0
- package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
- package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
- package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
- package/dist/lib/evg/agent/fixtures/popover.css +99 -0
- package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
- package/dist/lib/evg/agent/roundtrip.sh +74 -0
- package/dist/lib/evg/agent/smoke.sh +230 -0
- package/dist/lib/evg/android/README.md +97 -0
- package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
- package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
- package/dist/lib/evg/android/androidstubs/App.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
- package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
- package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
- package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
- package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
- package/dist/lib/evg/android/androidstubs/View.kt +113 -0
- package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
- package/dist/lib/evg/apple/README.md +128 -0
- package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
- package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
- package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
- package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
- package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
- package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
- package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
- package/dist/lib/evg/bench/README.md +64 -0
- package/dist/lib/evg/bench/layout-bench.mjs +316 -0
- package/dist/lib/evg/bench/layout-cases.mjs +510 -0
- package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
- package/dist/lib/evg/bin/.gitignore +17 -0
- package/dist/lib/evg/evg_test.rgr +313 -0
- package/dist/lib/evg/gl/README.md +198 -0
- package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
- package/dist/lib/evg/gl/blur-check.mjs +399 -0
- package/dist/lib/evg/gl/boxmodel.json +1 -0
- package/dist/lib/evg/gl/demo.html +46 -0
- package/dist/lib/evg/gl/effect-presets.css +285 -0
- package/dist/lib/evg/gl/effect-presets.js +63 -0
- package/dist/lib/evg/gl/effect-shots.mjs +135 -0
- package/dist/lib/evg/gl/evg-a11y.js +566 -0
- package/dist/lib/evg/gl/evg-binary.js +160 -0
- package/dist/lib/evg/gl/evg-engine.js +296 -0
- package/dist/lib/evg/gl/evg-fx.js +237 -0
- package/dist/lib/evg/gl/evg-gestures.js +209 -0
- package/dist/lib/evg/gl/evg-list.js +167 -0
- package/dist/lib/evg/gl/evg-measure.js +186 -0
- package/dist/lib/evg/gl/evg-textinput.js +303 -0
- package/dist/lib/evg/gl/evg-view.js +144 -0
- package/dist/lib/evg/gl/evg-webgl.js +3942 -0
- package/dist/lib/evg/gl/fx-check.mjs +998 -0
- package/dist/lib/evg/gl/fx-demo.html +166 -0
- package/dist/lib/evg/gl/fx-demo.js +2 -0
- package/dist/lib/evg/gl/fx-serve.mjs +47 -0
- package/dist/lib/evg/gl/gestures-check.mjs +189 -0
- package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
- package/dist/lib/evg/gl/measure-check.mjs +135 -0
- package/dist/lib/evg/gl/rotation-check.mjs +207 -0
- package/dist/lib/evg/gl/shift-check.mjs +113 -0
- package/dist/lib/evg/gl/stroke-check.mjs +168 -0
- package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
- package/dist/lib/evg/gl/view-check.mjs +343 -0
- package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
- package/dist/lib/evg/html/evg-dom.js +318 -0
- package/dist/lib/evg/html/evg-html.js +600 -0
- package/dist/lib/evg/inspect/README.md +415 -0
- package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
- package/dist/lib/evg/inspect/evg-inspect.js +947 -0
- package/dist/lib/evg/inspect/shots/css.png +0 -0
- package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
- package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
- package/dist/lib/evg/inspect/shots/state.png +0 -0
- package/dist/lib/evg/inspect/shots.mjs +226 -0
- package/dist/lib/evg/oracle/css-blur.json +576 -0
- package/dist/lib/evg/oracle/css-box.json +157 -0
- package/dist/lib/evg/oracle/css-timing.json +709 -0
- package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
- package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
- package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
- package/dist/lib/evg/original/EVGColor.clj +310 -0
- package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
- package/dist/lib/evg/original/SVGPath.rgr +627 -0
- package/dist/lib/evg/original/Vec2.crgr +103 -0
- package/dist/lib/evg/ranger.json +9 -0
- package/dist/lib/evg/showcase/README.md +246 -0
- package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
- package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
- package/dist/lib/evg/showcase/build.mjs +651 -0
- package/dist/lib/evg/showcase/pages/album.tsx +30 -0
- package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
- package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
- package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
- package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
- package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
- package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
- package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
- package/dist/lib/evg/showcase/pages/more.tsx +332 -0
- package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
- package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
- package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
- package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
- package/dist/lib/evg/showcase/pages/units.tsx +35 -0
- package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
- package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
- package/dist/lib/evg/showcase/pages/views.tsx +223 -0
- package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
- package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
- package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
- package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
- package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
- package/dist/lib/evg/showcase/themes/more-default.css +128 -0
- package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
- package/dist/lib/evg/showcase/themes/showcase.css +508 -0
- package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
- package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
- package/dist/lib/evg/showcase/themes/views-default.css +15 -0
- package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
- package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
- package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
- package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
- package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
- package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
- package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
- package/dist/lib/evg/web/responsive/README.md +97 -0
- package/dist/lib/evg/web/responsive/build.mjs +90 -0
- package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
- package/dist/lib/evg/web/responsive/index.html +177 -0
- package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
- package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
- package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
- package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
- package/dist/lib/evg/web/tools/minify.mjs +60 -0
- package/dist/lib/evg/web/tracer/build.mjs +87 -0
- package/dist/lib/evg/web/tracer/index.html +2805 -0
- package/dist/lib/evg/web/tracer/sample.png +0 -0
- package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
- package/dist/lib/evgr/Cargo.lock +21 -0
- package/dist/lib/evgr/Cargo.toml +19 -0
- package/dist/lib/evgr/README.md +140 -0
- package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
- package/dist/lib/evgr/bench/compare.mjs +252 -0
- package/dist/lib/evgr/bench/speed.mjs +277 -0
- package/dist/lib/evgr/bin/.gitignore +3 -0
- package/dist/lib/evgr/src/bin/bench.rs +106 -0
- package/dist/lib/evgr/src/bin/smoke.rs +13 -0
- package/dist/lib/evgr/src/grid.rs +730 -0
- package/dist/lib/evgr/src/lib.rs +1003 -0
- package/dist/lib/evgr/src/style.rs +468 -0
- package/dist/lib/evgr/src/text.rs +83 -0
- package/dist/lib/image/BitReader.rgr +171 -0
- package/dist/lib/image/Buffer.rgr +173 -0
- package/dist/lib/image/DCT.rgr +283 -0
- package/dist/lib/image/Deflate.rgr +339 -0
- package/dist/lib/image/HuffmanDecoder.rgr +181 -0
- package/dist/lib/image/ImageBuffer.rgr +706 -0
- package/dist/lib/image/JPEGDecoder.rgr +808 -0
- package/dist/lib/image/PNGDecoder.rgr +544 -0
- package/dist/lib/image/PNGEncoder.rgr +388 -0
- package/dist/lib/image/PPMImage.rgr +224 -0
- package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
- package/dist/lib/image/README.md +33 -0
- package/dist/lib/image/RasterBuffer.rgr +294 -0
- package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
- package/dist/lib/image/WebPDecoder.rgr +433 -0
- package/dist/lib/image/WebPLossless.rgr +1035 -0
- package/dist/lib/image/WebPLossy.rgr +1929 -0
- package/dist/lib/image/ranger.json +9 -0
- package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
- package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
- package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
- package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
- package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
- package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
- package/dist/lib/image/tests/run_webp_tests.sh +39 -0
- package/dist/lib/rust/RsJson.rgr +468 -0
- package/dist/lib/rust/RsPrelude.rgr +1663 -0
- package/dist/lib/shell_test.rgr +7 -7
- package/dist/lib/stdlib.rgr +21 -5
- package/dist/lib/zip/ranger.json +6 -0
- package/dist/rgrc.js +87318 -67610
- package/package.json +1010 -1040
- package/dist/README.md +0 -117
- package/dist/package.json +0 -47
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// A real text-input session for a canvas that draws its own fields.
|
|
4
|
+
//
|
|
5
|
+
// THE ARGUMENT. A canvas UI has no text field, so the obvious thing is to turn
|
|
6
|
+
// `keydown` into edits: a printable key inserts, Backspace deletes, Ctrl+Arrow
|
|
7
|
+
// moves by a word. That is what this codebase did, and it is a dead end — not
|
|
8
|
+
// because it is hard, but because the list of things it cannot do never ends:
|
|
9
|
+
// IME and dead keys, the clipboard, undo, emoji, mobile keyboards that send no
|
|
10
|
+
// per-character keydown at all, and platform-specific word boundaries.
|
|
11
|
+
//
|
|
12
|
+
// So the platform owns the editing session instead. A real `<input>` sits over
|
|
13
|
+
// the drawn field, transparent and one pixel of nothing; it receives the keys,
|
|
14
|
+
// the IME, the paste and the undo; and Ranger MIRRORS its state. Ranger keeps
|
|
15
|
+
// what it is good at — the tree, the layout, the styles, the hit targets, the
|
|
16
|
+
// pixels — and hands over the one system service the platform genuinely knows
|
|
17
|
+
// better.
|
|
18
|
+
//
|
|
19
|
+
// WHAT WAS MEASURED FIRST (conformance/oracle/textinput.json, from a real
|
|
20
|
+
// `<input>` driven through the DevTools protocol, IME composition included):
|
|
21
|
+
//
|
|
22
|
+
// `beforeinput` fires with the OLD value; `input` fires with the new one.
|
|
23
|
+
// So this can be a plain mirror — read `value`, `selectionStart` and
|
|
24
|
+
// `selectionEnd` on `input` — and never has to diff or replay an inputType.
|
|
25
|
+
// That one fact is the whole design.
|
|
26
|
+
//
|
|
27
|
+
// Composition arrives as compositionstart, then compositionupdate +
|
|
28
|
+
// beforeinput/input `insertCompositionText` per keystroke, then
|
|
29
|
+
// compositionend. At `beforeinput` the selection is the range being
|
|
30
|
+
// replaced and `data` is what replaces it, so the composing range is
|
|
31
|
+
// `selectionStart .. selectionStart + data.length` — which is what a field
|
|
32
|
+
// has to underline, and the one part a hidden proxy cannot draw for us.
|
|
33
|
+
//
|
|
34
|
+
// Copy, cut, paste and undo need no code at all: they arrive as
|
|
35
|
+
// `insertFromPaste`, `deleteByCut`, `historyUndo` and `historyRedo` like any
|
|
36
|
+
// other edit.
|
|
37
|
+
//
|
|
38
|
+
// And one Backspace removes 1 code unit from "abc", 2 from an emoji, 4 from
|
|
39
|
+
// a flag and 11 from a ZWJ family — while ArrowLeft over a combining acute
|
|
40
|
+
// skips 2 where Backspace removes 1. Chromium's own delete and its own
|
|
41
|
+
// caret motion disagree about that cluster. A hand-written grapheme walker
|
|
42
|
+
// would have to reproduce an inconsistency rather than a standard; through
|
|
43
|
+
// the proxy there is nothing to reproduce.
|
|
44
|
+
//
|
|
45
|
+
// WHAT THIS DELIBERATELY DOES NOT DO. It does not draw anything, it does not
|
|
46
|
+
// decide what a field looks like, and it does not know what a field IS. It
|
|
47
|
+
// takes a value and a selection in and reports a value and a selection out.
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {object} opts
|
|
51
|
+
* @param {HTMLElement} opts.host where to park the proxy
|
|
52
|
+
* @param {HTMLCanvasElement} opts.canvas the element that keeps focus when no
|
|
53
|
+
* field is active
|
|
54
|
+
* @param {(edit: {value: string, selStart: number, selEnd: number,
|
|
55
|
+
* inputType: string, isComposing: boolean}) => void} opts.onEdit
|
|
56
|
+
* @param {(c: {active: boolean, start: number, end: number, text: string}) => void} [opts.onComposition]
|
|
57
|
+
* @param {(k: {key: string, shiftKey: boolean, ctrlKey: boolean,
|
|
58
|
+
* metaKey: boolean, altKey: boolean,
|
|
59
|
+
* preventDefault: () => void}) => boolean} [opts.onKey]
|
|
60
|
+
* Keys the APPLICATION owns rather than the field: Tab, Escape, and
|
|
61
|
+
* anything that opens a menu. Returning true consumes the key.
|
|
62
|
+
*/
|
|
63
|
+
export function createTextInputBridge({ host, canvas, onEdit, onComposition, onKey }) {
|
|
64
|
+
// ONE PROXY PER SESSION, not one for the page. Chromium keeps an input's
|
|
65
|
+
// undo history across a programmatic `value` write when the input is not
|
|
66
|
+
// focused at the time, so a proxy reused from field to field carried the
|
|
67
|
+
// last field's edits in its history: measured, the third Ctrl+Z in Full
|
|
68
|
+
// Name — after two had taken back the typed "zz" — restored a state from
|
|
69
|
+
// the session before it, with the caret at 0. A fourth would have put
|
|
70
|
+
// another field's text into this one. A fresh element has an empty history,
|
|
71
|
+
// which is what a fresh field has.
|
|
72
|
+
const makeProxy = () => {
|
|
73
|
+
const own = document.createElement("input");
|
|
74
|
+
own.type = "text";
|
|
75
|
+
// Off the accessibility tree: the drawn field already has a `textbox` node
|
|
76
|
+
// with a name and a value, and two text boxes for one control is one too
|
|
77
|
+
// many for a reader. The proxy is a keyboard fixture, not a control.
|
|
78
|
+
own.setAttribute("aria-hidden", "true");
|
|
79
|
+
own.tabIndex = -1;
|
|
80
|
+
own.autocapitalize = "off";
|
|
81
|
+
own.autocomplete = "off";
|
|
82
|
+
own.spellcheck = false;
|
|
83
|
+
// Positioned over the field rather than off-screen. An off-screen input is
|
|
84
|
+
// the usual trick and it is wrong on a phone: the browser scrolls to the
|
|
85
|
+
// focused element and to the composition popup, so an input at -9999px
|
|
86
|
+
// takes the page with it. Transparent and in place costs nothing and keeps
|
|
87
|
+
// the IME candidate window next to the text it is composing.
|
|
88
|
+
Object.assign(own.style, {
|
|
89
|
+
position: "absolute",
|
|
90
|
+
opacity: "0",
|
|
91
|
+
padding: "0",
|
|
92
|
+
margin: "0",
|
|
93
|
+
border: "0",
|
|
94
|
+
outline: "none",
|
|
95
|
+
background: "transparent",
|
|
96
|
+
color: "transparent",
|
|
97
|
+
caretColor: "transparent",
|
|
98
|
+
// SIXTEEN PIXELS, for iOS. Safari zooms the page in when it focuses a
|
|
99
|
+
// text field that computes to less than 16px, and this proxy is what
|
|
100
|
+
// takes the keyboard for every drawn field. It inherited whatever the
|
|
101
|
+
// host had, which on a phone-sized page is smaller than that. The
|
|
102
|
+
// proxy is transparent and clipped to the field's box, so the size is
|
|
103
|
+
// invisible and only Safari's decision depends on it.
|
|
104
|
+
fontSize: "16px",
|
|
105
|
+
zIndex: "1",
|
|
106
|
+
// It must not take the pointer. Ranger does every hit test and places
|
|
107
|
+
// every caret; the proxy only needs the keyboard, and it is sitting
|
|
108
|
+
// exactly on top of the field it serves. Without this it swallows the
|
|
109
|
+
// pointermove over its own box — measured: the I-beam cursor stopped
|
|
110
|
+
// appearing the moment a field was focused, because the canvas never saw
|
|
111
|
+
// the pointer again.
|
|
112
|
+
pointerEvents: "none",
|
|
113
|
+
});
|
|
114
|
+
own.style.display = "none";
|
|
115
|
+
host.appendChild(own);
|
|
116
|
+
return own;
|
|
117
|
+
};
|
|
118
|
+
let own = makeProxy();
|
|
119
|
+
let el = own;
|
|
120
|
+
|
|
121
|
+
let active = null; // the tid of the field the session belongs to
|
|
122
|
+
let composing = false;
|
|
123
|
+
let echo = false; // set while WE are writing to the proxy
|
|
124
|
+
// The elements the listeners below have been put on: the own proxy once,
|
|
125
|
+
// and every mirror input the session has been handed.
|
|
126
|
+
const wired = new WeakSet();
|
|
127
|
+
|
|
128
|
+
const report = (inputType, isComposing) => {
|
|
129
|
+
if (echo) return;
|
|
130
|
+
onEdit({
|
|
131
|
+
value: el.value,
|
|
132
|
+
selStart: el.selectionStart ?? 0,
|
|
133
|
+
selEnd: el.selectionEnd ?? 0,
|
|
134
|
+
inputType: inputType || "",
|
|
135
|
+
isComposing: !!isComposing,
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
function wire(target) {
|
|
140
|
+
if (wired.has(target)) return;
|
|
141
|
+
wired.add(target);
|
|
142
|
+
// Only the element the session is on reports: a mirror input the session
|
|
143
|
+
// left keeps its listeners and must stay quiet.
|
|
144
|
+
const mine = () => target === el && active !== null;
|
|
145
|
+
target.addEventListener("input", (ev) => { if (mine()) report(ev.inputType, ev.isComposing); });
|
|
146
|
+
|
|
147
|
+
// The composing range, for the underline. Taken at `beforeinput`, where
|
|
148
|
+
// the selection is still the range about to be replaced — after `input`
|
|
149
|
+
// it has collapsed to the caret and the range is gone.
|
|
150
|
+
target.addEventListener("beforeinput", (ev) => {
|
|
151
|
+
if (!mine() || !onComposition) return;
|
|
152
|
+
if (ev.inputType !== "insertCompositionText") return;
|
|
153
|
+
const start = target.selectionStart ?? 0;
|
|
154
|
+
const text = ev.data || "";
|
|
155
|
+
onComposition({ active: true, start, end: start + text.length, text });
|
|
156
|
+
});
|
|
157
|
+
target.addEventListener("compositionend", () => {
|
|
158
|
+
if (!mine()) return;
|
|
159
|
+
composing = false;
|
|
160
|
+
if (onComposition) onComposition({ active: false, start: 0, end: 0, text: "" });
|
|
161
|
+
});
|
|
162
|
+
target.addEventListener("compositionstart", () => {
|
|
163
|
+
if (mine()) composing = true;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// The selection can move without an edit: an arrow key, Ctrl+A, a
|
|
167
|
+
// double-click, a drag with the mouse. Those are the platform's word and
|
|
168
|
+
// grapheme rules doing exactly what the bridge exists to borrow, so they
|
|
169
|
+
// are mirrored like any other change.
|
|
170
|
+
target.addEventListener("keyup", () => { if (mine()) report("", composing); });
|
|
171
|
+
target.addEventListener("select", () => { if (mine()) report("", composing); });
|
|
172
|
+
target.addEventListener("mouseup", () => { if (mine()) report("", composing); });
|
|
173
|
+
|
|
174
|
+
// Keys the application owns. A field must not swallow Tab or Escape, and
|
|
175
|
+
// the page's own shortcuts have to keep working while a field has focus.
|
|
176
|
+
target.addEventListener("keydown", (ev) => {
|
|
177
|
+
if (!mine() || !onKey) return;
|
|
178
|
+
if (ev.isComposing) return; // an IME is using this key; it is not ours
|
|
179
|
+
const taken = onKey({
|
|
180
|
+
key: ev.key,
|
|
181
|
+
shiftKey: ev.shiftKey,
|
|
182
|
+
ctrlKey: ev.ctrlKey,
|
|
183
|
+
metaKey: ev.metaKey,
|
|
184
|
+
altKey: ev.altKey,
|
|
185
|
+
preventDefault: () => ev.preventDefault(),
|
|
186
|
+
});
|
|
187
|
+
if (taken) ev.preventDefault();
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
wire(own);
|
|
191
|
+
|
|
192
|
+
// A mirror's input, while the session is in it, takes the pointer: a click
|
|
193
|
+
// puts the caret where the platform says, a drag selects, a double-click
|
|
194
|
+
// takes the word — and every one of those comes back through `select` and
|
|
195
|
+
// `mouseup` for the app to draw. The canvas keeps everything outside it.
|
|
196
|
+
function takePointer(target, yes) {
|
|
197
|
+
if (target === own) return;
|
|
198
|
+
target.style.pointerEvents = yes ? "auto" : "none";
|
|
199
|
+
target.style.cursor = yes ? "text" : "";
|
|
200
|
+
target.style.caretColor = "transparent";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
/** True while a field owns the keyboard. */
|
|
205
|
+
isActive: () => active !== null,
|
|
206
|
+
activeTid: () => active,
|
|
207
|
+
isComposing: () => composing,
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Hand the session to a field. `box` is where the field is drawn, in
|
|
211
|
+
* canvas coordinates, so the proxy can sit on top of it.
|
|
212
|
+
*/
|
|
213
|
+
focusField(tid, { value, selStart, selEnd, kind, maxLength, readOnly, box }, element) {
|
|
214
|
+
// The element the session edits in: the accessibility mirror's own
|
|
215
|
+
// input for this field when the host has one — then the reader, the
|
|
216
|
+
// keyboard and the pointer all meet in one element and Tab walks on
|
|
217
|
+
// from it to the next control — and the hidden proxy otherwise.
|
|
218
|
+
// A field of our own gets a NEW proxy — see `makeProxy`. A mirror input
|
|
219
|
+
// is the host's element and keeps whatever history the host gives it.
|
|
220
|
+
if (!element) {
|
|
221
|
+
const fresh = makeProxy();
|
|
222
|
+
if (el === own) takePointer(own, false);
|
|
223
|
+
own.remove();
|
|
224
|
+
own = fresh;
|
|
225
|
+
}
|
|
226
|
+
const next = element || own;
|
|
227
|
+
if (next !== el) {
|
|
228
|
+
takePointer(el, false);
|
|
229
|
+
if (el === own) own.style.display = "none";
|
|
230
|
+
el = next;
|
|
231
|
+
wire(el);
|
|
232
|
+
}
|
|
233
|
+
takePointer(el, true);
|
|
234
|
+
active = tid;
|
|
235
|
+
echo = true;
|
|
236
|
+
// `type` is the platform's, not ours. A password proxy gives the right
|
|
237
|
+
// reveal behaviour and the right keyboard; a number proxy gives a
|
|
238
|
+
// numeric keypad on a phone. The character filtering `InputCtl` grew by
|
|
239
|
+
// hand belongs here instead, where the platform already does it.
|
|
240
|
+
el.type = kind === "password" ? "password" : "text";
|
|
241
|
+
el.inputMode = kind === "number" ? "decimal" : kind === "email" ? "email" : "text";
|
|
242
|
+
el.readOnly = !!readOnly;
|
|
243
|
+
if (maxLength > 0) el.maxLength = maxLength;
|
|
244
|
+
else el.removeAttribute("maxlength");
|
|
245
|
+
el.value = value;
|
|
246
|
+
el.setSelectionRange(selStart, selEnd);
|
|
247
|
+
if (box && el === own) {
|
|
248
|
+
el.style.display = "block";
|
|
249
|
+
el.style.left = box.x + "px";
|
|
250
|
+
el.style.top = box.y + "px";
|
|
251
|
+
el.style.width = box.w + "px";
|
|
252
|
+
el.style.height = box.h + "px";
|
|
253
|
+
}
|
|
254
|
+
if (document.activeElement !== el) el.focus({ preventScroll: true });
|
|
255
|
+
echo = false;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Ranger changed the state — a click moved the caret, say.
|
|
260
|
+
*
|
|
261
|
+
* This also takes the focus BACK, and has to: the page focuses its canvas
|
|
262
|
+
* on every pointerdown so that keys reach it, which quietly ends the
|
|
263
|
+
* session on the second click of a double-click. Measured — the word was
|
|
264
|
+
* selected in Ranger, the proxy still read 4,4 and unfocused, and the
|
|
265
|
+
* keystroke went round the old keydown path and inserted instead of
|
|
266
|
+
* replacing. While a session is active the proxy holds the keyboard, and
|
|
267
|
+
* "active" is not a thing anything else gets to decide by accident.
|
|
268
|
+
*/
|
|
269
|
+
sync({ value, selStart, selEnd }) {
|
|
270
|
+
if (active === null) return;
|
|
271
|
+
echo = true;
|
|
272
|
+
if (el.value !== value) el.value = value;
|
|
273
|
+
el.setSelectionRange(selStart, selEnd);
|
|
274
|
+
if (document.activeElement !== el) el.focus({ preventScroll: true });
|
|
275
|
+
echo = false;
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
blurField() {
|
|
279
|
+
if (active === null) return;
|
|
280
|
+
this.release();
|
|
281
|
+
if (canvas) canvas.focus({ preventScroll: true });
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* End the session and leave the focus where it is going. Tab out of a
|
|
286
|
+
* field is this: the browser moves the focus to the next control, and
|
|
287
|
+
* the canvas must not take it back on the way.
|
|
288
|
+
*/
|
|
289
|
+
release() {
|
|
290
|
+
if (active === null) return;
|
|
291
|
+
active = null;
|
|
292
|
+
composing = false;
|
|
293
|
+
takePointer(el, false);
|
|
294
|
+
if (el === own) {
|
|
295
|
+
own.style.display = "none";
|
|
296
|
+
own.value = "";
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
/** For tests and for anything that needs to see the real element. */
|
|
301
|
+
element: () => el,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// When a built frame may be drawn again, and when it has to be built.
|
|
4
|
+
//
|
|
5
|
+
// A display list that carries a camera (`EVGDisplayList.setView`) can be
|
|
6
|
+
// drawn at a view it was not built at: the painter multiplies in the shader,
|
|
7
|
+
// so a pan costs a uniform rather than a walk, a layout and a flatten
|
|
8
|
+
// (PLAN_VIEW_TRANSFORM.md). Two things stop that being true for every view,
|
|
9
|
+
// and this is the arithmetic that decides:
|
|
10
|
+
//
|
|
11
|
+
// THE BAND. A run was rasterised into the glyph atlas at a size and a curve
|
|
12
|
+
// was flattened for one. Stretched far enough, text blurs and a circle
|
|
13
|
+
// shows its facets; shrunk far enough, the frame carries points nobody can
|
|
14
|
+
// see. So a frame is good between `1/band` and `band` of the scale it was
|
|
15
|
+
// built at — √2 by default, about three notches of a pinch.
|
|
16
|
+
//
|
|
17
|
+
// THE REGION. Culling asks what is inside the frame, and a frame drawn at a
|
|
18
|
+
// view it was not built at shows scene the build never considered. So a
|
|
19
|
+
// frame is built for the viewport GROWN by an overscan, and it is good
|
|
20
|
+
// until the viewport leaves that.
|
|
21
|
+
//
|
|
22
|
+
// Both are host decisions rather than engine ones — a viewer that would
|
|
23
|
+
// rather have sharp text than frames picks a tighter band, and one on a
|
|
24
|
+
// phone picks a smaller overscan — which is why they are here and not in the
|
|
25
|
+
// display list.
|
|
26
|
+
//
|
|
27
|
+
// Everything in this file is arithmetic on plain numbers: no GL, no DOM, and
|
|
28
|
+
// `view-policy-check.mjs` holds it to its own rules without a browser.
|
|
29
|
+
|
|
30
|
+
/** The default overscan: one viewport of scene in every direction. */
|
|
31
|
+
export const DEFAULT_OVERSCAN = 1;
|
|
32
|
+
|
|
33
|
+
/** The default band: √2 either way. See the note above. */
|
|
34
|
+
export const DEFAULT_BAND = Math.SQRT2;
|
|
35
|
+
|
|
36
|
+
const scaleOf = (v) => {
|
|
37
|
+
const s = v && v.scale;
|
|
38
|
+
return s === undefined || s === null || s === 0 ? 1 : s;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* What the canvas shows, in scene coordinates.
|
|
43
|
+
*
|
|
44
|
+
* The inverse of the camera: a point on the page is `scene * scale + pan`, so
|
|
45
|
+
* the scene under the page is `(page - pan) / scale`.
|
|
46
|
+
*/
|
|
47
|
+
export function sceneViewport(view, width, height) {
|
|
48
|
+
const s = scaleOf(view);
|
|
49
|
+
const x = ((view && view.x) || 0);
|
|
50
|
+
const y = ((view && view.y) || 0);
|
|
51
|
+
return { x: -x / s, y: -y / s, w: width / s, h: height / s };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The region to build for: what the canvas shows, grown by the overscan.
|
|
56
|
+
*
|
|
57
|
+
* `overscan` is in viewports, not pixels, so the same number means the same
|
|
58
|
+
* thing at every zoom — one viewport each way is 9× the area in the worst
|
|
59
|
+
* case and usually far less, since a board at 8× has almost nothing on it.
|
|
60
|
+
*/
|
|
61
|
+
export function regionFor(view, width, height, overscan = DEFAULT_OVERSCAN) {
|
|
62
|
+
const v = sceneViewport(view, width, height);
|
|
63
|
+
const gx = v.w * overscan;
|
|
64
|
+
const gy = v.h * overscan;
|
|
65
|
+
return { x: v.x - gx, y: v.y - gy, w: v.w + gx * 2, h: v.h + gy * 2 };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Is `inner` wholly inside `outer`? */
|
|
69
|
+
export function rectInside(inner, outer) {
|
|
70
|
+
return (
|
|
71
|
+
inner.x >= outer.x &&
|
|
72
|
+
inner.y >= outer.y &&
|
|
73
|
+
inner.x + inner.w <= outer.x + outer.w &&
|
|
74
|
+
inner.y + inner.h <= outer.y + outer.h
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* May a frame built as `built` be drawn at `view` on a `width` x `height`
|
|
80
|
+
* canvas — and if not, why not?
|
|
81
|
+
*
|
|
82
|
+
* `built` is `{ view, region }`: the camera the list was walked at and the
|
|
83
|
+
* scene it was walked for. Returns `{ keep, reason }`, and the reason is
|
|
84
|
+
* what a host prints when someone asks why a pan cost a rebuild.
|
|
85
|
+
*/
|
|
86
|
+
export function frameFits(built, view, width, height, opts = {}) {
|
|
87
|
+
if (!built || !built.view || !built.region) return { keep: false, reason: "nothing built" };
|
|
88
|
+
const band = opts.band || DEFAULT_BAND;
|
|
89
|
+
const ratio = scaleOf(view) / scaleOf(built.view);
|
|
90
|
+
if (ratio > band || ratio < 1 / band) {
|
|
91
|
+
return { keep: false, reason: "scale left the band", ratio };
|
|
92
|
+
}
|
|
93
|
+
const shown = sceneViewport(view, width, height);
|
|
94
|
+
if (!rectInside(shown, built.region)) {
|
|
95
|
+
return { keep: false, reason: "viewport left the region" };
|
|
96
|
+
}
|
|
97
|
+
return { keep: true, reason: "" };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A frame keeper: the state above, with the two calls a host makes.
|
|
102
|
+
*
|
|
103
|
+
* const keep = createViewKeeper({ overscan: 1 });
|
|
104
|
+
* if (!keep.fits(view, w, h).keep) { …build…; keep.built(view, w, h); }
|
|
105
|
+
* frame.draw(shifts, view);
|
|
106
|
+
*
|
|
107
|
+
* `built` records what the host just walked, and returns the region it
|
|
108
|
+
* should have walked for — a host that culls to it passes the region to the
|
|
109
|
+
* engine BEFORE building, so the two calls are `regionFor` then `built`.
|
|
110
|
+
*/
|
|
111
|
+
export function createViewKeeper(opts = {}) {
|
|
112
|
+
const overscan = opts.overscan === undefined ? DEFAULT_OVERSCAN : opts.overscan;
|
|
113
|
+
const band = opts.band || DEFAULT_BAND;
|
|
114
|
+
let state = null;
|
|
115
|
+
let kept = 0;
|
|
116
|
+
let builds = 0;
|
|
117
|
+
return {
|
|
118
|
+
/** The region a build at this view should cover. */
|
|
119
|
+
region: (view, w, h) => regionFor(view, w, h, overscan),
|
|
120
|
+
/** Can the frame in hand be drawn at this view? */
|
|
121
|
+
fits(view, w, h) {
|
|
122
|
+
const f = frameFits(state, view, w, h, { band });
|
|
123
|
+
if (f.keep) kept += 1;
|
|
124
|
+
return f;
|
|
125
|
+
},
|
|
126
|
+
/** Remember what was just built, and for what. */
|
|
127
|
+
built(view, w, h) {
|
|
128
|
+
builds += 1;
|
|
129
|
+
state = { view: { x: view.x || 0, y: view.y || 0, scale: scaleOf(view) }, region: regionFor(view, w, h, overscan) };
|
|
130
|
+
return state.region;
|
|
131
|
+
},
|
|
132
|
+
/** Throw the frame away: a new document, an edit, a resize. */
|
|
133
|
+
reset() {
|
|
134
|
+
state = null;
|
|
135
|
+
},
|
|
136
|
+
get state() {
|
|
137
|
+
return state;
|
|
138
|
+
},
|
|
139
|
+
/** Draws served from a kept frame, and builds. For a status line. */
|
|
140
|
+
get counts() {
|
|
141
|
+
return { kept, builds };
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|